sunil+b...@nimmagadda.net wrote:
> >Synopsis:    apmd(8) poll timer off by 10x
> >Category:    system
> >Environment:
>       System      : OpenBSD 6.3
>       Details     : OpenBSD 6.3-current (GENERIC.MP) #54: Wed May 30 23:03:50 
> MDT 2018
>                        
> dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP
> 
>       Architecture: OpenBSD.amd64
>       Machine     : amd64
> >Description:
>         With apmd_flags="-Az10", expected apmd(8) to suspend when
>         battery is at 10%, however, it didn't check in time and
>         laptop ran of out power.
> >How-To-Repeat:
>         Disconnect A/C adapter and run with -z percent greater
>         than current estimated battery life reported by apm(8);
>         poll every minute, for example...
>       # rcctl stop apmd
>       # apmd -A -z90 -t60
>         should suspend in a minute, however, it suspends after 10
>         minutes.
> >Fix:
>       The following diff...
> 
>         1. Provides a dedicated timer that fires every 10 seconds
>         instead of relying on EVFILT_READ freqency.
> 
>         2. Increments a counter and checks against timeout value,
>         if it exceeds, invokes auto-action.
> 
>         3. Wraps a few long lines that exceed 80 cols upon code
>         shuffle.

Revised diff without whitespace changes and...

1. While querying for changed objects using kevent(2), request changes
for all objects registered instead of just 1 and handle them.

Index: apmd.c
===================================================================
RCS file: /cvs/src/usr.sbin/apmd/apmd.c,v
retrieving revision 1.81
diff -u -p -w -r1.81 apmd.c
--- apmd.c      15 Oct 2017 15:14:49 -0000      1.81
+++ apmd.c      8 Jun 2018 10:16:53 -0000
@@ -56,6 +56,9 @@
 #define AUTO_SUSPEND 1
 #define AUTO_HIBERNATE 2
 
+#define TIMO (10*60)                   /* 10 minutes */
+#define TIMER_ID 1
+
 const char apmdev[] = _PATH_APM_CTLDEV;
 const char sockfile[] = _PATH_APM_SOCKET;
 
@@ -341,8 +344,6 @@ hibernate(int ctl_fd)
        ioctl(ctl_fd, APM_IOC_HIBERNATE, 0);
 }
 
-#define TIMO (10*60)                   /* 10 minutes */
-
 int
 main(int argc, char *argv[])
 {
@@ -353,13 +354,12 @@ main(int argc, char *argv[])
        int statonly = 0;
        int powerstatus = 0, powerbak = 0, powerchange = 0;
        int noacsleep = 0;
-       struct timespec ts = {TIMO, 0}, sts = {0, 0};
        struct apm_power_info pinfo;
-       time_t apmtimeout = 0;
+       time_t apmtimeout = TIMO, counter = 0;
        const char *sockname = sockfile;
        const char *errstr;
        int kq, nchanges;
-       struct kevent ev[2];
+       struct kevent ev[3];
        int ncpu_mib[2] = { CTL_HW, HW_NCPU };
        int ncpu;
        size_t ncpu_sz = sizeof(ncpu);
@@ -379,8 +379,8 @@ main(int argc, char *argv[])
                        sockname = optarg;
                        break;
                case 't':
-                       ts.tv_sec = strtoul(optarg, NULL, 0);
-                       if (ts.tv_sec == 0)
+                       apmtimeout = strtoul(optarg, NULL, 0);
+                       if (apmtimeout == 0)
                                usage();
                        break;
                case 's':       /* status only */
@@ -466,57 +466,30 @@ main(int argc, char *argv[])
 
        EV_SET(&ev[0], sock_fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR,
            0, 0, NULL);
+       EV_SET(&ev[1], TIMER_ID, EVFILT_TIMER, EV_ADD, 0, 10000, NULL);
        if (ctl_fd == -1)
-               nchanges = 1;
+               nchanges = 2;
        else {
-               EV_SET(&ev[1], ctl_fd, EVFILT_READ, EV_ADD | EV_ENABLE |
+               EV_SET(&ev[2], ctl_fd, EVFILT_READ, EV_ADD | EV_ENABLE |
                    EV_CLEAR, 0, 0, NULL);
-               nchanges = 2;
+               nchanges = 3;
        }
-       if (kevent(kq, ev, nchanges, NULL, 0, &sts) < 0)
+       if (kevent(kq, ev, nchanges, NULL, 0, NULL) < 0)
                error("kevent", NULL);
 
        if (sysctl(ncpu_mib, 2, &ncpu, &ncpu_sz, NULL, 0) < 0)
                error("cannot read hw.ncpu", NULL);
 
        for (;;) {
-               int rv;
+               int i, rv;
 
-               sts = ts;
-
-               apmtimeout += 1;
-               if ((rv = kevent(kq, NULL, 0, ev, 1, &sts)) < 0)
+               if ((rv = kevent(kq, NULL, 0, ev, nchanges, NULL)) < 0)
                        break;
 
-               if (apmtimeout >= ts.tv_sec) {
-                       apmtimeout = 0;
-
-                       /* wakeup for timeout: take status */
-                       powerbak = power_status(ctl_fd, 0, &pinfo);
-                       if (powerstatus != powerbak) {
-                               powerstatus = powerbak;
-                               powerchange = 1;
-                       }
-
-                       if (!powerstatus && autoaction &&
-                           autolimit > (int)pinfo.battery_life) {
-                               syslog(LOG_NOTICE,
-                                   "estimated battery life %d%%, "
-                                   "autoaction limit set to %d%% .",
-                                   pinfo.battery_life,
-                                   autolimit
-                               );
-
-                               if (autoaction == AUTO_SUSPEND)
-                                       suspend(ctl_fd);
-                               else
-                                       hibernate(ctl_fd);
-                       }
-               }
-
-               if (!rv)
-                       continue;
-
+               for (i = 0; i < rv; i++) {
+                       struct kevent *ke = &ev[i];
+                       switch (ke->filter) {
+                       case EVFILT_READ:
                if (ev->ident == ctl_fd) {
                        suspends = standbys = hibernates = resumes = 0;
                        syslog(LOG_DEBUG, "apmevent %04x index %d",
@@ -600,6 +573,42 @@ main(int argc, char *argv[])
                                hibernate(ctl_fd);
                                break;
                        }
+
+                               break;
+                       case EVFILT_TIMER:
+                               if (ev->ident != TIMER_ID)
+                                       error("invalid timer id", NULL);
+
+                               counter += 10;
+                               if (counter >= apmtimeout) {
+                                       counter = 0;
+
+                                       /* wakeup for timeout: take status */
+                                       powerbak = power_status(ctl_fd, 0, 
&pinfo);
+                                       if (powerstatus != powerbak) {
+                                               powerstatus = powerbak;
+                                               powerchange = 1;
+                                       }
+
+                                       if (!powerstatus && autoaction &&
+                                           autolimit > 
(int)pinfo.battery_life) {
+                                               syslog(LOG_NOTICE,
+                                                   "estimated battery life 
%d%%, "
+                                                   "autoaction limit set to 
%d%% .",
+                                                   pinfo.battery_life,
+                                                   autolimit
+                                               );
+
+                                               if (autoaction == AUTO_SUSPEND)
+                                                       suspend(ctl_fd);
+                                               else
+                                                       hibernate(ctl_fd);
+                                       }
+                               }
+
+                               break;
+                       }
+               }
        }
        error("kevent loop", NULL);
 

Reply via email to