1 /* $NetBSD: screenblank.c,v 1.15 2001/11/06 22:56:02 augustss Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 1998 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, 1998 \ 47 The NetBSD Foundation, Inc. All rights reserved."); 48 __RCSID("$NetBSD: screenblank.c,v 1.15 2001/11/06 22:56:02 augustss Exp $"); 49 #endif 50 51 #include <sys/types.h> 52 #include <sys/time.h> 53 #include <sys/stat.h> 54 #include <sys/ioctl.h> 55 #include <sys/queue.h> 56 #include <ctype.h> 57 #include <err.h> 58 #include <errno.h> 59 #include <fcntl.h> 60 #include <limits.h> 61 #include <math.h> 62 #include <paths.h> 63 #include <stdlib.h> 64 #include <stdio.h> 65 #include <string.h> 66 #include <signal.h> 67 #include <syslog.h> 68 #include <unistd.h> 69 #include <util.h> 70 71 #include <dev/wscons/wsconsio.h> 72 73 #ifdef HAVE_FBIO 74 #include <dev/sun/fbio.h> 75 #endif 76 77 #include "pathnames.h" 78 79 u_long setvideo = WSDISPLAYIO_SVIDEO; /* "set video" ioctl */ 80 int videoon = WSDISPLAYIO_VIDEO_ON; /* value for "on" */ 81 int videooff = WSDISPLAYIO_VIDEO_OFF; /* value for "off" */ 82 83 struct dev_stat { 84 LIST_ENTRY(dev_stat) ds_link; /* linked list */ 85 const char *ds_path; /* path to device */ 86 int ds_isfb; /* boolean; framebuffer? */ 87 time_t ds_atime; /* time device last accessed */ 88 time_t ds_mtime; /* time device last modified */ 89 }; 90 LIST_HEAD(ds_list, dev_stat) ds_list; 91 92 int main(int, char *[]); 93 static void add_dev(const char *, int); 94 static void change_state(int); 95 static void cvt_arg(char *, struct timeval *); 96 static void sighandler(int); 97 static void usage(void); 98 99 int 100 main(int argc, char *argv[]) 101 { 102 struct dev_stat *dsp; 103 struct timeval timo_on, timo_off, *tvp, tv; 104 struct sigaction sa; 105 struct stat st; 106 int ch, change, fflag = 0, kflag = 0, mflag = 0, state; 107 const char *kbd, *mouse, *display; 108 109 LIST_INIT(&ds_list); 110 111 /* 112 * Set the default timeouts: 10 minutes on, .25 seconds off. 113 */ 114 timo_on.tv_sec = 600; 115 timo_on.tv_usec = 0; 116 timo_off.tv_sec = 0; 117 timo_off.tv_usec = 250000; 118 119 while ((ch = getopt(argc, argv, "d:e:f:km")) != -1) { 120 switch (ch) { 121 case 'd': 122 cvt_arg(optarg, &timo_on); 123 break; 124 125 case 'e': 126 cvt_arg(optarg, &timo_off); 127 break; 128 129 case 'f': 130 fflag = 1; 131 add_dev(optarg, 1); 132 break; 133 134 case 'k': 135 if (mflag || kflag) 136 usage(); 137 kflag = 1; 138 break; 139 140 case 'm': 141 if (kflag || mflag) 142 usage(); 143 mflag = 1; 144 break; 145 146 default: 147 usage(); 148 } 149 } 150 argc -= optind; 151 if (argc) 152 usage(); 153 154 /* 155 * Default to WSCONS support. 156 */ 157 kbd = _PATH_WSKBD; 158 mouse = _PATH_WSMOUSE; 159 display = _PATH_WSDISPLAY; 160 161 #ifdef HAVE_FBIO 162 /* 163 * If a display device wasn't specified, check to see which we 164 * have. If we can't open the WSCONS display, fall back to fbio. 165 */ 166 if (!fflag) { 167 int fd; 168 169 if ((fd = open(display, O_RDONLY, 0666)) == -1) 170 setvideo = FBIOSVIDEO; 171 else 172 (void) close(fd); 173 } 174 175 /* 176 * Do this here so that -f ... args above can influence us. 177 */ 178 if (setvideo == FBIOSVIDEO) { 179 videoon = FBVIDEO_ON; 180 videooff = FBVIDEO_OFF; 181 kbd = _PATH_KEYBOARD; 182 mouse = _PATH_MOUSE; 183 display = _PATH_FB; 184 } 185 #endif 186 187 /* 188 * Add the keyboard, mouse, and default framebuffer devices 189 * as necessary. We _always_ check the console device. 190 */ 191 add_dev(_PATH_CONSOLE, 0); 192 if (!kflag) 193 add_dev(kbd, 0); 194 if (!mflag) 195 add_dev(mouse, 0); 196 if (!fflag) 197 add_dev(display, 1); 198 199 /* Ensure that the framebuffer is on. */ 200 state = videoon; 201 change_state(state); 202 tvp = &timo_on; 203 204 /* 205 * Make sure the framebuffer gets turned back on when we're 206 * killed. 207 */ 208 sa.sa_handler = sighandler; 209 sa.sa_flags = SA_NOCLDSTOP; 210 if (sigemptyset(&sa.sa_mask)) 211 err(1, "sigemptyset"); 212 if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL) || 213 sigaction(SIGHUP, &sa, NULL)) 214 err(1, "sigaction"); 215 216 openlog("screenblank", LOG_PID, LOG_DAEMON); 217 /* Detach. */ 218 if (daemon(0, 0)) 219 err(1, "daemon"); 220 pidfile(NULL); 221 222 /* Start the state machine. */ 223 for (;;) { 224 change = 0; 225 for (dsp = ds_list.lh_first; dsp != NULL; 226 dsp = dsp->ds_link.le_next) { 227 /* Don't check framebuffers. */ 228 if (dsp->ds_isfb) 229 continue; 230 if (stat(dsp->ds_path, &st) == -1) { 231 syslog(LOG_CRIT, 232 "Can't stat `%s' (%m)", dsp->ds_path); 233 exit(1); 234 } 235 if (st.st_atime > dsp->ds_atime) { 236 change = 1; 237 dsp->ds_atime = st.st_atime; 238 } 239 if (st.st_mtime > dsp->ds_mtime) { 240 change = 1; 241 dsp->ds_mtime = st.st_mtime; 242 } 243 } 244 245 if (state == videoon) { 246 if (!change) { 247 state = videooff; 248 change_state(state); 249 tvp = &timo_off; 250 } 251 } else { 252 if (change) { 253 state = videoon; 254 change_state(state); 255 tvp = &timo_on; 256 } 257 } 258 259 tv = *tvp; 260 if (select(0, NULL, NULL, NULL, &tv) == -1) 261 err(1, "select"); 262 } 263 /* NOTREACHED */ 264 } 265 266 static void 267 add_dev(const char *path, int isfb) 268 { 269 struct dev_stat *dsp; 270 struct stat sb; 271 272 /* Make sure we can stat the device. */ 273 if (stat(path, &sb) == -1) { 274 warn("Can't stat `%s'", path); 275 return; 276 } 277 278 #ifdef HAVE_FBIO 279 /* 280 * We default to WSCONS. If this is a frame buffer 281 * device, check to see if it responds to the old 282 * Sun-style fbio ioctls. If so, switch to fbio mode. 283 */ 284 if (isfb && setvideo != FBIOSVIDEO) { 285 int onoff, fd; 286 287 if ((fd = open(path, O_RDWR, 0666)) == -1) { 288 warn("Can't open `%s'", path); 289 return; 290 } 291 if ((ioctl(fd, FBIOGVIDEO, &onoff)) == 0) 292 setvideo = FBIOSVIDEO; 293 (void)close(fd); 294 } 295 #endif 296 297 /* Create the entry... */ 298 dsp = malloc(sizeof(struct dev_stat)); 299 if (dsp == NULL) 300 err(1, "Can't allocate memory for `%s'", path); 301 (void)memset(dsp, 0, sizeof(struct dev_stat)); 302 dsp->ds_path = path; 303 dsp->ds_isfb = isfb; 304 305 /* ...and put it in the list. */ 306 LIST_INSERT_HEAD(&ds_list, dsp, ds_link); 307 } 308 309 /* ARGSUSED */ 310 static void 311 sighandler(int sig) 312 { 313 314 /* Kill the pid file and re-enable the framebuffer before exit. */ 315 change_state(videoon); 316 exit(0); 317 } 318 319 static void 320 change_state(int state) 321 { 322 struct dev_stat *dsp; 323 int fd; 324 int fail = 1; 325 326 for (dsp = ds_list.lh_first; dsp != NULL; dsp = dsp->ds_link.le_next) { 327 /* Don't change the state of non-framebuffers! */ 328 if (dsp->ds_isfb == 0) 329 continue; 330 if ((fd = open(dsp->ds_path, O_RDWR, 0)) == -1) { 331 syslog(LOG_WARNING, "Can't open `%s' (%m)", 332 dsp->ds_path); 333 continue; 334 } 335 if (ioctl(fd, setvideo, &state) == -1) 336 syslog(LOG_WARNING, "Can't set video on `%s' (%m)", 337 dsp->ds_path); 338 else 339 fail = 0; 340 (void)close(fd); 341 } 342 if (fail) { 343 syslog(LOG_CRIT, "No frame buffer devices, exiting\n"); 344 exit(1); 345 } 346 } 347 348 static void 349 cvt_arg(char *arg, struct timeval *tvp) 350 { 351 char *cp; 352 int seconds, microseconds, factor; 353 int period = 0; 354 factor = 1000000; 355 microseconds = 0; 356 seconds = 0; 357 358 for (cp = arg; *cp != '\0'; ++cp) { 359 if (*cp == '.') { 360 if (period) 361 errx(1, "Invalid argument: %s", arg); 362 period = 1; 363 continue; 364 } 365 366 if (!isdigit(*cp)) 367 errx(1, "Invalid argument: %s", arg); 368 369 if (period) { 370 if (factor > 1) { 371 microseconds = microseconds * 10 + (*cp - '0'); 372 factor /= 10; 373 } 374 } else 375 seconds = (seconds * 10) + (*cp - '0'); 376 } 377 378 tvp->tv_sec = seconds; 379 if (factor > 1) 380 microseconds *= factor; 381 382 tvp->tv_usec = microseconds; 383 } 384 385 static void 386 usage(void) 387 { 388 389 (void)fprintf(stderr, 390 "Usage: %s [-k | -m] [-d timeout] [-e timeout] [-f framebuffer]\n", 391 getprogname()); 392 exit(1); 393 } 394