3 * We provide a wakeup service to use the rtc wakealarm
4 * to ensure the system is running at given times and to
7 * Client can connect and register a time as seconds since epoch
8 * We echo back the time and then when the time comes we echo "Now".
9 * We keep system awake until another time is written, or until
10 * connection is closed.
12 * Copyright (C) 2011 Neil Brown <neilb@suse.de>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <sys/socket.h>
41 time_t stamp; /* When to wake */
42 int active; /* stamp has passed */
43 struct conn *next; /* sorted by 'stamp' */
57 static void do_timeout(int fd, short ev, void *data);
58 static void add_han(struct conn *han)
60 struct state *state = han->state;
61 struct conn **hanp = &state->conns;
66 (*hanp)->stamp < han->stamp)
67 hanp = &(*hanp)->next;
71 if (event_get_base(&state->tev))
72 evtimer_del(&state->tev);
73 do_timeout(0, 0, (void*)state);
76 static void del_han(struct conn *han)
78 struct state *state = han->state;
79 struct conn **hanp = &state->conns;
83 hanp = &(*hanp)->next;
88 state->active_count--;
89 if (state->active_count == 0
91 suspend_allow(state->disablefd);
98 static void destroy_han(struct conn *han)
104 static void do_read(int fd, short ev, void *data)
106 struct conn *han = data;
110 n = read(fd, buf, sizeof(buf)-1);
111 if (n < 0 && errno == EAGAIN)
120 han->stamp = atol(buf);
121 sprintf(buf, "%lld\n", (long long)han->stamp);
122 write(fd, buf, strlen(buf));
126 static void do_timeout(int fd, short ev, void *data)
128 struct state *state = data;
130 time_t now = time(0);
132 for (han = state->conns; han && han->stamp <= now; han = han->next)
135 han->state->active_count++;
136 write(EVENT_FD(&han->ev), "Now\n", 4);
140 tv.tv_sec = han->stamp - now;
142 evtimer_add(&state->tev, &tv);
146 static void do_accept(int fd, short ev, void *data)
148 struct state *state = data;
150 int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
154 han = malloc(sizeof(*han));
162 state->active_count++;
163 han->next = state->conns;
166 event_set(&han->ev, newfd, EV_READ | EV_PERSIST, do_read, han);
167 event_add(&han->ev, NULL);
168 write(newfd, "0\n", 2);
171 static int do_suspend(void *data)
173 struct state *state = data;
178 if (event_get_base(&state->tev))
179 evtimer_del(&state->tev);
180 /* active_count must be zero */
181 if (state->conns == NULL)
184 if (state->conns->stamp > now + 4) {
185 int fd = open("/sys/class/rtc/rtc0/since_epoch", O_RDONLY);
186 time_t rtc_now = now;
189 int n = read(fd, buf, 20);
191 if (n > 1 && n < 20) {
193 rtc_now = strtoul(buf, NULL, 10);
196 fd = open("/sys/class/rtc/rtc0/wakealarm", O_WRONLY);
200 sprintf(buf, "%lld\n",
201 (long long)state->conns->stamp
202 - now + rtc_now - 2);
203 write(fd, buf, strlen(buf));
208 /* too close to next wakeup */
209 if (!state->disabled) {
210 suspend_block(state->disablefd);
216 static void do_resume(void *data)
218 struct state *state = data;
220 do_timeout(0, 0, (void*)state);
223 int main(int argc, char *argv[])
226 struct sockaddr_un addr;
229 st.disablefd = suspend_open();
234 s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
235 addr.sun_family = AF_UNIX;
236 strcpy(addr.sun_path, "/run/suspend/wakealarm");
237 unlink("/run/suspend/wakealarm");
238 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
243 st.watcher = suspend_watch(do_suspend, do_resume, &st);
244 event_set(&st.ev, s, EV_READ | EV_PERSIST, do_accept, &st);
245 event_add(&st.ev, NULL);
246 evtimer_set(&st.tev, do_timeout, &st);