]> git.neil.brown.name Git - susman.git/blob - wakeevent.c
wakealarmd: cope with delta between system time and RTC time.
[susman.git] / wakeevent.c
1
2 /* Register an fd which produces wake events with
3  * eventlib.
4  * Whenever the fd is readable, we block suspend,
5  * call the handler, then allow suspend.
6  * Meanwhile we open a socket to the event daemon passing
7  * it the same fd.
8  * At a lower priority, when we read 'S' from the daemon we reply
9  * with 'R'.
10  *
11  * Copyright (C) 2011 Neil Brown <neilb@suse.de>
12  *
13  *    This program is free software; you can redistribute it and/or modify
14  *    it under the terms of the GNU General Public License as published by
15  *    the Free Software Foundation; either version 2 of the License, or
16  *    (at your option) any later version.
17  *
18  *    This program is distributed in the hope that it will be useful,
19  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *    GNU General Public License for more details.
22  *
23  *    You should have received a copy of the GNU General Public License along
24  *    with this program; if not, write to the Free Software Foundation, Inc.,
25  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #include <stdlib.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <event.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include "libsus.h"
35
36 struct han {
37         struct event    ev;
38         struct event    sev;
39         int             sock;
40         int             disable;
41         void            (*fn)(int,short,void*);
42         void            *data;
43 };
44
45 static void wakeup_call(int fd, short ev, void *data)
46 {
47         /* A (potential) wakeup event can be read from this fd.
48          * We won't go to sleep because we haven't replied to
49          * 'S' yet as that is handle with a lower priority.
50          */
51         struct han *han = data;
52         han->fn(fd, ev, han->data);
53 }
54
55 static void wakeup_sock(int fd, short ev, void *data)
56 {
57         char buf;
58         struct han *han = data;
59         int n = read(fd, &buf, 1);
60
61         if (n < 0 && errno == EAGAIN)
62                 return;
63         if (n != 1) {
64                 /* How do I signal an error ?*/
65                 event_del(&han->sev);
66                 return;
67         }
68         if (buf == 'S')
69                 /* As we are at a lower priority (higher number)
70                  * than the main event, we must have handled everything
71                  */
72                 write(fd, "R", 1);
73 }
74
75 static void send_fd(int sock, int fd)
76 {
77         struct msghdr msg = {0};
78         struct iovec iov;
79         struct cmsghdr *cmsg;
80         int myfds[1];
81         char buf[CMSG_SPACE(sizeof myfds)];
82         int *fdptr;
83
84         msg.msg_control = buf;
85         msg.msg_controllen = sizeof buf;
86         cmsg = CMSG_FIRSTHDR(&msg);
87         cmsg->cmsg_level = SOL_SOCKET;
88         cmsg->cmsg_type = SCM_RIGHTS;
89         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
90         fdptr = (int*)CMSG_DATA(cmsg);
91         fdptr[0] = fd;
92         msg.msg_controllen = cmsg->cmsg_len;
93         msg.msg_iov = &iov;
94         msg.msg_iovlen = 1;
95         iov.iov_base = "W";
96         iov.iov_len = 1;
97         sendmsg(sock, &msg, 0);
98 }
99
100 struct event *wake_set(int fd, void(*fn)(int,short,void*), void *data, int prio)
101 {
102         struct sockaddr_un addr;
103         struct han *h = malloc(sizeof(*h));
104
105         if (!h)
106                 return NULL;
107
108         h->fn = fn;
109         h->data = data;
110         h->disable = suspend_open();
111         h->sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
112         if (h->sock < 0 || h->disable < 0)
113                 goto abort;
114         addr.sun_family = AF_UNIX;
115         strcpy(addr.sun_path, "/run/suspend/registration");
116         if (connect(h->sock, (struct sockaddr*)&addr, sizeof(addr)) != 0)
117                 goto abort;
118
119         fcntl(h->sock, F_SETFL, fcntl(h->sock, F_GETFL, 0) | O_NONBLOCK);
120
121         send_fd(h->sock, fd);
122
123         event_set(&h->ev, fd, EV_READ|EV_PERSIST, wakeup_call, h);
124         event_set(&h->sev, h->sock, EV_READ|EV_PERSIST, wakeup_sock, h);
125         event_priority_set(&h->ev, prio);
126         event_priority_set(&h->sev, prio+1);
127         event_add(&h->ev, NULL);
128         event_add(&h->sev, NULL);
129
130         return &h->ev;
131
132 abort:
133         suspend_close(h->disable);
134         if (h->sock >= 0)
135                 close(h->sock);
136         free(h);
137         return NULL;
138 }
139
140 void wake_destroy(struct event *ev)
141 {
142         struct han *h = (struct han *)ev;
143         event_del(&h->ev);
144         event_del(&h->sev);
145         close(h->sock);
146         suspend_close(h->disable);
147         free(h);
148 }