]> git.neil.brown.name Git - susman.git/blob - lsused.c
99179a89f68b7170e5cb68333dbfeec70e191809
[susman.git] / lsused.c
1
2 /*
3  * lsused - Linux SUSpend Event monitoring Daemon
4  *
5  * apps and services can send fds to this daemon.
6  * We will not allow suspend to happen if any fd is readable.
7  * A client should also notice that it is readable, take a
8  * shared lock on the suspend/disabled file and then read the event.
9  *
10  * The client opens connects on a unix domain socket to
11  * /var/run/suspend/registration
12  * It sets 'W' with some fds attached to be watched.
13  * On notification if any fds are readable we send by 'S' to say
14  * Suspend Soon and wait for 'R' to say 'Ready'.
15  * We don't bother checking the fds again until the next suspend
16  * attempt.
17  *
18  *
19  * Copyright (C) 2011 Neil Brown <neilb@suse.de>
20  *
21  *    This program is free software; you can redistribute it and/or modify
22  *    it under the terms of the GNU General Public License as published by
23  *    the Free Software Foundation; either version 2 of the License, or
24  *    (at your option) any later version.
25  *
26  *    This program is distributed in the hope that it will be useful,
27  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
28  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  *    GNU General Public License for more details.
30  *
31  *    You should have received a copy of the GNU General Public License along
32  *    with this program; if not, write to the Free Software Foundation, Inc.,
33  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34  */
35 #define _GNU_SOURCE
36 #include <stdlib.h>
37 #include <event.h>
38 #include <poll.h>
39 #include <sys/socket.h>
40 #include <sys/un.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include "libsus.h"
44
45
46 struct handle {
47         struct event    ev;
48         int             sent;           /* 'S' has been sent */
49         int             suspending;     /* ... 'R' hasn't been received yet */
50         struct handle   *next;
51         struct state    *state;
52 };
53
54 struct state {
55         int             waiting;        /* Number of replies waiting for */
56         struct handle   *handles;       /* linked list of handles */
57         struct pollfd   *fds;           /* for 'poll' */
58         struct handle   **hans;         /* aligned with fds */
59         int             nfds;           /* number of active 'fds' */
60         int             fdsize;         /* allocated size of fds array */
61         void            *sus;           /* handle from suspend_watch */
62 };
63
64 static void del_fd(struct state *state, int i)
65 {
66         state->fds[i] = state->fds[state->nfds - 1];
67         state->hans[i] = state->hans[state->nfds - 1];
68         state->nfds--;
69 }
70
71 static void add_fd(struct state *state, struct handle *han,
72                    int fd, short events)
73 {
74         int n = state->nfds;
75         if (state->nfds >= state->fdsize) {
76                 /*need to make bigger */
77                 int need = 16;
78                 while (need < n+1)
79                         need *= 2;
80                 state->fds = realloc(state->fds,
81                                      need * sizeof(struct pollfd));
82                 state->hans = realloc(state->hans,
83                                       need * sizeof(struct handle *));
84         }
85         state->hans[n] = han;
86         state->fds[n].fd = fd;
87         state->fds[n].events = events;
88         state->nfds++;
89 }
90
91 static void add_han(struct handle *han, struct state *state)
92 {
93
94         han->next = state->handles;
95         state->handles = han;
96 }
97
98 static void del_han(struct handle *han)
99 {
100         struct state *state = han->state;
101         struct handle **hanp = &state->handles;
102         struct handle *h;
103         int i;
104
105         /* First remove the fds; */
106         for (i = 0; i < state->nfds ; i++)
107                 if (state->hans[i] == han) {
108                         del_fd(state, i);
109                         i--;
110                 }
111
112         /* Then remove the han */
113         for (h = *hanp; h; hanp = &h->next, h = *hanp) {
114                 if (h == han) {
115                         *hanp = h->next;
116                         break;
117                 }
118         }
119 }
120
121 static void do_read(int fd, short ev, void *data)
122 {
123         struct handle *han = data;
124         char buf;
125         struct msghdr msg;
126         struct cmsghdr *cm;
127         struct iovec iov;
128         char mbuf[100];
129
130         buf = 0;
131         msg.msg_name = NULL;
132         msg.msg_namelen = 0;
133         msg.msg_iov = &iov;
134         msg.msg_iovlen = 1;
135         iov.iov_base = &buf;
136         iov.iov_len = 1;
137         msg.msg_control = mbuf;
138         msg.msg_controllen = sizeof(mbuf);
139         msg.msg_flags = 0;
140
141         if (recvmsg(fd, &msg, MSG_CMSG_CLOEXEC|MSG_DONTWAIT) < 0
142             && errno == EAGAIN)
143                 return;
144
145         switch (buf) {
146         case 'W':
147                 for (cm = CMSG_FIRSTHDR(&msg);
148                      cm != NULL;
149                      cm = CMSG_NXTHDR(&msg, cm))
150                         if (cm->cmsg_level == SOL_SOCKET &&
151                             cm->cmsg_type == SCM_RIGHTS) {
152                                 int *fdptr = (int*)CMSG_DATA(cm);
153                                 int n = (cm->cmsg_len -
154                                          CMSG_ALIGN(sizeof(struct cmsghdr)))
155                                         / sizeof(int);
156                                 int i;
157                                 for (i = 0; i < n; i++)
158                                         add_fd(han->state, han, fdptr[i],
159                                                 POLLIN|POLLPRI);
160                         }
161                 write(fd, "A", 1);
162                 break;
163
164         case 'R':
165                 if (han->suspending) {
166                         han->suspending = 0;
167                         han->state->waiting--;
168                         if (han->state->waiting == 0)
169                                 suspend_ok(han->state->sus);
170                 }
171                 break;
172
173         default:
174                 event_del(&han->ev);
175                 del_han(han);
176                 close(fd);
177         }
178 }
179
180 static void do_accept(int fd, short ev, void *data)
181 {
182         struct state *state = data;
183         struct handle *han;
184         int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
185         if (newfd < 0)
186                 return;
187
188         han = malloc(sizeof(*han));
189         if (!han) {
190                 close(newfd);
191                 return;
192         }
193         han->sent = 0;
194         han->suspending = 0;
195         han->state = state;
196         add_han(han, state);
197         event_set(&han->ev, newfd, EV_READ | EV_PERSIST, do_read, han);
198         event_add(&han->ev, NULL);
199         write(newfd, "A", 1);
200 }
201
202 static int do_suspend(void *data)
203 {
204         struct state *state = data;
205         struct handle *han;
206         int n;
207         int i;
208
209         n = poll(state->fds, state->nfds, 0);
210         if (n == 0)
211                 /* nothing happening */
212                 return 1;
213         for (han = state->handles ; han ; han = han->next)
214                 han->sent = 0;
215         state->waiting = 1;
216         for (i = 0; i < state->nfds; i++)
217                 if (state->fds[i].revents) {
218                         han = state->hans[i];
219                         if (!han->sent) {
220                                 han->sent = 1;
221                                 han->suspending = 1;
222                                 write(EVENT_FD(&han->ev), "S", 1);
223                                 state->waiting++;
224                         }
225                 }
226         state->waiting--;
227         return (state->waiting == 0);
228 }
229
230 static void did_resume(void *data)
231 {
232         struct state *state = data;
233         struct handle *han;
234
235         for (han = state->handles ; han ; han = han->next)
236                 if (han->sent)
237                         write(EVENT_FD(&han->ev), "A", 1);
238 }
239
240 main(int argc, char *argv[])
241 {
242         struct sockaddr_un addr;
243         struct state state;
244         struct event ev;
245         int s;
246
247         memset(&state, 0, sizeof(state));
248
249         s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
250         addr.sun_family = AF_UNIX;
251         strcpy(addr.sun_path, "/var/run/suspend/registration");
252         unlink("/var/run/suspend/registration");
253         bind(s, (struct sockaddr *)&addr, sizeof(addr));
254         listen(s, 20);
255
256         event_init();
257
258         state.sus = suspend_watch(do_suspend, did_resume, &state);
259         event_set(&ev, s, EV_READ | EV_PERSIST, do_accept, &state);
260         event_add(&ev, NULL);
261
262         event_loop(0);
263         exit(0);
264 }