From owner-freebsd-isp Wed Mar 27 05:44:15 1996 Return-Path: owner-isp Received: (from root@localhost) by freefall.freebsd.org (8.7.3/8.7.3) id FAA28187 for isp-outgoing; Wed, 27 Mar 1996 05:44:15 -0800 (PST) Received: from orion.fa.tca.com (alex@nash.pr.mcs.net [204.95.47.72]) by freefall.freebsd.org (8.7.3/8.7.3) with SMTP id FAA28181 for ; Wed, 27 Mar 1996 05:44:10 -0800 (PST) Received: (from alex@localhost) by orion.fa.tca.com (8.6.12/8.6.9) id HAA18354; Wed, 27 Mar 1996 07:49:50 -0600 Date: Wed, 27 Mar 1996 07:49:50 -0600 Message-Id: <199603271349.HAA18354@orion.fa.tca.com> From: Alex Nash To: freebsd-isp@freebsd.org Subject: RE: UPS with FreeBSD? Reply-to: alex@fa.tca.com Sender: owner-isp@freebsd.org X-Loop: FreeBSD.org Precedence: bulk > > I've looked through the archives, but I want to know if anyone has a > > simple solution for using APC Back-UPS 600 with FreeBSD 2.1. > > I'm looking for the same thing. I don't have a simple solution, but I do have *a* solution... > >I want something like a port with some directions for making up a cable. > > Same here -- I think one could use a parallel port adapted to the contact > closures on the Back-UPS 600. I'm not sure what the electrical specs are on the parallel port, but the Back-UPS series uses RS-232 level inputs and outputs. Unfortunately, you can't get away with using a standard serial cable. > > Barring that, I'll try to port some Linux software that I stumbled > > across, and make it available. However, if someone already has > > something working, I'd love to be able to just use it. > > Could you send me a copy of the Linux software -- or point me? I'd really > appreciate it. Linux UPS software takes advantage of init's ability to examine /etc/powerfail when sent a hangup signal. If /etc/powerfail contains FAIL, then init runs the powerfail entry from /etc/inittab. If it contains ok, it runs the powerokwait entry. FreeBSD init doesn't do this. Linux also runs a script at shutdown which can toggle the UPS' shutoff line, but I don't know how to do this under FreeBSD. Here's what I did: 1. Made a cable that connected the UPS' power fail line to CTS, and the shutoff line to RTS. 2. Wrote a daemon (upsd) that: - checked the state of CTS every 5 seconds - runs 'shutdown -h +1 "Power failure"' if CTS is high for a specified duration - continues to monitor CTS, and kills shutdown if the power comes back on (Source included at the end of this message.) 3. Hacked shutdown to not fork itself off into the background. This allows upsd to keep track of it and kill it off if the power comes back. Here's the section I commented out: #if 0 { int forkpid; forkpid = fork(); if (forkpid == -1) { perror("shutdown: fork"); exit(1); } if (forkpid) { (void)printf("shutdown: [pid %d]\n", forkpid); exit(0); } } #endif (yes, I could have parsed the output to get the pid) 4. Added a line to this section of /usr/src/i386/i386/machdep.c: if (howto & RB_HALT) { + outb(0x2f8 + 4/*MCR*/, 0x02/*MCR_RTS*/); printf("\n"); printf("The operating system has halted.\n"); As you can see, this is far from an elegant solution. It would be nice to eliminate step 3 by having a command line option to tell shutdown not to daemonize itself. And of course, step 4 must not be an ugly kernel hack. Comments, suggestions? Alex -----------------------------CUT HERE----------------------------- #include #include #include #include #include #include #include #include #include void SigChild () { while ( wait3(NULL, WNOHANG, NULL) > 0 ) ; } int LinePowerOk (int fd) { int flags; ioctl(fd, TIOCMGET, &flags); /* CTS is high during a power failure */ if (flags & TIOCM_CTS) return(0); return(1); } void SetShutoff (int fd, int state) { int powerBit = TIOCM_RTS; ioctl(fd, state ? TIOCMBIS : TIOCMBIC, &powerBit); } void SetPortMode (int fd) { struct termios term; tcgetattr(fd, &term); term.c_cflag &= ~HUPCL; tcsetattr(fd, TCSANOW, &term); } void InitConnection (int fd) { SetPortMode(fd); SetShutoff(fd, 0); } void SignalInit (int powerOk) { #ifdef LINUX static char *powerStatus = "/etc/powerstatus"; int fd; unlink(powerStatus); fd = open(powerStatus, O_CREAT | O_WRONLY, 0644); if (fd != -1) { static char okString[] = "OK\n"; static char failString[] = "FAIL\n"; if (powerOk) write(fd, okString, sizeof(okString) - 1); else write(fd, failString, sizeof(failString) - 1); close(fd); } kill(1, SIGPWR); #else static pid_t shutdownPid = 0; if (powerOk) { if (shutdownPid) kill(shutdownPid, SIGTERM); shutdownPid = 0; } else { if (shutdownPid == 0) { shutdownPid = fork(); if (shutdownPid < 0) syslog(LOG_DAEMON | LOG_CRIT, "unable to fork shutdown process"); if (shutdownPid == 0) { static char message[] = "Power failure"; execl("/sbin/shutdown", "shutdown", "-h", "+1", message, NULL); } else sync(); } } #endif } void WatchLine (int fd) { int previousState = LinePowerOk(fd); int failCount = 0; int shutdownInProgress = 0; while (1) { int currentState = LinePowerOk(fd); if (currentState == 1 && previousState == 0 && shutdownInProgress) { shutdownInProgress = 0; failCount = 0; syslog(LOG_DAEMON | LOG_CRIT, "Power restored"); SignalInit(1); } if (currentState == 0) { if (++failCount == 6) { shutdownInProgress = 1; SignalInit(0); syslog(LOG_DAEMON | LOG_CRIT, "Power failure"); } } previousState = currentState; sleep(5); } } int main (int argc, char *argv[]) { int fd = open("/dev/ups", O_RDWR); if (fd == -1) { perror("unable to open /dev/ups"); return(1); } InitConnection(fd); if (argc >= 2 && strcmp(argv[1], "killpower") == 0) SetShutoff(fd, 1); else { struct sigaction action; switch (fork()) { case 0: /* child */ setsid(); break; case -1: /* error */ fprintf(stderr, "%s: can't fork\n", argv[0]); return(1); default: /* parent */ return(0); } action.sa_flags = SA_RESTART; sigemptyset(&action.sa_mask); action.sa_handler = SigChild; sigaction(SIGCHLD, &action, NULL); openlog("upsd", 0, LOG_DAEMON); WatchLine(fd); } return(0); } -----------------------------CUT HERE-----------------------------