Re: Temperature sensor

Scott Mcdermott <[email protected]> Thu, 20 Mar 2003 14:08:19 -0500
Newsgroups gmane.network.netsaint.user
Message-ID <[email protected]>
David Arnold on Thu 20/03 09:54 -0800:
> Can you send me the scripts that you wrote to interface to
> this device. We are in the process of purchasing a unit
> for our server room.

I wrote this to Netsaint plugin for the Hot Little Therm.
check_hlt.c (text/plain, 6.5 KB)
/*
 * Netsaint "hot little therm" plugin (for remote execution by the ssh plugin)
 *
 * author:	Scott McDermott <[email protected]>
 * owner:	Questra Consulting
 * license:	GPL >= 2.0
 * therm unit:	http://www.spyderplant.com/
 *
 * 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,
		nr_read,
		alert_state;
	char	*result,
		*buf;
	float	celsius,
		fahrenheit;

	process_options(argc, argv);

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

	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 (nr_read = 0; nr_read < READ_LEN; nr_read += ret) {
		wait_for_input(fd);
		ret = xread(fd, buf + nr_read, READ_LEN - nr_read);
	}

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