/* * pf.c - powerfail daemon * Copyright 1992-2000 Ronald Florence * based on original from rac@sherpa.UUCP (Roger Cornelius), 13 May 1990 * * maf+@osu.edu - fixed bug where pf would get stuck in the last state * before the shutdown sequence, then immediately shutdown on the next * change to battery. * * Toby Creek added SUSPEND option for * Sparc-4m & 4u architectures. With SUSPEND defined, pf will freeze * the system so it can be brought back with the power button on the * keyboard. This may not be what you want for an unattended machine. * * To install, you need a serial port with modem-control enabled. For * Sunos-4.1.x put 'off remote' in the status column of /etc/ttytab * for the serial port that will be connected to the UPS. For * Solaris, use pmadm -r -p zsmon -s tty[a|b] to remove any monitor * on the port. Make sure you have eeprom ttyX-ignore-cd=false on the * port. * * The cable from the UPS needs only two wires, the signal ground from * the UPS should be connected to the signal ground on the serial port * [pin 7 on a 25-pin port, pin 5 on a 9-pin port, or pin 4 on the * Sun IPC/IPX serial ports]; the powerfail signal from the UPS to * CD [pin 8 on a 25-pin port, pin 1 on a 9-pin port, or pin-7 on the * Sun IPC/IPX serial ports]. * * Define DEVICE, the serial port that will monitor the UPS, and * IGNORE, the time in seconds before a system shutdown is initiated, * to suit your setup. You can also specify the device, ignoretime, * or the shutdown script on the command line when pf is started. The * normal usage is to put pf somewhere in /etc/rc or in one of the * late init files in /etc/init.d * * compile: [g]cc -O [-DSUSPEND] -o pf pf.c */ #include #include #include #include #include #ifdef SUSPEND #define USAGE "Usage: pf [-d device] [-i ignoretime] \n" #else #define USAGE "Usage: pf [-d device] [-i ignoretime] [-s script]\n" #endif #define DEVICE "/dev/ttyb" #define OPEN_TM 3 #define IGNORE 600 jmp_buf jbuf; /* for Sunos-4.1.x */ /* char *script[] = {"/usr/etc/shutdown", "-h", "now", "Power Failure", 0}; */ /* for Solaris */ char *script[] = {"/usr/sbin/shutdown", "-y", "-i0", "-g30", "Power Failure", 0}; main(argc,argv) int argc; char **argv; { void sighand(), myfork(); int didopen(), opt, ignore; char *device; extern char *optarg; device = DEVICE; ignore = IGNORE; if (0 != getuid()) { fputs("pf: must be root\n", stderr); exit(1); } #ifdef SUSPEND while ((opt = getopt(argc, argv, "d:i:?")) != -1) #else while ((opt = getopt(argc, argv, "d:i:s:?")) != -1) #endif { switch (opt) { case 'd': device = optarg; break; case 'i': ignore = atoi(optarg); break; #ifndef SUSPEND case 's': *script = optarg; script[1] = '\0'; break; #endif default : fputs(USAGE, stderr); exit(1); } } if (1 != getppid()) /* if parent not init */ { myfork(); setpgrp(); myfork(); } close(0); /* close files and make sure we don't */ close(1); /* hold any mounted fs open */ close(2); chdir("/"); /* set up syslogging, direct to */ /* console in case logger fails, */ /* no wait for child process */ openlog("UPS", LOG_CONS|LOG_NOWAIT, LOG_DAEMON); /* Here we attempt to open the port attached to the UPS. The open * will block, waiting for CD, until we have a power failure. When * the open succeeds, we close the port, sleep the amount of time * specified in `ignore', then try the open again, this time setting * an alarm. If the alarm runs out before the second open succeeds, * we only had a temporary outage and the process starts over. If the * second open succeeds, we assume it's an extended failure so we exec * /etc/shutdown or the suspend daemon, after a warning and one final * try to open the line. */ restart: setjmp(jbuf); if (didopen(device)) /* power failure */ { syslog(LOG_CRIT, "Power line Event"); sleep(ignore); signal(SIGALRM, sighand); alarm(OPEN_TM); if (didopen(device)) { alarm(0); syslog(LOG_ALERT, "Power line failure - System going down in 60 seconds"); sleep(30); alarm(OPEN_TM); if (didopen(device)) { alarm(0); syslog(LOG_ALERT, "Power line failure - System going down now"); sleep(5); #ifdef SUSPEND system("/usr/openwin/bin/sys-suspend -fnx"); sleep(30); #else execvp(script[0], script); syslog(LOG_ALERT, "Unable to shutdown system"); #endif goto restart; } else { alarm(0); syslog(LOG_ALERT, "Shutdown cancelled - Power restored"); goto restart; } } else { syslog(LOG_CRIT, "Power restored - Countdown expired"); goto restart; } } } void myfork() { int stat; stat = fork(); if (stat < 0) syslog(LOG_ALERT,"pf: fork failed"); else if (stat > 0) /* parent */ exit(0); } /* * Attempt to open the device attached to the UPS * Should never succeed except during power failure */ int didopen(device) char *device; { int fd; fd = open(device, O_RDONLY); if (fd != -1) { close(fd); return(1); } syslog(LOG_ALERT, "pf: bogus open on UPS line"); exit(1); } void sighand(sig) int sig; { longjmp(jbuf,1); }