/* CHECKUPS.SY5 - A program to monitor the BEST Power Technology FERRUPS uninterruptible power supply from a UNIX system. This program brings down the system when the reserve capacity drops below 5 minutes and the inverter is running. Copyright (C) 1985 - 1991 by BEST Power Technology, Inc. Version: April 29, 1991 (for UNIX System V) Features Added: High Ambient Temperature Shutdown Retries 3 Times before complaining about Communications Restart option added ( 3/29 ) Allow variable time to execute UPSDOWN script ( 3/29 ) "SMODE A" code removed "PW377" removed ( 4/29 ) */ #include #include #include #include struct termio ttyctl; /* Default values of command line options */ #define CUTOFF 5 #define ALLOW 2 #define SLEEPTIME 60 #define RETRIES 3 /* Message types. */ #define TIMEOUT 0 #define INVERTER 1 #define ALARM 2 /* Misc. defines */ #define MAXLINE 256 #define ERROR (-1) #define OK 0 #define TRUE 1 #define FALSE 0 #define NULL 0 /* global variables */ int msgtype; /* see above #defines */ int timeleft; /* number of minutes of backup time remaining */ int cutoff; /* value of 'timeleft' at which to start shutdown */ int sleeptime; /* secs. to sleep before re-checking for UPS messages */ int inverter; /* TRUE if inverter on */ int alarmflg; /* TRUE if alarm condition exists */ int cktemp; /* flag to select shutdown on high ambient temperature */ int retries; /* number of retries before timeout */ int restart; /* flag to select auto-restart after shutdown */ int allow; /* time to give UPSDOWN script to execute */ char *device; /* pointer to device name for tty line */ int fildes; /* file descriptor for tty line connected to UPS */ FILE *fp; /* pointer to file containing message to be broadcast */ char linebuf[MAXLINE]; char cmdbuf[80]; main(argc, argv) int argc; char *argv[]; { int i,n; char *p; int mtype; /* set default values of the command line options */ device = NULL; cutoff = CUTOFF; allow = ALLOW; sleeptime = SLEEPTIME; cktemp = FALSE; retries = RETRIES; restart = FALSE; /* read tty device name and options from command line */ i = 1; while (i < argc) { p = argv[i++]; if (*p != '-') { device = p; continue; } while (*p) { switch (tolower(*p++)) { case '-': break; case 'a': p += getnum(p,&allow); break; case 'c': p += getnum(p,&cutoff); break; case 's': p += getnum(p,&sleeptime); break; case 'r': restart = TRUE; break; case 'h': cktemp = TRUE; break; default: puts("invalid option\n"); usage(); exit(2); } } } /* check values of options */ if (device == NULL) { puts("device name required\n"); usage(); exit(2); } /* don't allow a cutoff of less than 5 minutes */ if (cutoff < 5) cutoff = 5; if ( allow < 1 || allow > cutoff -1 ) { printf("Invalid number for allow\n"); exit(2); } /* open the tty line connected to the FERRUPS */ if ((fildes = open(device, O_RDWR|O_NDELAY)) == ERROR) { printf("Cannot open tty line\n"); exit(2); } /* Set the tty line parameters, including baud rate, etc. We need to be able to do a read which will NOT hang up waiting for characters from the tty line if there are none. */ ttyctl.c_iflag = IXON | IXOFF; ttyctl.c_oflag = 0; ttyctl.c_cflag = B1200 | CS8 | CREAD | HUPCL | CLOCAL; ttyctl.c_lflag = 0; /* in particular, ICANON is off ! */ ttyctl.c_cc[VMIN] = 1; ttyctl.c_cc[VTIME] = 0; if (ioctl(fildes,TCSETA,&ttyctl) == ERROR) { printf("Error in configuring tty line\n"); exit(2); } /* set up shutdown string to send to UPS */ /* restart added 3/29/91 */ /* PW377 taken out 4/29/91 */ if ( restart ) strcpy(cmdbuf,"OFF A\r"); else strcpy(cmdbuf,"OFF \r"); n = allow * 60; /* convert to seconds */ i = 6; do { cmdbuf[i--] = n % 10 + '0'; } while ( (n/=10) > 0 ); while ( i > 3 ) cmdbuf[i--] = '0'; /* go into main program loop */ while (TRUE) { /* see if FERRUPS has a message ready */ if (msgrdy()) { /* trap spurious timeouts */ if (msgtype == TIMEOUT) { for (i=0; i < retries && msgtype == TIMEOUT; i++) { sleep(5); if (!msgrdy()) { msgtype = -1; break; } } if (i < retries && msgtype == -1) continue; } /* trap momentary inverter on or alarm situations */ mtype = msgtype; if (msgtype == INVERTER || msgtype == ALARM) { /* see if condition lasts 20 seconds */ sleep(20); if (!msgrdy()) continue; if (msgtype == INVERTER) mtype = INVERTER; } msgtype = mtype; /* display the message from FERRUPS */ printmsg(); /* see if we must shutdown */ if (msgtype == ALARM) { sleep(60); sendstr(cmdbuf); system("/etc/upsdown"); sleep(300); /* sleep until shutdown */ while (msgrdy()) { sendstr(cmdbuf); sleep(300); } close(fildes); exit(0); } } sleep(sleeptime); } } usage() { puts("usage: checkups [-c#] [-s#] [-h] device\n"); } msgrdy() /* Return TRUE if the situation requires that the users be interrupted by a message from the background task. */ { while(mfchar()); /* clear tty buffer */ /* Ask UPS to send the "fixed format" status string */ if (sendstr("f\r") == ERROR) { msgtype = TIMEOUT; return TRUE; } sleep(5); /* allow time for FERRUPS' response */ /* report TIMEOUT error if no response */ if (!mfchar()) { msgtype = TIMEOUT; return TRUE; } /* initialize the variables to be read from the status string */ inverter = FALSE; alarmflg = FALSE; timeleft = 9999; /* store the FERRUPS status string at 'linebuf' */ if (getstat(linebuf) == ERROR) { msgtype = TIMEOUT; return TRUE; } /* read inverter and alarm info from status string */ inverter = (linebuf[17] == '1'); alarmflg = (1 & tobinary(linebuf[20])) /* low AC out */ | (2 & tobinary(linebuf[21])) /* near low battery */ | (2 & tobinary(linebuf[23]));/* user test alarm */ if (cktemp) alarmflg |= (8 & tobinary(linebuf[20])); linebuf[62] = '\0'; /* mark end of remaining time */ getnum(&linebuf[58],&timeleft); /* is it time to shutdown ? */ if (alarmflg || ((timeleft <= cutoff) && inverter) ) { msgtype = ALARM; return TRUE; } /* or just warn everyone about time remaining */ if (inverter) { msgtype = INVERTER; return TRUE; } /* no message required */ return FALSE; } getstat(buf) char *buf; /* Get the fixed format status string into 'buf'. Return ERROR if the string is not found among the chars received from the UPS prior to "eoftty". Otherwise return OK. */ { int c,count; char *p; while (c = mfchar()) { if (ishex(c) ) { /* this might be the start of the status string */ count = 1; p = buf; *p++ = c; while (ishex(c = mfchar()) && count < 80) { *p++ = c; count++; } if (c == '\r' && count == 80) return OK; } } return ERROR; } ishex(c) int c; /* Return TRUE if c is a hexidecmal character, i.e. is in the set "0123456789ABCDEFabcdef". */ { if (isdigit(c)) return TRUE; c = toupper(c); if ((c >= 'A') && (c <= 'F')) return TRUE; return FALSE; } printmsg() { /* Create a file containing message to be broadcast to users. */ fp = fopen("ups.msg","w"); /* Write message to file. */ fputs("\n",fp); switch (msgtype) { case TIMEOUT: fputs("Cannot communicate with UPS.\n",fp); fputs("Please check cable and refer to the owner's manual.\n",fp); break; case INVERTER: fputs("UPS Inverter On.\n",fp); fprintf(fp,"Backup time remaining: %d minutes.\n",timeleft); break; case ALARM: fputs("Warning !!!\n",fp); fputs("System shutdown in 1 minute.\n",fp); fputs("Please log off NOW !\n",fp); break; default: fputs("unknown message from checkups\n",fp); } /* Close the file. */ fclose(fp); /* Broadcast the message. */ system("/etc/wall < ups.msg"); } getnum(string,var) char *string; int *var; /* Convert digits at string to a number at var. Return number of digits converted. */ { int length; int num; length = 0; num = 0; while (isdigit(*string)) { num = num * 10 + *string++ - '0'; ++length; } *var = num; return length; } tobinary(c) char c; /* Convert c as a hex digit to binary. */ { return (isdigit(c) ? c - '0' : toupper(c) - 'A' + 10); } sendstr(str) char *str; /* Send 'str' to the UPS. Return OK or ERROR. */ { int n; n = strlen(str); if (write(fildes,str,n) != n) return ERROR; return OK; } #define CMASK 0x7f mfchar() /* Return the next character from the UPS. Return 0 if none ready. */ { char c; return( (read(fildes,&c,1) > 0) ? c & CMASK : 0); }