1/* $NetBSD: screenblank.c,v 1.28 2008/07/21 13:36:59 lukem Exp $ */ 2 3/*- 4 * Copyright (c) 1996-2002 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * Screensaver daemon for the Sun 3 and SPARC, and platforms using WSCONS. 34 */ 35 36#include <sys/cdefs.h> 37#ifndef lint 38__COPYRIGHT("@(#) Copyright (c) 1996-2002\ 39 The NetBSD Foundation, Inc. All rights reserved."); 40__RCSID("$NetBSD: screenblank.c,v 1.28 2008/07/21 13:36:59 lukem Exp $"); 41#endif 42 43#include <sys/types.h> 44#include <sys/time.h> 45#include <sys/stat.h> 46#include <sys/ioctl.h> 47#include <sys/queue.h> 48#include <ctype.h> 49#include <err.h> 50#include <errno.h> 51#include <fcntl.h> 52#include <limits.h> 53#include <math.h> 54#include <paths.h> 55#include <stdlib.h> 56#include <stdio.h> 57#include <string.h> 58#include <signal.h> 59#include <syslog.h> 60#include <unistd.h> 61#include <util.h> 62 63#include <dev/wscons/wsconsio.h> 64 65#ifdef HAVE_FBIO 66#include <dev/sun/fbio.h> 67#endif 68 69#include "pathnames.h" 70 71static u_long setvideo = WSDISPLAYIO_SVIDEO; /* "set video" ioctl */ 72static int videoon = WSDISPLAYIO_VIDEO_ON; /* value for "on" */ 73static int videooff = WSDISPLAYIO_VIDEO_OFF; /* value for "off" */ 74 75struct dev_stat { 76 LIST_ENTRY(dev_stat) ds_link; /* linked list */ 77 const char *ds_path; /* path to device */ 78 int ds_isfb; /* boolean; framebuffer? */ 79 time_t ds_atime; /* time device last accessed */ 80 time_t ds_mtime; /* time device last modified */ 81}; 82static LIST_HEAD(ds_list, dev_stat) ds_list; 83 84static void add_dev(const char *, int); 85static void change_state(int); 86static void cvt_arg(char *, struct timespec *); 87__dead static void sighandler(int); 88static int is_graphics_fb(struct dev_stat *); 89__dead static void usage(void); 90 91int 92main(int argc, char *argv[]) 93{ 94 struct dev_stat *dsp; 95 struct timespec timo_on, timo_off, *tvp, tv; 96 struct sigaction sa; 97 struct stat st; 98 int ch, change, fflag = 0, kflag = 0, mflag = 0, state; 99 int bflag = 0, uflag = 0; 100 const char *kbd, *mouse, *display; 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_nsec = 0; 109 timo_off.tv_sec = 0; 110 timo_off.tv_nsec = 250000000; 111 112 while ((ch = getopt(argc, argv, "bd:e:f:i:kmu")) != -1) { 113 switch (ch) { 114 case 'b': 115 bflag = 1; 116 uflag = 0; 117 break; 118 119 case 'd': 120 cvt_arg(optarg, &timo_on); 121 break; 122 123 case 'e': 124 cvt_arg(optarg, &timo_off); 125 break; 126 127 case 'f': 128 fflag = 1; 129 add_dev(optarg, 1); 130 break; 131 132 case 'i': 133 add_dev(optarg, 0); 134 break; 135 136 case 'k': 137 if (mflag || kflag) 138 usage(); 139 kflag = 1; 140 break; 141 142 case 'm': 143 if (kflag || mflag) 144 usage(); 145 mflag = 1; 146 break; 147 148 case 'u': 149 uflag = 1; 150 bflag = 0; 151 break; 152 153 default: 154 usage(); 155 } 156 } 157 argc -= optind; 158 if (argc) 159 usage(); 160 161 /* 162 * Default to WSCONS support. 163 */ 164 kbd = _PATH_WSKBD; 165 mouse = _PATH_WSMOUSE; 166 display = _PATH_WSDISPLAY; 167 168#ifdef HAVE_FBIO 169 /* 170 * If a display device wasn't specified, check to see which we 171 * have. If we can't open the WSCONS display, fall back to fbio. 172 */ 173 if (!fflag) { 174 int fd; 175 176 if ((fd = open(display, O_RDONLY, 0666)) == -1) 177 setvideo = FBIOSVIDEO; 178 else 179 (void) close(fd); 180 } 181 182 /* 183 * Do this here so that -f ... args above can influence us. 184 */ 185 if (setvideo == FBIOSVIDEO) { 186 videoon = FBVIDEO_ON; 187 videooff = FBVIDEO_OFF; 188 kbd = _PATH_KEYBOARD; 189 mouse = _PATH_MOUSE; 190 display = _PATH_FB; 191 } 192#endif 193 194 /* 195 * Add the default framebuffer device if necessary. 196 * We _always_ check the console device. 197 */ 198 add_dev(_PATH_CONSOLE, 0); 199 if (!fflag) 200 add_dev(display, 1); 201 202 /* 203 * If this is an one-off blank/unblank request, handle it now. 204 * We don't need to open keyboard/mouse device for that. 205 */ 206 if (bflag || uflag) { 207 change_state(bflag ? videooff : videoon); 208 exit(0); 209 } 210 211 212 /* Add the keyboard and mouse devices as necessary. */ 213 if (!kflag) 214 add_dev(kbd, 0); 215 if (!mflag) 216 add_dev(mouse, 0); 217 218 /* Ensure that the framebuffer is on. */ 219 state = videoon; 220 change_state(state); 221 tvp = &timo_on; 222 223 /* 224 * Make sure the framebuffer gets turned back on when we're 225 * killed. 226 */ 227 sa.sa_handler = sighandler; 228 sa.sa_flags = SA_NOCLDSTOP; 229 if (sigemptyset(&sa.sa_mask)) 230 err(1, "sigemptyset"); 231 if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL) || 232 sigaction(SIGHUP, &sa, NULL)) 233 err(1, "sigaction"); 234 235 openlog("screenblank", LOG_PID, LOG_DAEMON); 236 /* Detach. */ 237 if (daemon(0, 0)) 238 err(1, "daemon"); 239 pidfile(NULL); 240 241 /* Start the state machine. */ 242 for (;;) { 243 change = 0; 244 LIST_FOREACH(dsp, &ds_list, ds_link) { 245 /* Don't check framebuffers in graphics mode. */ 246 if (is_graphics_fb(dsp)) 247 continue; 248 if (stat(dsp->ds_path, &st) == -1) { 249 syslog(LOG_CRIT, 250 "Can't stat `%s' (%m)", dsp->ds_path); 251 exit(1); 252 } 253 if (st.st_atime > dsp->ds_atime) { 254 change = 1; 255 dsp->ds_atime = st.st_atime; 256 } 257 if (st.st_mtime > dsp->ds_mtime) { 258 change = 1; 259 dsp->ds_mtime = st.st_mtime; 260 } 261 } 262 263 if (state == videoon) { 264 if (!change) { 265 state = videooff; 266 change_state(state); 267 tvp = &timo_off; 268 } 269 } else { 270 if (change) { 271 state = videoon; 272 change_state(state); 273 tvp = &timo_on; 274 } 275 } 276 277 tv = *tvp; 278 if (nanosleep(&tv, NULL) == -1) 279 err(1, "nanosleep"); 280 } 281 /* NOTREACHED */ 282} 283 284static void 285add_dev(const char *path, int isfb) 286{ 287 struct dev_stat *dsp; 288 struct stat sb; 289 290 /* Make sure we can stat the device. */ 291 if (stat(path, &sb) == -1) { 292 warn("Can't stat `%s'", path); 293 return; 294 } 295 296#ifdef HAVE_FBIO 297 /* 298 * We default to WSCONS. If this is a frame buffer 299 * device, check to see if it responds to the old 300 * Sun-style fbio ioctls. If so, switch to fbio mode. 301 */ 302 if (isfb && setvideo != FBIOSVIDEO) { 303 int onoff, fd; 304 305 if ((fd = open(path, O_RDWR, 0666)) == -1) { 306 warn("Can't open `%s'", path); 307 return; 308 } 309 if ((ioctl(fd, FBIOGVIDEO, &onoff)) == 0) 310 setvideo = FBIOSVIDEO; 311 (void)close(fd); 312 } 313#endif 314 315 /* Create the entry... */ 316 dsp = malloc(sizeof(struct dev_stat)); 317 if (dsp == NULL) 318 err(1, "Can't allocate memory for `%s'", path); 319 (void)memset(dsp, 0, sizeof(struct dev_stat)); 320 dsp->ds_path = path; 321 dsp->ds_isfb = isfb; 322 323 /* ...and put it in the list. */ 324 LIST_INSERT_HEAD(&ds_list, dsp, ds_link); 325} 326 327/* ARGSUSED */ 328static void 329sighandler(int sig) 330{ 331 332 /* Kill the pid file and re-enable the framebuffer before exit. */ 333 change_state(videoon); 334 exit(0); 335} 336 337/* 338 * Return 1 if we are a framebuffer in graphics mode or a framebuffer 339 * where we cannot tell the mode. Return 0 if we are not a framebuffer 340 * device, or a wscons framebuffer in text mode. 341 */ 342static int 343is_graphics_fb(struct dev_stat *dsp) 344{ 345 int fd; 346 int state; 347 348 if (dsp->ds_isfb == 0) 349 return 0; 350 351 /* We can't tell if we are not a wscons device */ 352 if (setvideo != WSDISPLAYIO_SVIDEO) 353 return 1; 354 355 if ((fd = open(dsp->ds_path, O_RDWR, 0)) == -1) { 356 syslog(LOG_WARNING, "Cannot open `%s' (%m)", dsp->ds_path); 357 return 1; 358 } 359 360 if (ioctl(fd, WSDISPLAYIO_GMODE, &state) == -1) { 361 syslog(LOG_WARNING, "Cannot get mode on `%s' (%m)", 362 dsp->ds_path); 363 /* We can't tell, so we say we are mapped */ 364 state = WSDISPLAYIO_MODE_MAPPED; 365 } 366 367 (void)close(fd); 368 369 return state != WSDISPLAYIO_MODE_EMUL; 370} 371 372static void 373change_state(int state) 374{ 375 struct dev_stat *dsp; 376 int fd; 377 int fail = 1; 378 379 LIST_FOREACH(dsp, &ds_list, ds_link) { 380 /* Don't change the state of non-framebuffers! */ 381 if (dsp->ds_isfb == 0) 382 continue; 383 if ((fd = open(dsp->ds_path, O_RDWR, 0)) == -1) { 384 syslog(LOG_WARNING, "Can't open `%s' (%m)", 385 dsp->ds_path); 386 continue; 387 } 388 if (ioctl(fd, setvideo, &state) == -1) 389 syslog(LOG_WARNING, "Can't set video on `%s' (%m)", 390 dsp->ds_path); 391 else 392 fail = 0; 393 (void)close(fd); 394 } 395 if (fail) { 396 syslog(LOG_CRIT, "No frame buffer devices, exiting\n"); 397 exit(1); 398 } 399} 400 401static void 402cvt_arg(char *arg, struct timespec *tvp) 403{ 404 char *cp; 405 int seconds, nanoseconds, factor; 406 int period = 0; 407 factor = 1000000000; 408 nanoseconds = 0; 409 seconds = 0; 410 411 for (cp = arg; *cp != '\0'; ++cp) { 412 if (*cp == '.') { 413 if (period) 414 errx(1, "Invalid argument: %s", arg); 415 period = 1; 416 continue; 417 } 418 419 if (!isdigit((unsigned char)*cp)) 420 errx(1, "Invalid argument: %s", arg); 421 422 if (period) { 423 if (factor > 1) { 424 nanoseconds = nanoseconds * 10 + (*cp - '0'); 425 factor /= 10; 426 } 427 } else 428 seconds = (seconds * 10) + (*cp - '0'); 429 } 430 431 tvp->tv_sec = seconds; 432 if (factor > 1) 433 nanoseconds *= factor; 434 435 tvp->tv_nsec = nanoseconds; 436} 437 438static void 439usage(void) 440{ 441 442 (void)fprintf(stderr, 443 "usage: %s [-k | -m] [-d inactivity-timeout] [-e wakeup-delay]\n" 444 "\t\t[-f framebuffer] [-i input-device]\n" 445 " %s {-b | -u}\n", 446 getprogname(), 447 getprogname()); 448 exit(1); 449} 450