2 * lsusd - Linus SUSpend daemon.
3 * This daemon enters suspend when required and allows clients
4 * to block suspend, request suspend, or be notified of suspend.
6 * Copyright (C) 2011 Neil Brown <neilb@suse.de>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 static void alert_watchers(void)
37 fd = open("/run/suspend/watching-next",
38 O_RDWR|O_CREAT|O_TRUNC, 0640);
42 fd = open("/run/suspend/watching",
43 O_RDWR|O_CREAT|O_TRUNC, 0640);
46 if (write(fd, &zero, 1) != 1)
49 /* all watches must have moved to next file */
53 static void cycle_watchers(void)
58 fd = open("/run/suspend/watching", O_RDWR|O_CREAT, 0640);
61 rename("/run/suspend/watching-next",
62 "/run/suspend/watching");
63 zero[0] = zero[1] = 0;
68 static int read_wakeup_count()
74 fd = open("/sys/power/wakeup_count", O_RDONLY);
77 n = read(fd, buf, sizeof(buf)-1);
85 static int set_wakeup_count(int count)
92 return 1; /* Something wrong - just suspend */
94 fd = open("/sys/power/wakeup_count", O_RDWR);
98 snprintf(buf, sizeof(buf), "%d", count);
99 n = write(fd, buf, strlen(buf));
106 static void catch(int sig)
111 static void wait_request(int dirfd)
113 int found_immediate = 0;
114 int found_request = 0;
119 sigset_t set, oldset;
121 sigaddset(&set, SIGIO);
123 sigprocmask(SIG_BLOCK, &set, &oldset);
124 signal(SIGIO, catch);
126 fcntl(dirfd, F_NOTIFY, DN_CREATE);
129 dir = fdopendir(dup(dirfd));
130 while ((de = readdir(dir)) != NULL) {
131 if (strcmp(de->d_name, "immediate") == 0)
133 if (strcmp(de->d_name, "request") == 0)
137 if (!found_request && !found_immediate)
140 signal(SIGIO, SIG_DFL);
141 sigprocmask(SIG_UNBLOCK, &set, &oldset);
142 } while (!found_immediate && !found_request);
145 static int request_valid()
147 /* check if the request to suspend is still valid.
148 * If the 'immediate' file is not locked, we remove
149 * and ignore it as the requesting process has died
151 int fd = open("/run/suspend/immediate", O_RDWR);
153 if (flock(fd, LOCK_EX|LOCK_NB) == 0) {
154 /* we got the lock, so owner must have died */
155 unlink("/run/suspend/immediate");
163 fd = open("/run/suspend/request", O_RDONLY);
170 static void do_suspend(void)
172 int fd = open("/sys/power/state", O_RDWR);
174 write(fd, "mem\n", 4);
180 main(int argc, char *argv)
185 mkdir("/run/suspend", 0770);
187 dir = open("/run/suspend", O_RDONLY);
188 disable = open("/run/suspend/disabled", O_RDWR|O_CREAT, 0640);
190 if (dir < 0 || disable < 0)
193 /* Create the initial files */
204 /* Don't accept an old request */
205 unlink("/run/suspend/request");
207 if (flock(disable, LOCK_EX|LOCK_NB) != 0) {
208 flock(disable, LOCK_EX);
209 flock(disable, LOCK_UN);
210 unlink("/run/suspend/request");
211 /* blocked - so need to ensure request still valid */
214 flock(disable, LOCK_UN);;
215 /* we got that without blocking but are not holding it */
217 /* Next two might block, but that doesn't abort suspend */
218 count = read_wakeup_count();
219 fstat(disable, &stb);
223 fstat(disable, &stb);
224 if (flock(disable, LOCK_EX|LOCK_NB) == 0
226 && ts.tv_sec == stb.st_atim.tv_sec
227 && ts.tv_nsec == stb.st_atim.tv_nsec
228 && set_wakeup_count(count))
230 flock(disable, LOCK_UN);