screenblank.c revision 1.6 1 /* $NetBSD: screenblank.c,v 1.6 1997/07/30 22:54:40 jtc Exp $ */
2
3 /*-
4 * Copyright (c) 1996 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Screensaver daemon for the Sun 3 and SPARC.
41 */
42
43 #include <sys/cdefs.h>
44 #ifndef lint
45 __COPYRIGHT(
46 "@(#) Copyright (c) 1996 The NetBSD Foundation, Inc. All rights reserved.");
47 __RCSID("$NetBSD: screenblank.c,v 1.6 1997/07/30 22:54:40 jtc Exp $");
48 #endif
49
50 #include <sys/types.h>
51 #include <sys/time.h>
52 #include <sys/stat.h>
53 #include <sys/ioctl.h>
54 #include <sys/queue.h>
55 #include <ctype.h>
56 #include <err.h>
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <limits.h>
60 #include <math.h>
61 #include <paths.h>
62 #include <stdlib.h>
63 #include <stdio.h>
64 #include <string.h>
65 #include <signal.h>
66 #include <unistd.h>
67
68 #include <machine/fbio.h>
69
70 #include "pathnames.h"
71
72 struct dev_stat {
73 LIST_ENTRY(dev_stat) ds_link; /* linked list */
74 char *ds_path; /* path to device */
75 int ds_isfb; /* boolean; framebuffer? */
76 time_t ds_atime; /* time device last accessed */
77 time_t ds_mtime; /* time device last modified */
78 };
79 LIST_HEAD(ds_list, dev_stat) ds_list;
80
81 extern char *__progname;
82
83 int main __P((int, char *[]));
84 static void add_dev __P((char *, int));
85 static void change_state __P((int));
86 static void cvt_arg __P((char *, struct timeval *));
87 static void logpid __P((void));
88 static void sighandler __P((int));
89 static void usage __P((void));
90
91 int
92 main(argc, argv)
93 int argc;
94 char *argv[];
95 {
96 struct dev_stat *dsp;
97 struct timeval timo_on, timo_off, *tvp;
98 struct sigaction sa;
99 struct stat st;
100 int ch, change, fflag = 0, kflag = 0, mflag = 0, state;
101
102 LIST_INIT(&ds_list);
103
104 /*
105 * Set the default timeouts: 10 minutes on, .25 seconds off.
106 */
107 timo_on.tv_sec = 600;
108 timo_on.tv_usec = 0;
109 timo_off.tv_sec = 0;
110 timo_off.tv_usec = 250000;
111
112 while ((ch = getopt(argc, argv, "d:e:f:km")) != -1) {
113 switch (ch) {
114 case 'd':
115 cvt_arg(optarg, &timo_on);
116 break;
117
118 case 'e':
119 cvt_arg(optarg, &timo_off);
120 break;
121
122 case 'f':
123 fflag = 1;
124 add_dev(optarg, 1);
125 break;
126
127 case 'k':
128 if (mflag || kflag)
129 usage();
130 kflag = 1;
131 break;
132
133 case 'm':
134 if (kflag || mflag)
135 usage();
136 mflag = 1;
137 break;
138
139 default:
140 usage();
141 }
142 }
143 argc -= optind;
144 if (argc)
145 usage();
146
147 /*
148 * Add the keyboard, mouse, and default framebuffer devices
149 * as necessary. We _always_ check the console device.
150 */
151 add_dev(_PATH_CONSOLE, 0);
152 if (!kflag)
153 add_dev(_PATH_KEYBOARD, 0);
154 if (!mflag)
155 add_dev(_PATH_MOUSE, 0);
156 if (!fflag)
157 add_dev(_PATH_FB, 1);
158
159 /* Ensure that the framebuffer is on. */
160 state = FBVIDEO_ON;
161 change_state(state);
162 tvp = &timo_on;
163
164 /*
165 * Make sure the framebuffer gets turned back on when we're
166 * killed.
167 */
168 sa.sa_handler = sighandler;
169 sa.sa_flags = SA_NOCLDSTOP;
170 if (sigemptyset(&sa.sa_mask))
171 err(1, "sigemptyset");
172 if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL) ||
173 sigaction(SIGHUP, &sa, NULL))
174 err(1, "sigaction");
175
176 /* Detach. */
177 if (daemon(0, 0))
178 err(1, "daemon");
179 logpid();
180
181 /* Start the state machine. */
182 for (;;) {
183 change = 0;
184 for (dsp = ds_list.lh_first; dsp != NULL;
185 dsp = dsp->ds_link.le_next) {
186 /* Don't check framebuffers. */
187 if (dsp->ds_isfb)
188 continue;
189 if (stat(dsp->ds_path, &st) < 0)
190 err(1, "stat: %s", dsp->ds_path);
191 if (st.st_atime > dsp->ds_atime) {
192 change = 1;
193 dsp->ds_atime = st.st_atime;
194 }
195 if (st.st_mtime > dsp->ds_mtime) {
196 change = 1;
197 dsp->ds_mtime = st.st_mtime;
198 }
199 }
200
201 switch (state) {
202 case FBVIDEO_ON:
203 if (!change) {
204 state = FBVIDEO_OFF;
205 change_state(state);
206 tvp = &timo_off;
207 }
208 break;
209
210 case FBVIDEO_OFF:
211 if (change) {
212 state = FBVIDEO_ON;
213 change_state(state);
214 tvp = &timo_on;
215 }
216 break;
217 }
218
219 if (select(0, NULL, NULL, NULL, tvp) < 0)
220 err(1, "select");
221 }
222 /* NOTREACHED */
223 }
224
225 static void
226 add_dev(path, isfb)
227 char *path;
228 int isfb;
229 {
230 struct dev_stat *dsp1, *dsp2;
231
232 /* Create the entry... */
233 dsp1 = malloc(sizeof(struct dev_stat));
234 if (dsp1 == NULL)
235 errx(1, "can't allocate memory for %s", path);
236 bzero(dsp1, sizeof(struct dev_stat));
237 dsp1->ds_path = path;
238 dsp1->ds_isfb = isfb;
239
240 /* ...and put it in the list. */
241 if (ds_list.lh_first == NULL) {
242 LIST_INSERT_HEAD(&ds_list, dsp1, ds_link);
243 } else {
244 for (dsp2 = ds_list.lh_first; dsp2->ds_link.le_next != NULL;
245 dsp2 = dsp2->ds_link.le_next)
246 /* Nothing. */ ;
247 LIST_INSERT_AFTER(dsp2, dsp1, ds_link);
248 }
249 }
250
251 /* ARGSUSED */
252 static void
253 sighandler(sig)
254 int sig;
255 {
256
257 /* Kill the pid file and re-enable the framebuffer before exit. */
258 (void)unlink(_PATH_SCREENBLANKPID);
259 change_state(FBVIDEO_ON);
260 exit(0);
261 }
262
263 static void
264 change_state(state)
265 int state;
266 {
267 struct dev_stat *dsp;
268 int fd;
269
270 for (dsp = ds_list.lh_first; dsp != NULL; dsp = dsp->ds_link.le_next) {
271 /* Don't change the state of non-framebuffers! */
272 if (dsp->ds_isfb == 0)
273 continue;
274 if ((fd = open(dsp->ds_path, O_RDWR, 0)) < 0) {
275 warn("open: %s", dsp->ds_path);
276 continue;
277 }
278 if (ioctl(fd, FBIOSVIDEO, &state) < 0)
279 warn("ioctl: %s", dsp->ds_path);
280 (void)close(fd);
281 }
282 }
283
284 static void
285 cvt_arg(arg, tvp)
286 char *arg;
287 struct timeval *tvp;
288 {
289 char *cp;
290 double seconds = 0.0, exponent = -1.0;
291 int period = 0;
292
293 for (cp = arg; *cp != '\0'; ++cp) {
294 if (*cp == '.') {
295 if (period)
296 errx(1, "invalid argument: %s", arg);
297 period = 1;
298 continue;
299 }
300
301 if (!isdigit(*cp))
302 errx(1, "invalid argument: %s", arg);
303
304 if (period) {
305 seconds = seconds + ((*cp - '0') * pow(10.0, exponent));
306 exponent -= 1.0;
307 } else
308 seconds = (seconds * 10.0) + (*cp - '0');
309 }
310
311 tvp->tv_sec = (long)seconds;
312 tvp->tv_usec = (long)((seconds - tvp->tv_sec) * 1000000);
313 }
314
315 static void
316 logpid()
317 {
318 FILE *fp;
319
320 if ((fp = fopen(_PATH_SCREENBLANKPID, "w")) != NULL) {
321 fprintf(fp, "%u\n", getpid());
322 (void)fclose(fp);
323 }
324 }
325
326 static void
327 usage()
328 {
329
330 fprintf(stderr, "usage: %s [-k | -m] [-d timeout] [-e timeout] %s\n",
331 __progname, "[-f framebuffer]");
332 exit(1);
333 }
334