int main(int argc, char **argv) { int ups_fd, stat_fd, ch; int flags; int pstatus, poldstat = 1; int bstatus, boldstat = 1; int count = 0; int bcount = 0; int tries = 0; int ikill = 0; int ioctlbit; char *self = argv[0]; char killchar = ' '; struct upsdef *pups; while ((ch = getopt(argc, argv, "kc:d:r:s:t:")) != -1) switch (ch) { case 'k': ikill = 1; break; case 'c': config_file = optarg; break; case 'd': upsport = optarg; break; case 'r': rcpowerfail = optarg; break; case 's': upsstat = optarg; break; case 't': upstype = optarg; break; case '?': default: usage(self); } argc -= optind; argv += optind; if (argc > 0) usage(self); parse_config(config_file); if (upsport == NULL || upstype == NULL || upsstat == NULL) { usage(self); } for (pups = ups; pups; pups = pups->next) { if (strcmp(pups->tag, upstype) == 0) break; } if (!pups) { fprintf(stderr, "Error: %s: UPS <%s> unknown\n", self, argv[2]); exit(1); } /* Start syslog. */ openlog(self, LOG_CONS | LOG_PERROR, LOG_DAEMON); if ((ups_fd = open(upsport, O_RDWR | O_NDELAY)) < 0) { syslog(LOG_ERR, "%s: %s", upsport, strerror(errno)); closelog(); exit(1); } /* Kill the inverter and close out if inverter kill was selected */ if (ikill) { if (pups->killtime) { /* Explicitly clear both DTR and RTS as soon as possible */ ioctlbit = TIOCM_RTS; ioctl(ups_fd, TIOCMBIC, &ioctlbit); ioctlbit = TIOCM_DTR; ioctl(ups_fd, TIOCMBIC, &ioctlbit); /* clear killpower, apply cablepower to enable monitoring */ setlevel(ups_fd, pups->kill.line, !pups->kill.inverted); setlevel(ups_fd, pups->cablepower.line, !pups->cablepower.inverted); if (pups->kill.line == TIOCM_ST) { /* Send BREAK (TX high) to kill the UPS inverter. */ tcsendbreak(ups_fd, 1000 * pups->killtime); } else { /* Force high to send the UPS the inverter kill signal. */ setlevel(ups_fd, pups->kill.line, pups->kill.inverted); sleep(pups->killtime); } ioctl(ups_fd, TIOCMGET, &flags); /* * Feb/05/2001 Added support for Tripplite Omnismart 450PNP, this * UPS shutdowns inverter when data is sent over the Tx line * (jhcaiced) */ if (pups->flags & UPS_TXD_KILL_INVERTER) { sleep(2); write(ups_fd, &killchar, 1); } close(ups_fd); /************************************************************/ /* We never should have gotten here. */ /* The inverter kill has failed for one reason or another. */ /* If still in powerfail mode, exit with an error. */ /* If power is ok (power has returned) let rc.0 finish the */ /* reboot. */ /************************************************************/ if (getlevel(&pups->powerok, flags) == 0) { fprintf(stderr, "%s: UPS inverter kill failed.\n", self); exit(1); } /* if (getlevel(&pups->powerok,flags) == 0) */ /* Otherwise, exit normaly, power has returned. */ exit(0); } else { fprintf(stderr, "Error: %s: UPS <%s> has no support for killing the inverter.\n", self, pups->tag); exit(1); } /* if (pups->kill) */ } /* if (ikill) */ /****************************************/ /* If no kill signal, monitor the line. */ /****************************************/ /* Explicitly clear both DTR and RTS as soon as possible */ ioctl(ups_fd, TIOCMBIC, TIOCM_RTS); ioctl(ups_fd, TIOCMBIC, TIOCM_DTR); /* clear killpower, apply cablepower to enable monitoring */ setlevel(ups_fd, pups->kill.line, !pups->kill.inverted); setlevel(ups_fd, pups->cablepower.line, !pups->cablepower.inverted); /* Daemonize. */ #ifdef DEBUG closelog(); setsid(); #else switch (fork()) { case 0: /* Child */ closelog(); setsid(); break; case -1: /* Error */ syslog(LOG_ERR, "can't fork."); closelog(); exit(1); default: /* Parent */ closelog(); exit(0); } #endif /* switch(fork()) */ /* Restart syslog. */ openlog(self, LOG_CONS, LOG_DAEMON); /* Create an info file for powerfail scripts. */ unlink(upsstat); if ((stat_fd = open(upsstat, O_CREAT | O_WRONLY, 0644)) >= 0) { write(stat_fd, "OK\n", 3); close(stat_fd); } /* Give the UPS a chance to reach a stable state. */ sleep(2); /* Now sample the line. */ while (1) { /* Get the status. */ ioctl(ups_fd, TIOCMGET, &flags); /* Calculate present status. */ pstatus = getlevel(&pups->powerok, flags); bstatus = getlevel(&pups->battok, flags); if (pups->cableok.line) { /* Check the connection. */ tries = 0; while (getlevel(&pups->cableok, flags) == 0) { /* Keep on trying, and warn every two minutes. */ if ((tries % 60) == 0) syslog(LOG_ALERT, "UPS connection error"); sleep(2); tries++; ioctl(ups_fd, TIOCMGET, &flags); } /* while(getlevel(&pups->cableok,flags) */ if (tries > 0) syslog(LOG_ALERT, "UPS connection OK"); } else { /* * Do pseudo-cable check, bad power on startup == possible bad * cable */ if (tries < 1) { tries++; /* Do startup failure check */ if (!pstatus) { /* * Power is out: assume bad cable, but semi-scram to be * safe */ syslog(LOG_ALERT, "No power on startup; UPS connection error?"); /* * Set status registers to prevent further processing * until */ /* the status of the cable is changed. */ poldstat = pstatus; boldstat = bstatus; powerfail(PFM_CABLE); } /* if (!pstatus) */ } /* if (tries < 1) */ } /* if (pups->cableok.line) */ /* If anything has changed, process the change */ if (pstatus != poldstat || bstatus != boldstat) { count++; if (count < 4) { /* Wait a little to ride out short brown-outs */ sleep(1); continue; } /* if (count < 4) */ if (pstatus != poldstat) { if (pstatus) { /* Power is OK */ syslog(LOG_ALERT, "Line power restored"); powerfail(PFM_OK); } else { /* Power has FAILED */ if (bstatus) { /* Battery OK, normal shutdown */ syslog(LOG_ALERT, "Line power has failed"); powerfail(PFM_FAIL); } else { /* Low Battery, SCRAM! */ syslog(LOG_ALERT, "UPS battery power is low!"); powerfail(PFM_SCRAM); } /* if (bstatus) */ } /* if (pstatus) */ } /* if (pstatus != poldstat) */ if (bstatus != boldstat) { if (!bstatus && !pstatus) { /* Power is out and Battery is now low, SCRAM! */ syslog(LOG_ALERT, "UPS battery power is low!"); powerfail(PFM_SCRAM); } else { /* Battery status has changed */ if (bstatus) { /* Battery power is back */ syslog(LOG_ALERT, "UPS battery power is now OK"); } /* if (!bstatus) */ } /* if (!bstatus && !pstatus) */ } /* if (bstatus != boldstat) */ } /* if (pstatus != poldstat || bstatus != * boldstat) */ if (!bstatus && pstatus) { /* Line power is OK and UPS signals battery is low */ /* Log a message to the syslog every 10 minutes */ if ((bcount % 300) == 0) syslog(LOG_ALERT, "UPS battery power is low!"); bcount++; } else { /* Reset count */ bcount = 0; } /* if (!bstatus && pstatus) */ /* Reset count, remember status and sleep 2 seconds. */ count = 0; poldstat = pstatus; boldstat = bstatus; sleep(2); } /* while(1) */ /* Never happens */ return (0); }
/* Main program */ int main(int argc, char *argv[]) { int fd; int pkill = 0; int flags; int cntwait = 0; int cntstat = 0; int pstatus, poldstat = 1; /* assume power is good to start */ int bstatus, boldstat = 1; /* assume battery is good to start */ int wait = DEFAULT_WAIT; char *program = "tinyupsd"; char *device = DEFAULT_DEVICE; char *cwait; FILE *fp; /* program = argv[0]; */ /* Get any optional command line args */ while(argc > 1) { if(!strcmp(argv[1], "-d")) { device = argv[2]; } if(!strcmp(argv[1], "-w")) { cwait = argv[2]; wait = atoi(cwait); } if(!strcmp(argv[1], "-v")) { printf(version); exit(0); } if(!strcmp(argv[1], "-h")) { printf(usage); exit(0); } if(!strcmp(argv[1], "-k")) { pkill = 1; argc -= 1; argv += 1; } else { argc -= 2; argv += 2; } } /* Start syslog */ openlog(program, LOG_CONS|LOG_PERROR, LOG_DAEMON); /* Open monitor device */ if((fd = open(device, O_RDWR | O_NDELAY)) < 0) { syslog(LOG_ERR, "%s: %s", device, sys_errlist[errno]); closelog(); exit(1); } /* We were asked to kill ups power */ if(pkill) { /* We won't log to syslog, only to stderr */ closelog(); /* Kill power and exit (if the ups doesn't turn off */ powerkill(fd); close(fd); exit(0); } /* See if we are already running */ if(fp = fopen(PIDFILE, "r")) { syslog(LOG_ALERT, "fopen: %s: File already exists\n", PIDFILE); close(fd); closelog(); exit(1); } /* Daemonize */ switch(fork()) { case 0: /* Child */ closelog(); setsid(); break; case -1: /* Error */ syslog(LOG_ERR, "can't fork."); closelog(); exit(1); default: /* Parent */ closelog(); exit(0); } /* Do some signal handling */ signal(SIGQUIT, dodie); signal(SIGTERM, dodie); signal(SIGQUIT, dodie); /* Write our pid to file */ unlink(PIDFILE); if((fp = fopen(PIDFILE, "w")) != 0) { fprintf(fp, "%d\n", getpid()); fclose(fp); } /* Restart syslog */ openlog(program, LOG_CONS, LOG_DAEMON); syslog(LOG_NOTICE, "started dev=%s wait=%i", device, wait); /* Adjust our timeout for the 2 second increment countdown */ wait = wait / 2; /* Clear DTR and RTS */ ioctl(fd, TIOCMBIC, TIOCM_RTS); ioctl(fd, TIOCMBIC, TIOCM_DTR); /* Apply power to our cable by setting DTR */ ioctl(fd, TIOCMBIS, LINE_POWER); /* Begin our loop where we sample the line */ while(1) { /* Get the status */ ioctl(fd, TIOCMGET, &flags); /* Get ups condition */ pstatus = !(flags & LINE_FAIL); /* line high = power fail */ bstatus = flags & LINE_SCRAM; /* line low = battery low */ /* Process the change if anything has changed */ if(pstatus != poldstat || bstatus != boldstat) { /* Wait a little to ignore brownouts and false signals */ cntstat++; if(cntstat < 4) { sleep(1); continue; } #ifdef SYSV /* See if we have a power condition */ if(pstatus != poldstat) { if(pstatus) { /* Power has been restored */ syslog(LOG_ALERT, "power ok"); powerfail(0); } else { /* Power has failed */ if(bstatus) { /* Battery is good */ syslog(LOG_ALERT, "power has failed"); powerfail(1); } else { /* Battery is low */ syslog(LOG_ALERT, "power has failed and battery is low"); powerfail(2); } } } #endif } #ifndef SYSV if(!pstatus && !bstatus) { /* Low battery, shutting down now */ systemkill(fd); } else if(!pstatus) { /* No line power, count down now */ if((cntwait == 0) || ((cntwait) % 30 == 0)) { /* Inform immediately and every minute */ syslog(LOG_EMERG, "power failed, %d seconds to shutdown", ((wait-cntwait)*2)); } if(cntwait > wait) { /* Timeout reached, shutting down */ systemkill(fd); } cntwait++; } else { /* Power is ok, let everyone know, reset counter */ if(cntwait != 0) { syslog(LOG_EMERG, "power restored"); cntwait = 0; } } #endif /* Reset count, remember status and sleep for 2 seconds */ cntstat = 0; poldstat = pstatus; boldstat = bstatus; sleep(2); } }