File indexing completed on 2024-05-12 05:14:49

0001 /*
0002  *  kernelwakealarm.cpp  -  kernel alarm to wake from suspend
0003  *  Program:  kalarm
0004  *  SPDX-FileCopyrightText: 2023 one-d-wide <one-d-wide@protonmail.com>
0005  *  SPDX-FileCopyrightText: 2023 David Jarvie <djarvie@kde.org>
0006  *
0007  *  SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "kernelwakealarm.h"
0011 
0012 #include "kalarmcalendar/kadatetime.h"
0013 #include "kalarm_debug.h"
0014 
0015 #ifdef Q_OS_LINUX
0016 
0017 #include <unistd.h>
0018 #include <errno.h>
0019 #include <time.h>
0020 #include <string.h>
0021 #include <sys/timerfd.h>
0022 
0023 int KernelWakeAlarm::mAvailable = 0;  // 0 = unchecked, 1 = unavailable, 2 = available
0024 
0025 KernelWakeAlarm::KernelWakeAlarm()
0026 {
0027     // `timerfd_create(2)`
0028     int ret = timerfd_create(CLOCK_REALTIME_ALARM, 0);
0029 
0030     if (ret >= 0)
0031     {
0032         mTimerFd = ret;
0033         mAvailable = 2;
0034     }
0035     else
0036     {
0037         mAvailable = 1;
0038         switch (errno)
0039         {
0040             case EPERM:
0041                 qCWarning(KALARM_LOG) << "Kernel alarm timers not available (no CAP_WAKE_ALARM capability)";
0042                 break;
0043             case EINVAL:
0044                 qCWarning(KALARM_LOG) << "Kernel alarm timers not available (CLOCK_REALTIME_ALARM not supported)";
0045                 break;
0046             default:
0047                 qCWarning(KALARM_LOG) << "KernelWakeAlarm: Error creating kernel timer:" << strerror(errno);
0048                 mAvailable = 2;
0049                 break;
0050         }
0051     }
0052 }
0053 
0054 KernelWakeAlarm::KernelWakeAlarm(const KernelWakeAlarm& other)
0055     : KernelWakeAlarm()
0056 {
0057     if (other.mTriggerTime > 0)
0058     {
0059         if (arm(other.mTriggerTime))
0060             mTriggerTime = other.mTriggerTime;
0061     }
0062 }
0063 
0064 KernelWakeAlarm::~KernelWakeAlarm()
0065 {
0066     if (mTimerFd)
0067         close(mTimerFd.value());
0068 }
0069 
0070 KernelWakeAlarm& KernelWakeAlarm::operator=(const KernelWakeAlarm& other)
0071 {
0072     if (other.mTriggerTime > 0)
0073     {
0074         if (arm(other.mTriggerTime))
0075             mTriggerTime = other.mTriggerTime;
0076     }
0077     return *this;
0078 }
0079 
0080 bool KernelWakeAlarm::isValid() const
0081 {
0082     return mTimerFd.has_value();
0083 }
0084 
0085 bool KernelWakeAlarm::isAvailable()
0086 {
0087     if (!mAvailable)
0088         KernelWakeAlarm a;
0089     return mAvailable == 2;
0090 }
0091 
0092 bool KernelWakeAlarm::arm(const KAlarmCal::KADateTime& triggerTime)
0093 {
0094     if (triggerTime.isValid())
0095     {
0096         const time_t triggerSeconds = static_cast<time_t>(triggerTime.toSecsSinceEpoch());
0097         if (arm(triggerSeconds))
0098         {
0099             mTriggerTime = triggerSeconds;
0100             qCDebug(KALARM_LOG) << "KernelWakeAlarm::arm: Kernel timer set to:" << triggerTime.qDateTime();
0101             return true;
0102         }
0103     }
0104     return false;
0105 }
0106 
0107 bool KernelWakeAlarm::arm(time_t triggerSeconds)
0108 {
0109     if (!mTimerFd)
0110         return false;
0111     if (triggerSeconds  &&  triggerSeconds <= ::time(nullptr))
0112         return false;    // already expired
0113     struct itimerspec time = {};
0114     time.it_value.tv_sec = triggerSeconds;
0115 
0116     // `timerfd_settime(2)`
0117     if (timerfd_settime(mTimerFd.value(), TFD_TIMER_ABSTIME, &time, nullptr) < 0)
0118     {
0119         qCWarning(KALARM_LOG) << "KernelWakeAlarm::arm: Failed to set kernel timer:" << strerror(errno);
0120 
0121         return false;
0122     }
0123     return true;
0124 }
0125 
0126 void KernelWakeAlarm::disarm()
0127 {
0128     if (arm(0))
0129         mTriggerTime = 0;
0130 }
0131 
0132 #else // not Q_OS_LINUX
0133 
0134 KernelWakeAlarm::KernelWakeAlarm() {}
0135 KernelWakeAlarm::KernelWakeAlarm(const KernelWakeAlarm&) {}
0136 KernelWakeAlarm::~KernelWakeAlarm() {}
0137 KernelWakeAlarm& KernelWakeAlarm::operator=(const KernelWakeAlarm&) { return *this; }
0138 bool KernelWakeAlarm::arm(const KAlarmCal::KADateTime&)  { return false; }
0139 void KernelWakeAlarm::disarm() {}
0140 bool KernelWakeAlarm::isValid() const { return false; }
0141 bool KernelWakeAlarm::isAvailable()   { return false; }
0142 
0143 #endif // Q_OS_LINUX
0144 
0145 // vim: et sw=4: