Scott M. Mcdermott

UNIX Systems & Network Administrator
available for contract or salaried positions

check_hlt.c

/*
 * Nagios plugin to poll temp with "hot little therm" device via RS-232
 * (for remote execution by the ssh plugin)
 *
 * author:      Scott McDermott <scott.m.mcdermott@gmail.com>
 * therm unit:  http://www.spyderplant.com/ [defunct]
 *
 * arguments (all required):
 *      -d <device with HLT unit>
 *      -w <fahrenheit temperature warning threshold (too cold)>
 *      -c <fahrenheit temperature critical threshold (way too cold)>
 *      -W <fahrenheit temperature warning threshold (too hot)>
 *      -C <fahrenheit temperature critical threshold (way too hot)>
 */

#define _GNU_SOURCE

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <sys/time.h>

/* plugin return states
 */
#define STATE_UNKNOWN   -1              /* plugin can't run for some reason */
#define STATE_OK        0               /* everything within parameters */
#define STATE_WARNING   1               /* warning threshold exceeded */
#define STATE_CRITICAL  2               /* critical threshold exceeded */

/* program defines
 */
#define READ_LEN        6               /* chars in string output from therm */
#define READ_TIMEOUT    10              /* number of seconds to wait for input
                                           to be available before aborting */
#define CMD_GET_TMP     0x20            /* the address of the probe...in this
                                           case, unit 0, probe 2 (endianness) */
#define PORT_SPEED      B1200           /* terminal baud rate macro (for both
                                           directions) */

int     xread                   (int, void *, size_t);
int     xopen                   (char *, int);
void    *xmalloc                (ssize_t);
char    *xstrdup                (char *);

void    process_options         (int, char **);
void    send_command_byte       (int, int);
void    setup_terminal          (int);
void    wait_for_input          (int);
char    *decode_alert           (int);

unsigned long   warning_low, critical_low, warning_high, critical_high;
char            *device;

int
main(int argc, char **argv)
{
        int ret, fd, n, alert_state;
        char *result, *buf;
        float celsius, fahrenheit;

        process_options(argc, argv);

        buf = xmalloc(READ_LEN + 1);
        memset(buf, 0, READ_LEN + 1);

        fd = xopen(device, O_RDWR);
        setup_terminal(fd);

        send_command_byte(fd, CMD_GET_TMP);

        /* keep reading bytes as they become available until READ_LEN */
        for (n = 0; n < READ_LEN; n += ret) {
                wait_for_input(fd);
                ret = xread(fd, buf + n, READ_LEN - n);
        }

        if (sscanf(buf, "%f", &celsius) != 1) {
                fprintf(stderr, "main: sscanf failed\n");
                exit(EXIT_FAILURE);
        }

        fahrenheit = celsius * 9.0 / 5.0 + 32.0;

        if (fahrenheit > warning_high ||
            fahrenheit < warning_low) {
                alert_state = STATE_WARNING;
                if (fahrenheit > critical_high ||
                    fahrenheit < critical_low)
                        alert_state = STATE_CRITICAL;
        } else
                alert_state = STATE_OK;

        printf("HLT: %s: %3.1f (C) %3.1f (F)\n", decode_alert(alert_state),
               celsius, fahrenheit);

        exit(alert_state);
}

void
setup_terminal(int fd)
{
        struct termios tio;

        if (tcgetattr(fd, &tio) == -1) {
                perror("tcgetattr");
                exit(EXIT_FAILURE);
        }

        tio.c_iflag = IGNPAR|INPCK;
        tio.c_cflag = CLOCAL|CREAD|CS8;
        memset(&tio.c_oflag, 0, sizeof tio.c_oflag);
        memset(&tio.c_lflag, 0, sizeof tio.c_lflag);

        if (cfsetispeed(&tio, (speed_t)PORT_SPEED) == -1) {
                perror("cfsetispeed");
                exit(EXIT_FAILURE);
        }

        if (cfsetospeed(&tio, (speed_t)PORT_SPEED) == -1) {
                perror("cfsetospeed");
                exit(EXIT_FAILURE);
        }

        if (tcsetattr(fd, TCSANOW, &tio) == -1) {
                perror("tcsetattr");
                exit(EXIT_FAILURE);
        }

        if (tcflush(fd, TCIOFLUSH) == -1) {
                perror("tcflush");
                exit(EXIT_FAILURE);
        }
}

void
send_command_byte(int fd, int cmd)
{
        if (write(fd, &cmd, 1) == -1) {
                perror("write");
                exit(EXIT_FAILURE);
        }
}

/*
 * the therm doesn't report back immediately so we select on the input fd and
 * timeout if we don't hear anything before READ_TIMEOUT seconds.  Also
 * because of the slow line speed, we have to keep looping because a single
 * read() doesn't return all the bytes we know are coming.
 */
void
wait_for_input(int fd)
{
        fd_set fdset;
        struct timeval tv;

        memset(&tv, 0, sizeof tv);
        tv.tv_sec = READ_TIMEOUT;

        FD_ZERO(&fdset);
        FD_SET(fd, &fdset);

        if (!(select(FD_SETSIZE, &fdset, NULL, NULL, &tv))) {
                fprintf(stderr, "wait_for_input: input never became available"
                        "before READ_TIMEOUT (%d) seconds\n", READ_TIMEOUT);
                exit(EXIT_FAILURE);
        }
}

void
process_options(int count, char **vector)
{
        int ret;

        opterr = 0;

        while ((ret = getopt(count, vector, "w:c:d:W:C:")) != -1) switch (ret) {
                case 'w':
                        if (!(warning_low = strtoul(optarg, NULL, 10))) {
                                fprintf(stderr, "main: strtoul error (w)\n");
                                exit(EXIT_FAILURE);
                        }
                        continue;
                case 'c':
                        if (!(critical_low = strtoul(optarg, NULL, 10))) {
                                fprintf(stderr, "main: strtoul error (c)\n");
                                exit(EXIT_FAILURE);
                        }
                        continue;
                case 'W':
                        if (!(warning_high = strtoul(optarg, NULL, 10))) {
                                fprintf(stderr, "main: strtoul error (W)\n");
                                exit(EXIT_FAILURE);
                        }
                        continue;
                case 'C':
                        if (!(critical_high = strtoul(optarg, NULL, 10))) {
                                fprintf(stderr, "main: strtoul error (C)\n");
                                exit(EXIT_FAILURE);
                        }
                        continue;
                case 'd':
                        device = xstrdup(optarg);
                        continue;
                case '?':
                        fprintf(stderr, "unknown option argument\n");
                        exit(EXIT_FAILURE);
                case ':':
                        fprintf(stderr, "required option argument missing\n");
                        exit(EXIT_FAILURE);
                default:
                        fprintf(stderr, "invalid option\n");
                        exit(EXIT_FAILURE);
        }

        if (!device ||
            !warning_low || !critical_low ||
            !warning_high || !critical_high) {
                fprintf(stderr, "required arguments missing\n");
                exit(STATE_UNKNOWN);
        }
}

char *
decode_alert(int i)
{
        switch (i) {
                case STATE_UNKNOWN:
                        return (xstrdup("ERROR"));
                case STATE_OK:
                        return (xstrdup("OK"));
                case STATE_WARNING:
                        return (xstrdup("WARNING"));
                case STATE_CRITICAL:
                        return (xstrdup("CRITICAL"));
                default:
                        return (xstrdup("IMPOSSIBLE"));
        }
}

int
xopen(char *name, int flags)
{
        int fd;

        if (!name) {
                fprintf(stderr, "xopen: no name given!\n");
                exit(EXIT_FAILURE);
        }

        if ((fd = open(name, flags)) == -1) {
                perror("xopen");
                exit(EXIT_FAILURE);
        }

        return fd;
}

int
xread(int fd, void *address, size_t count)
{
        int ret;

        if (!address) {
                fprintf(stderr, "xread: address invalid\n");
                exit(EXIT_FAILURE);
        }

        if ((ret = read(fd, address, count)) == -1) {
                perror("xread: read");
                exit(EXIT_FAILURE);
        }

        return ret;
}

void *
xmalloc(ssize_t sz)
{
        void *p;

        if (!(p = malloc(sz))) {
                perror("xmalloc: allocation failure");
                exit(EXIT_FAILURE);
        }

        return p;
}

char *
xstrdup(char *s)
{
        char *p;

        if (!s) {
                fprintf(stderr, "xstrdup: null argument!\n");
                exit(EXIT_FAILURE);
        }

        if (!(p = strdup(s))) {
                perror("xstrdup");
                exit(EXIT_FAILURE);
        }

        return p;
}