head 1.18; access; symbols; locks; strict; comment @ * @; 1.18 date 95.12.12.17.33.57; author alexis; state Exp; branches; next 1.17; 1.17 date 95.12.12.16.56.50; author alexis; state Exp; branches; next 1.16; 1.16 date 95.12.12.16.31.32; author alexis; state Exp; branches; next 1.15; 1.15 date 95.12.12.16.12.28; author alexis; state Exp; branches; next 1.14; 1.14 date 95.12.11.15.52.52; author alexis; state Exp; branches; next 1.13; 1.13 date 95.12.11.15.16.56; author alexis; state Exp; branches; next 1.12; 1.12 date 95.12.11.15.00.59; author alexis; state Exp; branches; next 1.11; 1.11 date 95.12.11.12.42.38; author alexis; state Exp; branches; next 1.10; 1.10 date 95.12.08.14.45.39; author alexis; state Exp; branches; next 1.9; 1.9 date 95.12.07.15.38.53; author alexis; state Exp; branches; next 1.8; 1.8 date 95.12.06.16.20.12; author alexis; state Exp; branches; next 1.7; 1.7 date 95.12.05.17.00.43; author alexis; state Exp; branches; next 1.6; 1.6 date 95.12.04.11.21.51; author alexis; state Exp; branches; next 1.5; 1.5 date 95.12.01.15.09.33; author alexis; state Exp; branches; next 1.4; 1.4 date 95.12.01.09.16.24; author alexis; state Exp; branches; next 1.3; 1.3 date 95.11.30.09.45.26; author alexis; state Exp; branches; next 1.2; 1.2 date 95.11.28.12.50.11; author alexis; state Exp; branches; next 1.1; 1.1 date 95.11.28.10.48.57; author alexis; state Exp; branches; next ; desc @UPS DAEMON WRITTEN IN C @ 1.18 log @Upped the debug level of a couple of messages set at level 2. @ text @/***************************************************************************** * * * Program: UPSD (UPS Daemon) * * * * Date: Mon Dec 11 14:17:31 WET 1995 * * * * Author: Alexis Huxley * * * * Description: Daemon program to monitor the state of DCD in a * * specified serial port. This program is compatible with * * any UPS that has a relay that closes contacts in the event * * of loss of mains power, or a UPS that displays relay-like * * behaviour. * * * * It is known to work with the following UPSs with the * * following cables: * * * * Victron Micro 106 * * 25 female 9 female * * 8 <----------------------------------------> 5 * * 20 <----------------------------------------> 9 * * * *****************************************************************************/ /* * Include files and reasons for inclusion */ #include /* principally for NULL */ #include /* for strrchr(), strtok() */ #include /* for ioctl() */ #include /* for O_RDWR in open() */ #include /* for time_t ret'd by time() */ #include /* for stat.h, wait.h */ #include /* for stat(), umask() */ #include /* for sleep(), getpid() */ #include /* for messaging funcs */ #include /* for wait() */ #include /* for signal() */ #include /* for syslog() */ #if linux #include /* for setenv() */ #endif #include /* for getpwnam() */ /* * Non-configurable symbols */ #define NULLCP ((char *) NULL) /* I pine for K&R C! */ #define NULLFP ((FILE *) NULL) /* I pine for K&R C! */ #define TRUE (0 == 0) /* boolean flags */ #define FALSE (0 != 0) /* boolean flags */ #define LOCK_NAME_UUCP 1 /* /usr/lib/uucp/LCK.device */ #define LOCK_NAME_TEST 2 /* /usr/lib/uucp/LCK.device */ #define LOCK_CONT_ASCII 1 /* ascii pid in lock file */ #define LOCK_CONT_BINARY 2 /* binary pid in lock file */ #define EXITACTION_NONE 0x00 /* no action on intr */ #define EXITACTION_EXIT 0x01 /* no action on intr */ #define EXITACTION_DTLK 0x02 /* no action on intr */ #define EXITACTION_DPLK 0x04 /* no action on intr */ #if linux #define WALL_CMD "/usr/bin/wall" /* Linux wall */ #define WRITE_CMD "/usr/bin/write" /* Linux write */ #define SHUTDOWN_CMD "/sbin/shutdown" /* Linux shutdown */ #else #define WALL_CMD "/bin/wall" /* Ultrix wall */ #define WRITE_CMD "/bin/write" /* Ultrix write */ #define SHUTDOWN_CMD "/etc/shutdown" /* Ultrix shutdown */ #endif #define UPSD_ENV_INTERVAL "UPSD_INTERVAL" /* name of env variable */ #define UPSD_ENV_STATE "UPSD_STATE" /* name of env variable */ /* * Configurable symbols */ #define DFLT_DEBUG_LEVEL 2 /* default debug level */ #define DFLT_SHUTDOWN_INTERVAL 240 /* default 4 minute warning */ #define DFLT_WARNING_INTERVAL 30 /* wall every 10 secs */ #define DFLT_CHECKING_INTERVAL 1 /* check every 5 seconds */ #define RESET_FLOW_CONTROL TRUE /* if badly behaved set this */ #define LOCK_NAME LOCK_NAME_UUCP /* choose from above */ #define LOCK_CONT LOCK_CONT_ASCII /* choose from above */ #define TEST_DUMMY_DCD FALSE /* dcd or file /tmp/powerfail */ #define TEST_DONT_WALL FALSE /* wall or write */ #define TEST_DONT_HALT FALSE /* shutdown or pretend */ #define TEST_NOT_ROOT FALSE /* is root running this */ /* * Forward references for internal functons */ void debug(int, char *, ...); /* for debugging */ void info(char *, ...); /* verbose messages */ void warning(char *, ...); /* warning messages */ void error(char *, ...); /* error messages */ void internal(char *, ...); /* internal errors */ void usage(void); /* tell user what to do */ void shutdown_now(char *); /* do an immediate shutdown */ int shutdown_warning(char *, int); /* warn of impending shutdown */ int shutdown_cancelled(char *); /* tell shutdown cancelled */ int shutdown_powerfail(void); /* call as power fails */ int lock_device(char *, int); /* lock a serial device */ int pidrunning(int); /* test if a pid is running */ void sighandler(int); /* catches INT and TERM */ int callprog(char *, char *, char *, int); /* call external prog or dflt */ /* * Forward references for standard library functions not mentioned in headers */ #if !linux /* linux declares all these */ extern void openlog(char *, int); /* not mentioned in syslog.h */ extern void syslog(int, char *, ...); /* not mentioned in syslog.h */ extern int ioctl(int, int, char *); /* not mentioned in ioctl.h */ extern void setenv(char *, char *, int); /* not mentioned in anything! */ #endif /* * Global variables */ unsigned int debug_level = DFLT_DEBUG_LEVEL; /* debug level */ char *progname; /* basename of this program */ /* * Static Variables */ /* RCS version control id */ static char *fileid = "$Id: upsd.c,v 1.17 1995/12/12 16:56:50 alexis Exp alexis $"; static char *version; /* version of this file */ static exitactions; /* what do do on signal */ static char lockbuf[64]; /* name of lock file */ static char tmplockbuf[64]; /* name of tmp lock file */ /**/ /****************************************************************************** * * Function: main() * * Parameters: argc - count of command line arguments * * Return value: argv - char vectort for all command line arguments * * Description: this is the entry point for the UPS daemon. It enters * a loop where is monitors changes in the state of the * DCD line and, interpreting these as changes in the * state of the mains power supply relayed by the UPS, * in the event of mains power loss it informs the users * at regular intervals of an impending shutdown and * finally shuts down the machine. * Of course it handles option processing, locking, signal * processing, logging, debugging, verbose and quiet * behaviour, etc. * ******************************************************************************/ int main( int argc, /* argument count */ char *argv[]) /* argument list */ { char *cp; /* handy char pointer */ time_t tm; /* handy time var */ char *devcp; /* pointer to device name */ int shutdown; /* flag meaning now shutdown */ int pwrstat; /* mains status */ int lastpwrstat; /* previous mains status */ #if !TEST_DUMMY_DCD #if linux unsigned int ioctl_flags; /* linux ioctl uses 32 bits */ #else unsigned int ioctl_flags; /* ultrix ioctl uses 32 bits */ #endif #endif int devfd; /* to open port */ time_t shutdown_time; /* time to shutdown at */ time_t warnmsg_time; /* time to warn users */ struct stat devstat; /* to check dev is serial */ char fileidbuf[256]; /* used to extract ver. no */ int shutdown_interval; /* from powerloss to shutdown */ unsigned int checking_interval; /* between power checks */ int warning_interval; /* between shutdown warnings */ char *warning_prog; /* to run at warning time */ char *shutdown_prog; /* to run at shutdown time */ char *cancelled_prog; /* to run at cancelled time */ pid_t lockingpid; /* active pid holding lock */ #if RESET_FLOW_CONTROL char killpwr_bit = TIOCM_RTS; /* may be superflous */ #endif /************************************************************************** * * Important things to do at the beginning * **************************************************************************/ /* get basename of this prog */ progname = ((cp=strrchr(argv[0], '/')) == NULLCP) ? argv[0] : cp+1; strcpy(fileidbuf, fileid); /* get version number */ version = (strtok(fileidbuf, " "),strtok(NULLCP, " "),strtok(NULLCP, " ")); /************************************************************************** * * Set default values for options * **************************************************************************/ shutdown_interval = DFLT_SHUTDOWN_INTERVAL; /* set default shutdown int */ checking_interval = DFLT_CHECKING_INTERVAL; /* set default check interval */ warning_interval = DFLT_WARNING_INTERVAL; /* set default warning int */ shutdown_prog = NULLCP; /* action handled internally */ warning_prog = NULLCP; /* action handled internally */ cancelled_prog = NULLCP; /* action handled internally */ /************************************************************************** * * Process options * **************************************************************************/ while (argc > 1 && argv[1][0] == '-') { /* deal with options */ if (!strcmp(argv[1], "-V")) { /* -V prints version info */ printf("%s version %s\n", progname, version); return(0); } else if (!strcmp(argv[1], "-v")) { /* verbose */ debug_level = 3; } else if (!strcmp(argv[1], "-is")) { /* shutdown interval */ if (argc == 2) usage(); if (sscanf(argv[2], "%d", &shutdown_interval) != 1) usage(); argc--; argv++; } else if (!strcmp(argv[1], "-iw")) { /* warning interval */ if (argc == 2) usage(); if (sscanf(argv[2], "%d", &warning_interval) != 1) usage(); argc--; argv++; } else if (!strcmp(argv[1], "-ic")) { /* checking interval */ if (argc == 2) usage(); if (sscanf(argv[2], "%d", &checking_interval) != 1) usage(); argc--; argv++; } else if (!strcmp(argv[1], "-ps")) { /* program to run at shutdown */ if (argc == 2) usage(); shutdown_prog = argv[2]; argc--; argv++; } else if (!strcmp(argv[1], "-pw")) { /* program to run at warning */ if (argc == 2) usage(); warning_prog = argv[2]; argc--; argv++; } else if (!strcmp(argv[1], "-pc")) { /* program to run at cancelled */ if (argc == 2) usage(); cancelled_prog = argv[2]; argc--; argv++; } else if (!strcmp(argv[1], "-d")) { /* debugging (very verbose) */ if (argc == 2) usage(); if (sscanf(argv[2], "%d", &debug_level) != 1) usage(); argc--; argv++; } else /* any other option is error */ usage(); argc--; /* ready for next option */ argv++; } /************************************************************************** * * Process required paramters * **************************************************************************/ if (argc != 2) usage(); /* required par is port name */ devcp = argv[1]; /* save it */ /************************************************************************** * * Sanity checks * **************************************************************************/ debug(50, "stat()ing %s ...", devcp); /* does port exist */ if (stat(devcp, &devstat)) error("couldn't stat %s", devcp); /* is it a char device */ if (!(devstat.st_mode & S_IFCHR)) error("%s is not a character device", devcp); if (shutdown_prog != NULLCP) { /* check shutdown prog */ if (stat(shutdown_prog, &devstat)) error("couldn't stat %s", shutdown_prog); if (!(devstat.st_mode & S_IFREG)) error("%s is not a regular file", shutdown_prog); if (!(devstat.st_mode & S_IEXEC)) error("%s is not an executable program", shutdown_prog); } if (warning_prog != NULLCP) { /* check warning prog */ if (stat(warning_prog, &devstat)) error("couldn't stat %s", warning_prog); if (!(devstat.st_mode & S_IFREG)) error("%s is not a regular file", warning_prog); if (!(devstat.st_mode & S_IEXEC)) error("%s is not an executable program", warning_prog); } if (cancelled_prog != NULLCP) { /* check cancelled prog */ if (stat(cancelled_prog, &devstat)) error("couldn't stat %s", cancelled_prog); if (!(devstat.st_mode & S_IFREG)) error("%s is not a regular file", cancelled_prog); if (!(devstat.st_mode & S_IEXEC)) error("%s is not an executable program", cancelled_prog); } /************************************************************************** * * Initialise interrupt handling * **************************************************************************/ exitactions = EXITACTION_EXIT; signal(SIGTERM, sighandler); signal(SIGINT, sighandler); /************************************************************************** * * Initialise logging * **************************************************************************/ #if linux openlog(progname, LOG_PID, LOG_DAEMON); /* all syslog calls use pid */ #else openlog(progname, LOG_PID); /* all syslog calls use pid */ #endif /* note that we have started */ syslog(LOG_NOTICE, "%s version %s started on %s", progname, version, devcp); /************************************************************************** * * Lock port and open * **************************************************************************/ if ((lockingpid=lock_device(devcp, 0)) > 0) /* lock the device */ error("%s is in use by process %d", devcp, lockingpid); else if (lockingpid < 0) /* competing locks */ internal("lock collision"); debug(50, "open()ing %s ...", devcp); /* open for ioctl()ing */ if ((devfd = open(devcp, O_RDWR | O_NDELAY)) < 0) error("can't open %s", devcp); #if RESET_FLOW_CONTROL /* clear flow control */ ioctl(devfd, TIOCMBIC, (char *) &killpwr_bit); #endif /************************************************************************** * * Loop monitoring DCD * **************************************************************************/ info("Monitoring %s ...", devcp); debug(50, "entering loop ..."); /* loop looking at DCD */ for (shutdown=FALSE,lastpwrstat=TRUE;!shutdown;lastpwrstat=pwrstat) { sleep(checking_interval); /* pause between loops */ #if TEST_DUMMY_DCD pwrstat = !(!stat("/tmp/powerfail", &devstat)); #else ioctl(devfd, TIOCMGET, (char *) &ioctl_flags); /* get DCD and more! */ pwrstat = !(ioctl_flags & TIOCM_CD); /* filter out all except DCD */ #endif debug(20, "powerstat: %d, lastpwrstat: %d", pwrstat, lastpwrstat); /* * If the power is on and it was on before then don't do anything. */ if (pwrstat == TRUE && pwrstat == lastpwrstat) continue; /* * If the power is on, but it wasn't before then cancel the * impending shutdown. */ if (pwrstat == TRUE) { shutdown_cancelled(cancelled_prog); continue; } /* * If the power is off but it was on before, then reset shutdown * counters and ... */ if (pwrstat != lastpwrstat) { shutdown_time = (tm=time((time_t *) NULL)) + shutdown_interval; warnmsg_time = tm; shutdown_powerfail(); /* logs only, warning below */ } /* * ... if the power is off, regardless of what it was like before, * and if shutdown time has not been reached yet, then give the * warning message if it's time for the next one and continue with * with the next check. */ if ((tm=time((time_t *) NULL)) < shutdown_time) { debug(100, "time is: %ld", tm); debug(100, "warning time is: %ld", warnmsg_time); if (tm >= warnmsg_time) { /* don't warn each time */ shutdown_warning(warning_prog, (int) (shutdown_time - tm)); /* schedule next warning */ warnmsg_time = tm + warning_interval; debug(100, "new wrn time is: %ld", warnmsg_time); } continue; } /* * If we get to here then the power is off and the time interval * between a power down and the actual shutdown has elapsed. So we * just set a flag to get out of this loop. (The first line after * the loop will shutdown the machine.) */ shutdown = TRUE; /* to break out of loop */ } /* * We're done monitoring, we can unlock the device (not that there's * much point, nobody else wants info from the UPS) and shutdown. */ unlink(lockbuf); /* done using port */ exitactions &= ~EXITACTION_DPLK; debug(20, "exitactions at exit is %d", exitactions); shutdown_now(shutdown_prog); /* "the time has come ..." */ return(0); /* not reached */ } /****************************************************************************** * * Function: usage() * * Parameters: none * * Return value: doesn't return * * Description: tells the user how to use this command * ******************************************************************************/ void usage() { fprintf(stderr, "Usage: %s -V\n", progname); fprintf(stderr, " %s [ -v | -d level ] [ -i[wcs] secs ... ] [ -p[wcs] prog ... ] port\n", progname); fprintf(stderr, "\n"); fprintf(stderr, "See upsd(8) for more details\n"); fprintf(stderr, "\n"); exit(1); } /****************************************************************************** * * Function: info() * * Parameters: as per printf() * * Return value: none * * Description: this function formats and outputs (if debug_level is * set to a sufficiently high level) a nicely formatted * informational message. * ******************************************************************************/ void info( char *fmt, /* like printf() */ ...) /* parameters to format str */ { va_list argp; /* to grab var list */ if (3 > debug_level) return; /* verbose only if 3 or more */ printf("%s: INFO: ", progname); va_start(argp, fmt); vprintf(fmt, argp); va_end(argp); printf("\n"); } /****************************************************************************** * * Function: warning() * * Parameters: as per printf() * * Return value: none * * Description: this function formats and outputs (if debug_level is * set to a sufficiently high level) a nicely formatted * warning message. * ******************************************************************************/ void warning( char *fmt, /* like printf() */ ...) /* parameters to format str */ { va_list argp; /* to grab var list */ if (2 > debug_level) return; /* warnings only if 2 or more */ printf("%s: WARNING: ", progname); va_start(argp, fmt); vprintf(fmt, argp); va_end(argp); printf("\n"); } /****************************************************************************** * * Function: debug() * * Parameters: level - debug level at which this message should be sent * remaing parameters as per printf() * * Return value: none * * Description: this function formats and outputs (if debug_level is * set to a sufficiently high level) a nicely formatted * debug message. * ******************************************************************************/ void debug( int level, /* level of this message */ char *fmt, /* like printf() */ ...) { va_list argp; /* to grab var list */ if (level > debug_level) return; /* debug only suff. level */ fprintf(stderr, "%s: DEBUG[%d]: ", progname, level); va_start(argp, fmt); vfprintf(stderr, fmt, argp); va_end(argp); fprintf(stderr, "\n"); } /****************************************************************************** * * Function: error() * * Parameters: as per printf() * * Return value: none * * Description: this function formats and outputs (if debug_level is * set to a sufficiently high level) a nicely formatted * error message. * ******************************************************************************/ void error( char *fmt, /* like printf() */ ...) { va_list argp; /* to grab var list */ char msgbuf[256]; if (2 > debug_level) return; /* errors only if 2 or more */ va_start(argp, fmt); vsprintf(msgbuf, fmt, argp); va_end(argp); printf("%s: ERROR: %s\n", progname, msgbuf); syslog(LOG_NOTICE, msgbuf); /* do things to do at exit */ if (exitactions && EXITACTION_DTLK) unlink(tmplockbuf); if (exitactions && EXITACTION_DPLK) unlink(lockbuf); exit(2); } /****************************************************************************** * * Function: internal() * * Parameters: as per printf() * * Return value: none * * Description: this function formats an internal error nicely! * ******************************************************************************/ void internal( char *fmt, /* like printf() */ ...) { va_list argp; /* to grab var list */ char msgbuf[256]; va_start(argp, fmt); vsprintf(msgbuf, fmt, argp); va_end(argp); printf("%s: INTERNAL ERROR: %s\n", progname, msgbuf); syslog(LOG_NOTICE, msgbuf); /* don't tidy up */ exit(3); } /****************************************************************************** * * Function: shutdown_powerfail() * * Parameters: none * * Return value: 0 * * Description: as the power fails there is no action to be taken other * then logging the situation. Warning the users takes * place at regular intervals after the power has failed, * but there is no specific action associated with the * failure itself. * ******************************************************************************/ int shutdown_powerfail() { syslog(LOG_NOTICE, "power has failed"); /* just log it, no action */ return(0); } /****************************************************************************** * * Function: shutdown_warning() * * Parameters: warning_prog - name of program to execute or NULL * * Return value: 0 for success * non-zero of unsuccessful * * Description: if the user has specified a command on the command line * to execute when a warning should be issued then this * is executed at this time, otherwise a message is sent * to all users alerting them that a shutdown will follow * shortly. * ******************************************************************************/ int shutdown_warning( char *warning_prog, int interval) { info("SHUTDOWN IN %d SECONDS", interval); return(callprog(warning_prog, "\n\n*** POWER FAILURE! ***\n*** SHUTDOWN IN %3d SECONDS! ***\n\n", "impending", interval)); } /****************************************************************************** * * Function: shutdown_cancelled() * * Parameters: cancelled_prog - name of program to execute or NULL * * Return value: 0 for success * non-zero of unsuccessful * * Description: if the user has specified a command on the command line * to execute when a shutdown has been cancelled then this * is executed at this time, otherwise a message is sent * to all users alerting them that a scheduled shutdown has * been cancelled. * ******************************************************************************/ int shutdown_cancelled( char *cancelled_prog) { info("SHUTDOWN CANCELLED"); syslog(LOG_NOTICE, "power has returned"); return(callprog(cancelled_prog, "\n\n*** POWER RESTORED! ***\n*** SHUTDOWN CANCELLED! ***\n\n", "cancelled", 0)); } /****************************************************************************** * * Function: shutdown_now() * * Parameters: shutdown_prog - path of program to execute or NULL * * Return value: doesn't return * * Description: this program is responsible for actually performing an * immediate shutdown. If prog is specified then it is called, * otherwise "shutdown -h now" is called. * ******************************************************************************/ void shutdown_now( char *shutdown_prog) { info("SHUTTING DOWN NOW!"); syslog(LOG_NOTICE, "shutting down now"); if (shutdown_prog == NULL) { /* if not spec'd, do shutdown */ #if TEST_DONT_HALT error("shutdown not enabled, exiting"); #else execl(SHUTDOWN_CMD, SHUTDOWN_CMD, "-h", "now", NULLCP); #endif } else { /* exec user def'd action */ setenv("UPSD_INTERVAL", "0", 1); setenv("UPSD_STATE", "immediate", 1); execl(shutdown_prog, shutdown_prog, NULLCP); } } /****************************************************************************** * * Function: callprog() * * Parameters: prog - path of program to execute or NULL * fmtstr - message written to all users of prog was NULL * state - word written to environment if prog not NULL * interval - number written to enviroment if prog not NULL * * Return value: 0 for success * exits via error() otherwise * * Description: If prog is NULL then this function sends the specified * message to all users. Otherwise it calls prog, first * writing the passed word and number into the enviroment * available to prog. * ******************************************************************************/ int callprog( char *prog, /* user def'd program or NULL */ char *fmtstr, /* if no user def'd, say this */ char *state, /* to user def'd prog in env */ int interval) /* to user def'd prog in env */ { int pipefds[2]; /* to talk to wall */ char msgbuf[256]; /* handy char buffer */ int pid; /* handy pid */ char numbuf[16]; /* handy chat buffer */ if (prog == NULL && pipe(pipefds) < 0) /* no user def'd & can't pipe */ error("Can't open pipe"); if ((pid=fork()) < 0) /* fork to wall */ error("Can't fork"); /************************************************************************** * * Child * **************************************************************************/ /* * Child process takes input from parent and sends it to users or simply * calls a user defined program if one was supplied on the command line. */ else if (pid == 0) { /* fork ok, this is child */ debug(5, "child running ..."); if (prog == NULL) { /* no user def'd program */ debug(5, "child doesn't need to write pipe"); close(pipefds[1]); /* no need to write to parent */ debug(5, "child setting stdin to read from pipe"); close(0); /* stdin comes from parent */ dup(pipefds[0]); close(pipefds[0]); #if TEST_DONT_WALL /* wall or write, for testing */ debug(5, "child execing write ..."); execl(WRITE_CMD, WRITE_CMD, "alexis", NULLCP); error("can't exec %s", WRITE_CMD); #else debug(5, "child execing wall ..."); execl(WALL_CMD, WALL_CMD, NULLCP); error("can't exec %s", WALL_CMD); #endif } else { /* user def'd program to call */ sprintf(numbuf, "%d", interval); /* set up env for user def'd */ setenv(UPSD_ENV_INTERVAL, numbuf, 1); setenv(UPSD_ENV_STATE, state, 1); execl(prog, prog, NULLCP); /* exec user defined program */ error("can't exec %s", prog); } } /************************************************************************** * * Parent * **************************************************************************/ /* * Meanwhile the parent either feeds a message to the child to be sent * to users or does nothing while the child calls a user defined program. * In either case it waits until the child has finished. */ if (prog == NULL) { /* no user spec'd prog to run */ debug(5, "parent doesn't need to read from pipe"); close(pipefds[0]); /* no need to read from child */ debug(5, "parent writing message to pipe ..."); sprintf(msgbuf, fmtstr, interval); /* put interval into message */ write(pipefds[1], msgbuf, (int) strlen(msgbuf)); /* send to child */ debug(5, "parent closing write side of pipe ..."); close(pipefds[1]); /* nothing more to send */ } debug(5, "parent waiting for child to exit"); while (wait((int *) 0) != pid) /* wait for child to exit */ ; debug(5, "parent done"); return(0); } /****************************************************************************** * * Function: lock_device() * * Parameters: devcp - name of device to lock * attempt - number of attempt, this should be 0 * * Return value: 0 if successful * pid of process owning lock if unsuccessful * * Description: this function uses a standard locking mechanism to lock * the specified device. It honours existing locks and * can detect stale lock files. * ******************************************************************************/ int lock_device( char *devcp, /* name of device to lock */ int attempt) /* this is attempt number */ { char *basecp; /* dev name base e.g. tty00 */ char *cp; /* handy char pointer */ pid_t mypid; /* pid of this process */ pid_t lockingpid; /* pid of lock owning process */ FILE *tmplockfp; /* name of temp lock file */ FILE *lockfp; /* name of lock file */ #if !TEST_NOT_ROOT struct passwd *pwentp; /* to get uucp's uid */ #endif /* get basename of device */ basecp = ((cp=strrchr(devcp, '/')) == NULLCP) ? devcp : cp+1; mypid = getpid(); /* get pid of this process */ umask(022); /* readable lock files */ /************************************************************************** * * Set lockfile and temporary lockfile names * **************************************************************************/ #if (LOCK_NAME == LOCK_NAME_UUCP) sprintf(lockbuf, "/var/spool/uucp/LCK..%s", basecp); sprintf(tmplockbuf, "/var/spool/uucp/LCK..%s.%d", basecp, mypid); #elif (LOCK_NAME == LOCK_NAME_TEST) sprintf(lockbuf, "/tmp/LCK..%s", basecp); sprintf(tmplockbuf, "/tmp/LCK..%s.%d", basecp, mypid); #else internal("unsupported lock style %d", LOCK_STYLE); #endif debug(10, "lockfile is %s, tmplockfile is %s", lockbuf, tmplockbuf); /************************************************************************** * * Create a temporary lock * **************************************************************************/ if (attempt == 0) { /* 1st time create tmp lock */ exitactions |= EXITACTION_DTLK; if ((tmplockfp=fopen(tmplockbuf, "w")) == NULLFP) error("problem opening temporary lock %s", tmplockbuf); #if (LOCK_CONT == LOCK_CONT_ASCII) fprintf(tmplockfp, "%d", mypid); #else internal("unsupported lock contents %d", LOCK_CONT); #endif fclose(tmplockfp); #if !TEST_NOT_ROOT if ((pwentp=getpwnam("uucp")) == (struct passwd *) NULL) error("can't find user 'uucp'"); if (chown(tmplockbuf, pwentp->pw_uid, (gid_t) -1) != 0) error("can't change ownership to 'uucp'"); #endif } else if (attempt == 2) { /* if too many times ... */ unlink(tmplockbuf); /* remove tmp lock */ exitactions &= ~EXITACTION_DTLK; /* tmplock won't need rm'ing */ return(-1); /* give up */ } /************************************************************************** * * Create a permanent lock * **************************************************************************/ debug(50, "linking %s to %s ...", lockbuf, tmplockbuf); if (link(tmplockbuf, lockbuf) == 0) { /* slide lock into place */ exitactions |= EXITACTION_DPLK; /* lock will need removing */ unlink(tmplockbuf); /* remove temporary lock */ exitactions &= ~EXITACTION_DTLK; /* templock won't need rm'ing */ debug(50, "successful, %s should exist", lockbuf); return(0); /* ok! */ } debug(50, "lock file already exists"); /************************************************************************** * * Examine existing lock * **************************************************************************/ if ((lockfp=fopen(lockbuf, "r")) == NULLFP) /* open existing lock file */ error("problem opening existing lock file"); #if (LOCK_CONT == LOCK_CONT_ASCII) fscanf(lockfp, "%d", &lockingpid); /* get owning pid */ #else internal("unsupported lock contents %d", LOCK_CONT); #endif debug(10, "lockfile %s contains pid %d", lockbuf, lockingpid); if (pidrunning(lockingpid) != 0) { /* if owning pid is running */ unlink(tmplockbuf); /* remove temp lock */ exitactions &= ~EXITACTION_DTLK; /* templock won't need rm'ing */ return(lockingpid); /* tell call who locked it */ } /************************************************************************** * * Remove stale lock and try again * **************************************************************************/ debug(10, "stale lock file"); info("stale lock file %s removed", lockbuf); /* remove stale lock */ if (unlink(lockbuf) != 0) error("can't unlink %s", lockbuf); exitactions &= ~EXITACTION_DPLK; /* lock won't need rm'ing */ debug(10, "trying again"); return(lock_device(devcp, attempt+1)); /* try again */ } /****************************************************************************** * * Function: pidrunning() * * Parameters: runningpid - number of process to test if running * * Return value: 0 if not running * runningpid if runningpid is running * * Description: this function forks a process. The child calls ps and * sends all output to the parent. The parent scans this * data looking for the sequence NEWLINE,SPACE,PID,SPACE * which is sufficient to ascertain if the process is running. * ******************************************************************************/ int pidrunning( int runningpid) /* pid to test if running */ { int pipefds[2]; /* to read what ps says */ int thiscnt; /* bytes read from ps */ int totalcnt; /* total bytes read from ps */ char pslinebuf[16384]; /* total ps output */ char expectbuf[16]; /* string we're looking for */ int pid; /* handy pid (used with fork) */ int i; /* handy loop counter */ if (pipe(pipefds) < 0) /* make a pipe */ error("Can't open pipe"); if ((pid=fork()) < 0) /* fork for ps */ error("Can't fork"); /************************************************************************** * * Child process execs ps * **************************************************************************/ else if (pid == 0) { debug(5, "child running ..."); /* close the read side of the pipe */ close(pipefds[0]); /* dont need stdin from pipe */ close(1); /* make stdout to pipe */ dup(pipefds[1]); close(pipefds[1]); /* exec ps */ execl("/bin/ps", "/bin/ps", "-ax", NULLCP); error("can't exec /bin/ps"); } /************************************************************************** * * Parent hunts for specified process in ps output * **************************************************************************/ close(pipefds[1]); /* dont need stdout to pipe */ /* need multiple reads, why ? */ for (totalcnt=0; (thiscnt=read(pipefds[0], pslinebuf+totalcnt, (int) (sizeof(pslinebuf)-totalcnt))) != 0; totalcnt+=thiscnt) ; if (totalcnt == sizeof(pslinebuf)) /* some still to read ? */ internal("ps output exceeded %d bytes", sizeof(pslinebuf)); debug(100, "total read: %d", totalcnt); pslinebuf[totalcnt] = '\0'; /* so we can use strcmp() */ while (wait((int *) 0) != pid) /* wait for child to exit */ ; close(pipefds[0]); /* close from pipe */ debug(500, "ps exited and returned: [%s]", pslinebuf); sprintf(expectbuf, "\n%5d ", runningpid); /* pattern we're hunting for */ debug(100, "searching ps output for [%s]", expectbuf); for (i=0; i /* for stat() */ d44 1 d88 1 d132 1 a132 1 static char *fileid = "$Id: upsd.c,v 1.12 1995/12/11 15:00:59 alexis Exp alexis $"; d855 3 d862 1 d897 6 @ 1.12 log @Millions of comments added Parameterisation of external commands Scripts are now "programs" @ text @d62 1 a62 1 #define WALL_CMD "/bin/wall" is this right ? d64 1 a64 1 #define SHUTDOWN_CMD "/etc/shutdown" is this right ? a102 1 int unlock_device(char *); /* unlock a serial device */ d112 4 a115 4 void openlog(char *, int); /* not mentioned in syslog.h */ void syslog(int, char *, ...); /* not mentioned in syslog.h */ int ioctl(int, int, char *); /* not mentioned in ioctl.h */ void setenv(char *, char *, int); /* not mentioned in anything! */ d130 1 a130 1 static char *fileid = "$Id: upsd.c,v 1.11 1995/12/11 12:42:38 alexis Exp $"; d182 1 a182 1 int checking_interval; /* between power checks */ a224 4 printf("\n"); printf("checking interval: %d secs\n", DFLT_CHECKING_INTERVAL); printf("warning interval: %d secs\n", DFLT_WARNING_INTERVAL); printf("shutdown interval: %d secs\n", DFLT_SHUTDOWN_INTERVAL); d793 1 a793 1 setenv(UPSD_ENV_STATE", state, 1); d816 1 a816 1 write(pipefds[1], msgbuf, strlen(msgbuf)); /* send to child */ d1007 1 a1007 1 for (totalcnt=0; (thiscnt=read(pipefds[0], pslinebuf+totalcnt, sizeof(pslinebuf)-totalcnt)) != 0; totalcnt+=thiscnt) @ 1.11 log @Partial support for walling Fixed bug whereby lastpwrstat was always 1 Tidied returen codes and lint errors Exit actions mechanism implemented using bits in a long @ text @d3 1 a3 1 * Program: * d5 1 a5 1 * Author: * d7 15 a21 1 * Description: * d64 1 d66 1 a66 1 #define WALL_CMD "/bin/wall" /* Ultrix write */ d68 1 d70 2 d86 1 a86 1 #define TEST_DONT_SHUTDOWN TRUE /* shutdown or pretent */ d98 1 a98 1 int shutdown_now(char *); /* do an immediate shutdown */ d106 1 a106 1 int callscript(char *, char *, char *, int); /* call external prog or dflt */ d112 1 a112 1 #if !linux d131 1 a131 1 static char *fileid = "$Id: upsd.c,v 1.10 1995/12/08 14:45:39 alexis Exp $"; d141 1 a141 1 * Function: d143 1 a143 1 * Parameters: d145 1 a145 1 * Return value: d147 10 a156 1 * Description: d185 3 a187 3 char *warning_script; /* to run at warning time */ char *shutdown_script; /* to run at shutdown time */ char *cancelled_script; /* to run at cancelled time */ d213 3 a215 3 shutdown_script = NULLCP; /* action handled internally */ warning_script = NULLCP; /* action handled internally */ cancelled_script = NULLCP; /* action handled internally */ d226 5 a230 1 return(0); /* for LINT */ d255 1 a255 1 shutdown_script = argv[2]; d261 1 a261 1 warning_script = argv[2]; d267 1 a267 1 cancelled_script = argv[2]; d303 3 a305 3 if (shutdown_script != NULLCP) { /* check shutdown script */ if (stat(shutdown_script, &devstat)) error("couldn't stat %s", shutdown_script); d307 1 a307 1 error("%s is not a regular file", shutdown_script); d309 1 a309 1 error("%s is not an executable program", shutdown_script); d311 3 a313 3 if (warning_script != NULLCP) { /* check warning script */ if (stat(warning_script, &devstat)) error("couldn't stat %s", warning_script); d315 1 a315 1 error("%s is not a regular file", warning_script); d317 1 a317 1 error("%s is not an executable program", warning_script); d319 3 a321 3 if (cancelled_script != NULLCP) { /* check cancelled script */ if (stat(cancelled_script, &devstat)) error("couldn't stat %s", cancelled_script); d323 1 a323 1 error("%s is not a regular file", cancelled_script); d325 1 a325 1 error("%s is not an executable program", cancelled_script); a334 2 signal(0, sighandler); signal(SIGHUP, sighandler); d345 1 a345 1 openlog(progname, LOG_PID, LOG_DAEMON); d347 1 a347 1 openlog(progname, LOG_PID); d349 1 d360 1 a360 1 else if (lockingpid < 0) d400 1 a400 1 shutdown_cancelled(cancelled_script); d426 3 a428 2 if (tm >= warnmsg_time) { shutdown_warning(warning_script, (int) (shutdown_time - tm)); d442 1 a442 1 shutdown = TRUE; d444 7 a450 1 unlink(lockbuf); /* no longer monitoring port so unlock */ d453 3 d457 11 a467 2 return(shutdown_now(shutdown_script)); /* "the time has come ..." */ } d472 1 a472 1 fprintf(stderr, " %s [ -v | -d level ] [ -ic secs ] [ -is secs ] [ -iw secs ] port\n", progname); d479 14 d507 14 d535 15 d565 14 d592 1 a592 1 d598 12 d626 16 d644 1 a644 1 syslog(LOG_NOTICE, "power has failed"); d648 17 d666 1 a666 1 char *warning_script, d670 1 a670 1 return(callscript(warning_script, "\n\n*** POWER FAILURE! ***\n*** SHUTDOWN IN %3d SECONDS! ***\n\n", "warning", interval)); d673 17 d691 1 a691 1 char *cancelled_script) d695 1 a695 1 return(callscript(cancelled_script, "\n\n*** POWER RESTORED! ***\n*** SHUTDOWN CANCELLED! ***\n\n", "cancelled", 0)); d698 16 a713 2 int shutdown_now( char *shutdown_script) d717 3 a719 4 return(callscript(shutdown_script, "\n\n*** POWER FAILURE! ***\n*** SHUTTING DOWN NOW! ***\n\n", "immediate", 0)); #if TEST_DONT_SHUTDOWN error("shutdown not enabled"); d721 2 a722 1 exec shutdown here!!! d724 5 a728 1 return(0); d731 24 a754 5 int callscript( char *script, char *fmtstr, char *state, int interval) d756 4 a759 4 int pipefds[2]; char msgbuf[256]; int pid; char numbuf[16]; d761 1 a761 1 if (script == NULL && pipe(pipefds) < 0) d763 1 a763 1 if ((pid=fork()) < 0) d768 1 a768 1 * Child process execs ... d772 6 a777 1 else if (pid == 0) { d779 1 a779 3 /* close the read side of the pipe */ if (script == NULL) { d781 1 a781 1 close(pipefds[1]); /* dont need stdout to pipe */ d783 1 a783 1 close(0); /* make stdin from pipe */ d786 1 a786 1 #if TEST_DONT_WALL d795 6 a800 6 } else { sprintf(numbuf, "%d", interval); setenv("UPSD_INTERVAL", numbuf, 1); setenv("UPSD_STATE", state, 1); execl(script, script, NULLCP); error("can't exec %s", script); d806 1 a806 1 * Parent ... d810 7 a816 1 if (script == NULL) { d818 1 a818 1 close(pipefds[0]); d820 2 a821 2 sprintf(msgbuf, fmtstr, interval); write(pipefds[1], msgbuf, strlen(msgbuf)); d823 1 a823 1 close(pipefds[1]); d832 16 d849 2 a850 2 char *devcp, int attempt) d852 6 a857 6 char *basecp; char *cp; pid_t mypid; pid_t lockingpid; FILE *tmplockfp; FILE *lockfp; d859 1 d861 1 a861 1 mypid = getpid(); d865 1 a865 1 * Create a temporary lock d880 7 a886 1 if (attempt == 0) { d896 5 a900 4 } else if (attempt == 2) { unlink(tmplockbuf); exitactions &= ~EXITACTION_DTLK; return(-1); d910 4 a913 4 if (link(tmplockbuf, lockbuf) == 0) { exitactions |= EXITACTION_DPLK; unlink(tmplockbuf); exitactions &= ~EXITACTION_DTLK; d915 1 a915 1 return(0); d925 1 a925 1 if ((lockfp=fopen(lockbuf, "r")) == NULLFP) d928 1 a928 1 fscanf(lockfp, "%d", &lockingpid); d933 4 a936 4 if (pidrunning(lockingpid) != 0) { unlink(tmplockbuf); exitactions &= ~EXITACTION_DTLK; return(lockingpid); d947 1 d949 3 a951 3 exitactions &= ~EXITACTION_DPLK; debug(10, "trying again"); return(lock_device(devcp, attempt+1)); d954 16 d971 1 a971 1 int runningpid) d973 7 a979 6 int pipefds[2]; int thiscnt, totalcnt; char pslinebuf[16384]; char expectbuf[16]; int pid; int i; d981 1 a981 1 if (pipe(pipefds) < 0) d983 1 a983 1 if ((pid=fork()) < 0) d999 1 d1033 15 d1049 1 a1049 1 int signo) d1052 1 @ 1.10 log @Rearrangement of symbols killpwr_bit used to clear flow control type change from int to char Sources compile cleanly on Linux with addition of '#if linux' @ text @d27 3 d41 13 a53 6 #define LOCK_CONTENTS_ASCII 1 /* ascii pid in lock file */ #define LOCK_CONTENTS_BINARY 2 /* binary pid in lock file */ #define SIGHANDLER_NONE 0x00 /* no action on intr */ #define SIGHANDLER_EXIT 0x01 /* no action on intr */ #define SIGHANDLER_DTLK 0x02 /* no action on intr */ #define SIGHANDLER_DPLK 0x04 /* no action on intr */ d63 1 a63 1 #define RESET_FLOW_CONTROL TRUE /* if badly behaved set this */ d65 4 a68 2 #define LOCK_CONTENTS LOCK_CONTENTS_ASCII /* choose from above */ #define DUMMY_DCD_TEST FALSE /* dcd or file /tmp/powerfail */ d88 1 a88 1 int callscript(char *, char *, int); /* call external prog or dflt */ d98 1 d113 1 a113 1 static char *fileid = "$Id: upsd.c,v 1.9 1995/12/07 15:38:53 alexis Exp alexis $"; d115 3 a117 3 static sighandlerflags; /* what do do on signal */ static char lockbuf[64]; static char tmplockbuf[64]; d143 6 a148 2 #if !DUMMY_DCD_TEST char ioctl_flags; /* ioctl() sets this */ d174 1 a174 2 strcpy(fileidbuf, fileid); /* can't get ver no insitu */ d199 1 a199 1 exit(0); d303 3 a305 1 sighandlerflags = SIGHANDLER_EXIT; d331 1 a331 1 error("lock collision"); d335 2 a336 2 #if RESET_FLOW_CONTROL ioctl(devfd, TIOCMBIC, &killpwr_bit); /* clear flow control */ d349 1 a349 1 #if DUMMY_DCD_TEST d352 1 a352 1 ioctl(devfd, TIOCMGET, &ioctl_flags); /* get DCD and more! */ d355 1 a355 1 debug(20, "powerstat: %d", pwrstat); d387 2 a388 2 * If the power is off, regardless of what it was like before, and * if shutdown time has not been reached yet, then give the d413 3 d417 1 a417 2 shutdown_now(shutdown_script); /* "the time has come ..." */ exit(999); /* NOT REACHED !!! */ d486 3 d504 1 d519 1 a519 1 return(callscript(warning_script, "\n\n*** POWER FAILURE! ***\n*** SHUTDOWN IN %3d SECONDS! ***\n\n", interval)); d527 1 a527 2 return(callscript(cancelled_script, "\n\n*** POWER RESTORED! ***\n*** SHUTDOWN CANCELLED! ***\n\n", 0)); return(0); d535 1 a535 1 return(callscript(shutdown_script, "\n\n*** POWER FAILURE! ***\n*** SHUTTING DOWN NOW! ***\n\n", 0)); d537 6 a542 2 /* code to do shutdown goes here */ exit(4); d548 1 d578 1 d580 7 a586 5 execl("/bin/write", "/bin/write", "alexis", NULLCP); error("can't exec /bin/write"); } else if (interval == 0) { execl(script, script, NULLCP); error("can't exec %s", script); d589 3 a591 1 execl(script, script, numbuf, NULLCP); d650 1 a650 1 sighandlerflags |= SIGHANDLER_DTLK; d653 1 a653 1 #if (LOCK_CONTENTS == LOCK_CONTENTS_ASCII) d656 1 a656 1 internal("unsupported lock contents %d", LOCK_CONTENTS); d661 1 a661 1 sighandlerflags &= ~SIGHANDLER_DTLK; d673 1 a673 1 sighandlerflags |= SIGHANDLER_DPLK; d675 1 a675 1 sighandlerflags &= ~SIGHANDLER_DTLK; d689 1 a689 1 #if (LOCK_CONTENTS == LOCK_CONTENTS_ASCII) d692 1 a692 1 internal("unsupported lock contents %d", LOCK_CONTENTS); d697 1 a697 1 sighandlerflags &= ~SIGHANDLER_DTLK; d710 1 a710 1 sighandlerflags &= ~SIGHANDLER_DPLK; d779 4 a782 4 debug(4, "sighandler: signo: %d, sighandlerflags: %d", signo, sighandlerflags); if (sighandlerflags && SIGHANDLER_DTLK) unlink(tmplockbuf); if (sighandlerflags && SIGHANDLER_DPLK) unlink(lockbuf); if (sighandlerflags && SIGHANDLER_EXIT) { @ 1.9 log @Added support for dummy DCD testing -s[wsc] changed to -p[wsc] since they are external programs not scripts Abstracted support for external warnings -functions for handling warnings, cancellations and shutdowns are simpler. @ text @d29 1 a29 1 * Various symbols grouped here fopr convenience a31 4 #define DFLT_DEBUG_LEVEL 2 /* default debug level */ #define DFLT_SHUTDOWN_INTERVAL 240 /* default 4 minute warning */ #define DFLT_WARNING_INTERVAL 10 /* wall every 10 secs */ #define DFLT_CHECKING_INTERVAL 1 /* check every 5 seconds */ a35 1 #define RESET_FLOW_CONTROL FALSE /* if badly behaved set this */ a37 1 #define LOCK_NAME LOCK_NAME_TEST /* choose from above */ a39 1 #define LOCK_CONTENTS LOCK_CONTENTS_ASCII /* choose from above */ d44 13 a56 1 #define DUMMY_DCD_TEST TRUE /* dcd or file /tmp/powerfail */ d71 1 a71 1 int shutdown_powerfail(void); d75 2 a76 2 void sighandler(int); int callscript(char *, char *, int); d79 1 a79 1 * Forward references for external functons d82 1 d86 1 d100 1 a100 1 static char *fileid = "$Id: upsd.c,v 1.8 1995/12/06 16:20:12 alexis Exp alexis $"; d146 1 a146 1 int killpwr_bit = TIOCM_RTS; /* may be superflous */ d297 3 d301 1 @ 1.8 log @Signal handling Logging Added more comments Sanity check on external scripts Enhanced locking Simplified ps output handling @ text @d51 1 d64 1 a64 1 int shutdown_warning(int, char *); /* warn of impending shutdown */ d71 1 d93 1 a93 1 static char *fileid = "$Id: upsd.c,v 1.7 1995/12/05 17:00:43 alexis Exp alexis $"; d123 1 d125 1 d199 1 a199 1 } else if (!strcmp(argv[1], "-ss")) { /* shutdown script */ d205 1 a205 1 } else if (!strcmp(argv[1], "-sw")) { /* warning script */ d211 1 a211 1 } else if (!strcmp(argv[1], "-sc")) { /* cancelled script */ d320 3 d325 1 d368 1 a368 1 shutdown_warning((int) (shutdown_time - tm), warning_script); d480 2 a481 2 int interval, char *warning_script) d484 1 a484 1 return(0); d492 1 d501 3 d507 67 a666 1 @ 1.7 log @Added lockfile support Added beginnings of support for diferent lock styles Added beginnings of support for external scripts @ text @d3 1 a3 1 * Program: * d5 1 a5 1 * Author: * d7 1 a7 1 * Description: * d25 2 d47 4 d53 1 a53 1 * Forward references for internal and external functons d65 1 d69 9 d91 5 a95 1 static char *fileid = "$Id: upsd.c,v 1.6 1995/12/04 11:21:51 alexis Exp alexis $"; d99 11 a109 9 /* * Function: * * Parameters: * * Return value: * * Description: */ a116 1 char *version; /* version of this file */ d121 1 a121 1 int ioctl_flags; /* ioctl() sets this */ d138 5 d148 7 a154 1 version = (strtok(fileidbuf, " "), strtok(NULLCP, " "), strtok(NULLCP, " ")); d163 6 d226 6 d235 61 a295 2 /* lock the device */ if ((lockingpid=lock_device(devcp, 0)) > 0) d299 1 a299 6 debug(5, "stat()ing %s ...", devcp); /* does port exist */ if (stat(devcp, &devstat)) error("couldn't stat %s", devcp); /* is it a char device */ if (!(devstat.st_mode & S_IFCHR)) error("%s is not a character device", devcp); debug(5, "open()ing %s ...", devcp); /* open for ioctl()ing */ d301 1 a301 2 error("can't open specified device"); d306 6 d313 1 a313 1 debug(5, "entering loop ..."); /* loop looking at DCD */ d318 1 a318 1 debug(10, "powerstat: %d", pwrstat); d345 1 d378 1 a378 1 exit(0); /* to satisfy compiler */ d388 1 a388 1 exit(3); d439 1 a441 1 printf("%s: ERROR: ", progname); d443 1 a443 1 vprintf(fmt, argp); d445 3 a447 2 printf("\n"); exit(1); d455 1 a456 1 printf("%s: INTERNAL ERROR: ", progname); d458 1 a458 1 vprintf(fmt, argp); d460 9 a468 2 printf("\n"); exit(2); d483 1 d491 2 a492 1 return(0); a504 2 char lockbuf[64]; char tmplockbuf[64]; d509 5 a513 3 /* * Create a temporary lock file */ d524 1 a524 1 debug(5, "lockfile is %s, tmplockfile is %s", lockbuf, tmplockbuf); d527 1 d538 1 d542 5 a546 3 /* * If possible move into place */ d548 1 a548 1 debug(5, "linking %s to %s ...", lockbuf, tmplockbuf); d550 1 d552 2 a553 1 debug(10, "successful, %s should exist", lockbuf); d556 1 a556 1 debug(5, "lock file already exists"); d558 5 a562 3 /* * If we're here then it wasn't possible so check the existing file */ d571 6 a576 2 debug(5, "lockfile %s contains pid %d", lockbuf, lockingpid); if (pidrunning(lockingpid) != 0) return(lockingpid); d578 5 a582 4 /* * If we get to here then the process isn't running and we have a * stale lock file. */ d584 1 a584 1 debug(5, "stale lock file"); d587 3 a589 2 debug(5, "trying again"); a602 1 debug(5, "opening pipe ..."); a604 1 debug(5, "forking ..."); a606 1 else if (pid != 0) { d608 5 a612 5 /* the parent */ debug(5, "parent running ..."); /* don't need to write to pipe */ close(pipefds[1]); debug(5, "parent slurping child's output..."); d614 1 a614 26 for (totalcnt=0; (thiscnt=read(pipefds[0], pslinebuf+totalcnt, sizeof(pslinebuf)-totalcnt)) != 0; totalcnt+=thiscnt) { debug(5, "size of buffer: %d, read: %d, total read: %d", sizeof(pslinebuf), thiscnt, totalcnt); } /* internal("ps output exceeded %d bytes", sizeof(pslinebuf)); */ debug(5, "total read: %d", totalcnt); pslinebuf[totalcnt] = '\0'; while (wait((int *) 0) != pid) ; close(pipefds[0]); debug(500, "ps exited and returned: [%s]", pslinebuf); sprintf(expectbuf, "\n%5d ", runningpid); debug(5, "searching ps output for [%s]", expectbuf); for (i=0; i /* for stat.h, getpid() */ d24 1 d40 2 a41 1 #define LOCK_NAME LOCK_NAME_UUCP /* choose from above */ d56 4 a59 4 void shutdown_now(void); /* do an immediate shutdown */ int shutdown_warning(int); /* warn of impending shutdown */ int shutdown_cancelled(void); /* tell shutdown cancelled */ int lock_device(char *); /* lock a serial device */ d61 1 d75 1 a75 1 static char *fileid = "$Id: upsd.c,v 1.5 1995/12/01 15:09:33 alexis Exp alexis $"; d109 4 d127 3 d157 18 d192 4 a195 1 if (lock_device(devcp)) error("%s is in use by process %d"); d197 1 a197 1 if (stat(devcp, &devstat)) error("couldn't stat specified device"); d199 1 a199 1 if (!(devstat.st_mode & S_IFCHR)) error("not a character device"); d230 1 a230 1 shutdown_cancelled(); d256 1 a256 1 shutdown_warning((int) (shutdown_time - tm)); d273 1 a273 1 shutdown_now(); /* "the time has come ..." */ d323 1 a323 1 printf("%s: DEBUG[%d]: ", progname, level); d325 1 a325 1 vprintf(fmt, argp); d327 1 a327 1 printf("\n"); d360 2 a361 1 int interval) d367 2 a368 1 int shutdown_cancelled() d374 2 a375 1 void shutdown_now() d382 2 a383 1 char *devcp) d402 5 a406 2 sprintf(lockbuf, "/usr/lib/uucp/%s", basecp); sprintf(tmplockbuf, "/usr/lib/uucp/%s.%d", basecp, mypid); d410 7 a416 4 if ((tmplockfp=fopen(tmplockbuf, "w")) == NULLFP) error("problem opening temporary lock"); #if LOCK_CONTENTS == LOCK_CONTENTS_ASCII) fprintf(tmplockfp, "%d", mypid); d418 1 a418 1 internal("unsupported lock contents %d", LOCK_CONTENTS); d420 6 a425 2 fclose(tmplockfp); d430 2 a431 1 if (link(lockbuf, tmplockbuf)) { d433 1 d436 1 d444 1 a444 1 #if LOCK_CONTENTS == LOCK_CONTENTS_ASCII) d449 79 @ 1.5 log @Fixecd d buf g with strok\tok*() call Added i optionsa  for all periintervals NMAll debug and nemessage functions now support multiple avartriable argument lists , @ text @d17 1 a17 1 #include /* required for stat.h */ d19 1 a19 1 #include /* for sleep() */ d31 1 d35 5 d45 11 a55 9 void debug(int, char *, ...); void info(char *, ...); void warning(char *, ...); void error(char *, ...); void internal(char *, ...); void usage(void); void shutdown_now(void); /* void 'cos doesn't return */ int shutdown_warning(int); int shutdown_cancelled(void); d69 1 a69 1 static char *fileid = "$Id: upsd.c,v 1.4 1995/12/01 09:16:24 alexis Exp alexis $"; d80 1 d84 2 a85 2 int argc, char *argv[]) a90 1 d100 3 a102 3 int shutdown_interval = DFLT_SHUTDOWN_INTERVAL; int checking_interval = DFLT_CHECKING_INTERVAL; int warning_interval = DFLT_WARNING_INTERVAL; d104 1 a104 1 int killpwr_bit = TIOCM_RTS; d114 4 d157 2 a158 2 if (argc != 2) usage(); devcp = argv[1]; d160 3 a162 2 /* some sanity checks */ debug(5, "stat()ing %s ...", devcp); d164 1 d167 1 a167 2 /* get a handle */ debug(5, "open()ing %s ...", devcp); a170 1 debug(5, "entering loop ..."); d174 1 d176 1 a176 1 /* loop looking at DCD */ d178 3 a180 3 sleep(checking_interval); ioctl(devfd, TIOCMGET, &ioctl_flags); pwrstat = !(ioctl_flags & TIOCM_CD); d184 1 a184 1 * No change and power ok d187 1 a187 1 if (pwrstat == lastpwrstat && pwrstat == TRUE) d191 2 a192 1 * Changed and power ok d201 2 a202 1 * Changed and power not ok (note fall through) d212 4 a215 1 * No change and power not ok d229 7 d239 1 a239 1 shutdown_now(); d254 2 a255 2 char *fmt, ...) d257 1 a257 1 va_list argp; d259 1 a259 1 if (3 > debug_level) return; d268 2 a269 2 char *fmt, ...) d271 1 a271 1 va_list argp; d273 1 a273 1 if (2 > debug_level) return; d282 2 a283 2 int level, char *fmt, d286 1 a286 1 va_list argp; d288 1 a288 1 if (level > debug_level) return; d297 1 a297 1 char *fmt, d300 1 a300 1 va_list argp; d302 1 a302 1 if (2 > debug_level) return; d312 1 a312 1 char *fmt, d315 1 a315 1 va_list argp; d341 57 a397 1 exit(0); @ 1.4 log @Removed constraint that parameter must start '/dev/' and added constraint that it must be a character device. @ text @d19 2 d26 4 a29 2 #define DFLT_DEBUG_LEVEL 10 /* default debug level */ #define DFLT_TIME_OUT 240 /* default 4 minute warning */ d33 1 a33 3 #define INTER_LOOP_TIME 5 /* check every 5 seconds */ #define PWR_ON 1 /* power is on */ #define PWR_OFF 2 /* power is off */ d39 5 a43 7 void debugs(int, char *); void debugss(int, char *, char *); void debugsn(int, char *, int); void info(char *); void warning(char *); void error(char *); void internal(char *); d60 2 a61 1 static char *fileid = "$Id: upsd.c,v 1.3 1995/11/30 09:45:26 alexis Exp $"; /* RCS version control id */ a78 1 int idx; /* handy integer */ d89 1 d91 7 d102 3 a104 2 /* extract version number */ version = (strtok(fileid, " "), strtok(NULLCP, " "), strtok(NULLCP, " ")); d107 1 a107 1 if (strcmp(argv[1], "-V") == 0) { /* -V prints version info */ d111 1 a111 1 } else if (strcmp(argv[1], "-v")) { /* verbose */ d114 19 a132 1 } else if (strncmp(argv[1], "-d", 2)) { /* debugging (very verbose) */ d135 2 d145 1 a145 1 if (argc <= 1) usage(); d149 1 d154 1 d158 5 d164 2 a165 2 for (shutdown=FALSE,lastpwrstat=PWR_ON;!shutdown;lastpwrstat=pwrstat) { sleep(INTER_LOOP_TIME); d167 6 a172 1 pwrstat = (ioctl_flags & TIOCM_CD); d174 1 a174 1 if (pwrstat == lastpwrstat && pwrstat == PWR_ON) d177 5 a181 1 if (pwrstat == PWR_ON) { d186 13 a198 2 if (pwrstat != lastpwrstat) shutdown_time = time((time_t *) NULL) + DFLT_TIME_OUT; d201 7 a207 1 shutdown_warning((int) (shutdown_time - tm)); d215 1 d221 1 a221 1 fprintf(stderr, " %s [ -v | -d level ] port\n", progname); d228 3 a230 3 void debugs( int level, char *s1) d232 8 a239 2 if (level <= debug_level) printf("%s: DEBUG[%d]: %s\n", progname, level, s1); d242 3 a244 4 void debugss( int level, char *s1, char *s2) d246 8 a253 2 if (level <= debug_level) printf("%s: DEBUG[%d]: %s %s\n", progname, level, s1, s2); d256 1 a256 1 void debugsn( d258 2 a259 9 char *s1, int n1) { if (level <= debug_level) printf("%s: DEBUG[%d]: %s %d\n", progname, level, s1, n1); } void info( char *message) d261 1 a261 3 if (3 <= debug_level) printf("%s: INFO: %s\n", progname, message); } d263 6 a268 5 void warning( char *message) { if (2 <= debug_level) printf("%s: WARNING: %s\n", progname, message); d272 2 a273 1 char *message) d275 8 a282 2 if (2 <= debug_level) printf("%s: ERROR: %s\n", progname, message); d287 2 a288 1 char *message) d290 7 a296 1 printf("%s: INTERNAL: %s\n", progname, message); d303 1 a303 1 debugsn(5, "SHUTDOWN IN", interval); d309 1 a309 1 debugs(5, "SHUTDOWN CANCELLED"); d315 1 a315 1 debugs(5, "SHUTTING DOWN NOW!"); @ 1.3 log @Added missing functions and resolved syntax errors. @ text @d17 2 d60 1 a60 1 static char *fileid = "$Id: upsd.c,v 1.2 1995/11/28 12:50:11 alexis Exp alexis $"; /* RCS version control id */ d89 1 a117 3 if (strncmp(devcp, "/dev/", 5) != 0) warning("devices normally live in /dev!"); debugss(3, "monitoring DCD in port", devcp); d119 5 d127 1 @ 1.2 log @Doesn't compile and a couple of functions remain to be written, but the logic is there as well as the wrapper and required functionality. First step make and sort of syntax errors and unresolved symbols. @ text @d43 3 d58 1 a58 1 static char *fileid = "$Id: upsd.c,v 1.1 1995/11/28 10:48:57 alexis Exp alexis $"; /* RCS version control id */ d77 1 a90 1 debugss(5, "progname: ", progname); a92 1 debugss(5, "version: ", version); d138 2 a139 2 if (time((time_t *) NULL) < shutdown_time) { shutdown_warning(); d143 1 a143 1 shutdown = TRUE: d145 2 d212 19 @ 1.1 log @Initial revision @ text @d13 17 d35 3 a37 1 void debug(int, char *); d42 1 d55 1 a55 1 static char *fileid[] = "$Id$"; /* RCS version control id */ d68 1 a68 1 main d74 10 d86 1 a86 1 progname = (((cp=strrchr(argv[0], '/')) == NULL) ? progname : cp+1; d88 3 d92 2 a93 2 while (argc > 1 && argv[1][0] == '-') { if (strcmp(argv[1], "-V") == 0) { d97 1 a97 1 } else if (strcmp(argv[1], "-v") { d100 3 a102 3 } else if (strncmp(argv[1], "-d", 2) { (argc == 2) && usage(); (sscanf(argv[2], "%d", &debug_level) != 1) && usage(); d104 1 a104 1 } else d107 1 a107 1 argc--; d110 33 d148 4 a151 1 fprintf(stderr, " %s [ -v | -d level ]\n", progname); d155 8 d169 10 a178 1 printf("%s: DEBUG[%d]: %s%s\n", progname, level, s1, s2); @