/* * Unobtrusively log data coming in on a serial device. * * $Id: montty.c,v 1.6 2001/12/11 08:26:52 dds Exp $ * * (C) Copyright 2001-2002 Diomidis Spinellis * * Permission to use, copy, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ #include #include #include #include #include #include #include #include #include /* * Set terminal fd speed to s; clear non-blocking mode to make poll work */ static void init_term(int fd, int s) { struct termios tio; int val; if (tcgetattr(fd, &tio) < 0) { syslog(LOG_ERR, "unable get termios: %m"); exit(1); } if (cfsetspeed(&tio, s) < 0) { syslog(LOG_ERR, "unable to set speed to %d: %m", s); exit(1); } if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) { syslog(LOG_ERR, "unable set termios: %m"); exit(1); } if ((val = fcntl(fd, F_GETFL, 0)) < 0) { syslog(LOG_ERR, "fcntl(F_GETFL) failed: %m"); exit(1); } val &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, val) < 0) { syslog(LOG_ERR, "fcntl(F_SETFL) failed: %m"); exit(1); } } /* * Expand src to dst */ static void expand(char *src, char *dst, int len) { int n = 0; char *p; for (p = src, n = 0; *p && n < len - 1; p++, n++) if (*p == '\\') switch (*++p) { case '\\': *dst++ = '\\'; break; case 'a': *dst++ = '\a'; break; case 'b': *dst++ = '\b'; break; case 'f': *dst++ = '\f'; break; case 't': *dst++ = '\t'; break; case 'r': *dst++ = '\r'; break; case 'n': *dst++ = '\n'; break; case 'v': *dst++ = '\v'; break; case '0': *dst++ = '\0'; break; default: syslog(LOG_ERR, "invalid escape in %s", src); exit(1); } else *dst++ = *p; *dst = 0; } /* First argv used for initialisation */ #define INIT_ARGV 2 main(int argc, char *argv[]) { FILE *f; char logname[1024]; char buff[1024]; char devname[1024]; struct pollfd pfd[1]; int n; /* True when initialisation strings must be sent */ int need_init = 1; int init_index = INIT_ARGV; int lockresult; if (argc < 2) { fprintf(stderr, "usage: %s line [initialisation string] ...\n" "e.g. %s cuaa0 'ATS82=76\\r\\n'\n", argv[0], argv[0]); exit(1); } daemon(0, 0); snprintf(logname, sizeof(logname), "montty.%s", argv[1]); openlog(logname, 0, LOG_LOCAL0); syslog(LOG_INFO, "starting up: pid %d", getpid()); snprintf(buff, sizeof(buff), "/var/run/montty.%s.pid", argv[1]); if ((f = fopen(buff, "w")) == NULL) { syslog(LOG_ERR, "unable to open pid file %s: %m", buff); exit(1); } else { fprintf(f, "%d\n", getpid()); fclose(f); } snprintf(devname, sizeof(devname), "/dev/%s", argv[1]); if ((pfd[0].fd = open(devname, O_RDWR | O_NONBLOCK)) < 0) { syslog(LOG_ERR, "unable to open monitor file %s: %m", devname); exit(1); } syslog(LOG_INFO, "monitoring %s", devname); pfd[0].events = POLLIN | POLLRDNORM | POLLERR; for (;;) { if (!need_init) { /* No initialisation needed, just wait for input */ syslog(LOG_DEBUG, "waiting for input"); if (poll(pfd, 1, INFTIM) < 0) { syslog(LOG_ERR, "poll(INFTIM) failed: %m"); exit(1); } } /* * We have input, or we need to initialise the device; * acquire a lock. */ switch (lockresult = uu_lock(argv[1])) { case UU_LOCK_OK: syslog(LOG_DEBUG, "acquired lock"); /* * Now that we have the lock, * check if we need to write the initialisation data. */ if (need_init) { if (init_index == INIT_ARGV) { syslog(LOG_DEBUG, "recycle fd"); /* Refresh fd */ close(pfd[0].fd); if ((pfd[0].fd = open(devname, O_RDWR | O_NONBLOCK)) < 0) { syslog(LOG_ERR, "unable to re-open monitor file %s: %m", devname); exit(1); } init_term(pfd[0].fd, B115200); } if (init_index < argc) { expand(argv[init_index], buff, sizeof(buff)); syslog(LOG_DEBUG, "write: %s", buff); if (write(pfd[0].fd, buff, strlen(buff)) != strlen(buff)) { syslog(LOG_ERR, "write failed: %m"); exit(1); } init_index++; sleep(1); } if (init_index == argc) need_init = 0; syslog(LOG_DEBUG, "sent init string"); } /* Check if there is still something to read. */ if (poll(pfd, 1, 0) < 0) { syslog(LOG_ERR, "poll(0) failed: %m"); exit(1); } if (pfd[0].revents & (POLLIN | POLLRDNORM)) { if ((n = read(pfd[0].fd, buff, sizeof(buff))) == -1) { syslog(LOG_ERR, "read failed: %m"); exit(1); } syslog(LOG_DEBUG, "read %d bytes", n); buff[n] = 0; syslog(LOG_INFO, "%s", buff); } if (uu_unlock(argv[1]) == -1) { syslog(LOG_ERR, "uu_unlock error %m"); exit(1); } syslog(LOG_DEBUG, "lock released"); break; case UU_LOCK_INUSE: syslog(LOG_DEBUG, "lock in use; sleeping"); sleep(1); /* Someone is using the device, we must re-init it */ need_init = 1; init_index = INIT_ARGV; break; default: syslog(LOG_ERR, "uu_lock error %s", uu_lockerr(lockresult)); } } }