Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 27 Mar 1996 07:49:50 -0600
From:      Alex Nash <alex@orion.fa.tca.com>
To:        freebsd-isp@freebsd.org
Subject:   RE: UPS with FreeBSD?
Message-ID:  <199603271349.HAA18354@orion.fa.tca.com>

next in thread | raw e-mail | index | archive | help

> > 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 	<fcntl.h>
#include 	<unistd.h>
#include 	<signal.h>
#include 	<stdio.h>
#include	<termios.h>
#include 	<sys/ioctl.h>
#include 	<sys/syslog.h>
#include	<sys/types.h>
#include	<sys/wait.h>

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-----------------------------




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199603271349.HAA18354>