/* * Copyright 1996, 1997, 1998, 1999, 2003 by Daniel B. Suthers, * Pleasanton Ca. 94588 USA * E-MAIL dbs@tanj.com * * You may freely copy, use, and distribute this software, * in whole or in part, subject to the following restrictions: * * 1) You may not charge money for it. * 2) You may not remove or alter this copyright notice. * 3) You may not claim you wrote it. * 4) If you make improvements (or other changes), you are requested * to send them to me, so there's a focal point for distributing * improved versions. * */ /* This module is to be called by the first process to run under HEYU. * It: * 1) Locks the TTY port by putting it's pid in LOCKDIR/LCK..ttyX * 2) Validates any existing HEYU locks in LOCKDIR/LCK..heyu.monttyX * and sets a lock in LOCKDIR/LCK..heyu.monttyX with it's PID if * none exists. * 3) Starts reading from the TTY port associated with the CM11A * and writing the raw bytes to SPOOLDIR/heyu.out * The heyu.out file will be deleted if it exists and created new. * 4) Upon SIGHUP signal will truncate the .in file.... someday, but not yet * 5) Upon SIGTERM or SIGINT will... * Close the tty port * unlink the TTY lock * unlink the heyu.monttyX lock * unlink the heyu.out file * unlink the x10_tty file */ #ifdef SCO #define _IBCS2 #endif #include #include #include "x10.h" #include #include #include #ifdef LINUX #include #endif #include #include #include #include #include #include #include #include extern int tty; extern int verbose; extern unsigned char cm11map[]; extern int i_am_relay; extern void quit(), error(); extern char x10_tty[50]; char spoolfile[PATH_MAX]; char monfile[PATH_MAX]; char writelock[PATH_MAX]; int interrupted = 0; int auto_update_clock = 1; void alarmist(); void flag_intr(); int sleeptime; /* tty should be the device that we are going to use. It should be a fully * qualified path name (/dev/tty2), but may be just the device (tty2) */ int start_relay(tty_name, fork_a_child) char *tty_name; int fork_a_child; { unsigned char ibuff[80]; long child; long pid; int outfd; int count, expected; int powerfail, in_sync; int count_5a; int first_byte; int troublecount; int saved_dst; char argv[2][5]; struct stat file_buf; struct tm *tm_buff; extern char *argptr; extern int ttylock(), c_setclock(), lock_for_write(), munlock(); extern int setup_tty(), port_locked, check_lock_for_write(); extern char *make_lock_name(); extern unsigned long lockpid(); time_t pfail_time, starttime, seconds, first_5a; char RCSID[]= "@(#) $Id: relay.c,v 1.20 2003/03/17 01:40:32 dbs Exp dbs $\n"; display(RCSID); first_byte = 1; troublecount=0; in_sync = 0; seconds = time(NULL); first_5a = seconds ; tm_buff = localtime(&seconds); saved_dst = tm_buff->tm_isdst; /* is a relay in place ? */ if( lockpid(make_lock_name(monfile)) > 1) { if( stat(spoolfile, &file_buf) < 0 ) { char tmpbuf[sizeof(spoolfile) + 100]; sprintf(tmpbuf, "The file %s does not exist or is not writable.", spoolfile); error(tmpbuf); } if( verbose ) printf("There was already a monitor running (pid = %ld)\n", lockpid(make_lock_name(monfile)) ); return(-1); /* there was a valid monitor running */ } else { /* we will spawn a monitor process */ if(fork_a_child == 1) { child = fork(); if( child > 0 ) { sleep(3); /* give child time to set up */ return(1); /* this is parent process */ } if( child < 0 ) /* This is an error */ { perror("I could not spawn process"); syslog(LOG_DAEMON | LOG_ERR, "I could not spawn process.\n"); quit(); } } } strcpy(argptr, "heyu_relay"); pid = 999; /* dummy value in case we don't fork. */ if(fork_a_child == 1) { /* from this point out, it should be the child. */ close(0); close(1); close(2); pid = setsid(); /* break control terminal affiliation */ } openlog( "heyu_relay", 0, LOG_DAEMON); if (pid == -1) { syslog(LOG_ERR, "relay setsid failed--\n"); quit(1); } else syslog(LOG_ERR, "relay setting up-\n"); /* Ok. We're alone now. */ if( ttylock(monfile) < 0) { syslog(LOG_ERR, "Could not set up the heyu.mon lock-"); exit(0); /* a competeing process must have started up * in the last few milliseconds */ } setup_tty(1); /* open the real tty */ i_am_relay = 1; /* set flag so calling function will clean up. */ unlink(spoolfile); outfd=open(spoolfile, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0777); if( outfd < 0 ) { sprintf(ibuff, "Trouble creating spoolfile (%s)", spoolfile); syslog(LOG_ERR, ibuff); quit(); } chmod(spoolfile, 0777); (void) signal(SIGINT, flag_intr); (void) signal(SIGTERM, flag_intr); (void) signal(SIGHUP, flag_intr); /* certain codes come out 1 second apart. These are the 5a and a5 * codes. They indicate the CM11A wants a response, ie a polling * sequence indicator. * In order to handle powerfails, we have to timestamp each a5 character * as it comes in. Three 0xa5 characters in a row, 1 second apart * would indicate a power fail condition that needs a reply. * If the very first byte received is a5 or 5a, it's a condition * that needs a reply. * As an alternative, a leading byte that's more than 1 second from the * previous one __may__ be a polling sequence. * Adding a counter to make sure it was a standalone byte may help when * something like a checkum just happens to equal 0xa5. */ powerfail = 0; /* increment this each time a 0xa5 is seen */ strcpy(argv[0], " ");/* set a vector that can be used by c_setclock() */ strcpy(argv[1], " ");/* set a vector that can be used by c_setclock() */ count_5a = 0; pfail_time = time(NULL); #ifdef USESIGINT siginterrupt(SIGALRM,1); /* make reads interruptable */ #endif while(1) { alarm(0); /* just in case I ever forget */ /* Check the spool file to make sure it has not exceeded limits */ stat(spoolfile,&file_buf); if (file_buf.st_size > 1048576) { char tmpbuf[sizeof(spoolfile) + 100]; sleep(5); /* allow readers to finish */ if( ftruncate(outfd, 0) == 0 ) { sprintf(tmpbuf,"Relay: truncated file (%s). It has grown too large.\n", spoolfile); syslog(LOG_ERR, tmpbuf); } else { sprintf(tmpbuf,"Relay: Could not truncate file (%s). Exiting\n", spoolfile); syslog(LOG_ERR, tmpbuf); exit(2); } } starttime = time(NULL); /* save when we started to read */ /* Once every 10 minutes, we want to check for the daylight savings * flag. If it has changed, we want to upload the current time to * the clock. */ errno=0; signal(SIGALRM, alarmist); alarm(600); count = read(tty, ibuff, 1); /* just wait for a byte */ alarm(0); if ( count == -1 && errno == EINTR) { /* We didn't get anything except the alarm. */ troublecount = 0; { tm_buff = localtime(&starttime); if ((saved_dst != tm_buff->tm_isdst) && auto_update_clock == 1) { syslog(LOG_ERR, "Heyu relay DST change. CM11 clock set.\n"); saved_dst = tm_buff->tm_isdst; c_setclock(1, argv); alarm(2); /* just in case */ read(tty, ibuff, 1); /* throw away the unused checksum */ alarm(0); } } continue; } if ( count < 1 ) /* no bytes read or error. (not a alarm) */ { /* This is an indication that I could not read from the TTY for * some reason. I see it happen when using USB, but not when * using a standard serial port. Hmmmm. Maybe I'll need to close * and re-open the port. * Report read trouble every 100 occurances */ if ( troublecount++ % 100 ) { sleep(1); syslog(LOG_ERR, "Heyu relay reading bad data from cm11.\n"); } continue; } troublecount = 0; /* we got this far, so no trouble */ /**** WHAT IF THIS IS BACKKWARDS??? TST how about making it out of sync until I *****/ if( (time(NULL) - starttime ) > 5 ) { /* we must be in sync if it's been a while * since the first byte */ in_sync = 1; count_5a = 0; if( verbose && isatty(1) ) printf("insync\n"); } if ( ibuff[0] != 0x5a ) count_5a = 0; if (count_5a == 0 ) first_5a = starttime; if( (ibuff[0] == 0x5a && count_5a == 0) || ibuff[0] != 0x5a ) { /* just write the first 0x5a that's seen, or any unknown data */ if ( count > 0 && (ibuff[0] != 0x377 )) write(outfd,ibuff, 1); /* if we've reached this point and the RI is still asserted, it * should be time for sending 0xc3, NO? * let's try that next time that I'm deep into the code. */ } /* this code sets 'insync' if 1 or more seconds has passed since getting the previous 0x5a */ if(ibuff[0] == 0x5a && in_sync != 1) { if( (time(NULL) - first_5a ) >= 1 ) { in_sync = 1; if( verbose && isatty(1) ) printf("insync\n"); } } /* this code is supposed to prompt the CM11 when an alert signal (0x5a) is received that is not part of a packet. */ if( ibuff[0] == 0x5a && count >= 1 ) { if (check_lock_for_write() == 0 ) count_5a = 2; if( ++count_5a > 1 || ((count_5a > 0 && in_sync != 1))) { in_sync = 1; ibuff[0] = 0xc3; /* tell the CM11A to send it */ { port_locked = 1; write(tty,ibuff, 1); } /* read the number of bytes to read. If it's greater than * the size of the buffer, let the outer loop copy it * to the spoolfile. (out of sync... Noise, etc ) * If it's 1 byte, there's the chance that it's a special * Like power fail or hail request. */ alarm(2); count = read(tty, ibuff, 1); /* read a byte that tells how alarm(0); * many bytes will follow. */ if( count == 1 ) /* so far so good */ { expected = ibuff[0]; if( (expected < 0) || (expected > 20 ) ) { /* out of range * We must not be synced. */ in_sync = 0; continue; /* go to outer while to grab this */ } write(outfd,ibuff, 1); /* write the number of * bytes to the spool file. */ signal(SIGALRM, alarmist); alarm(10); count = read(tty, ibuff, expected); alarm(0); if( count != expected ) { /* This should be too few. * so we aren't in sync yet. */ if ( count > 0 ) { write(outfd,ibuff, count ); in_sync = 0; } continue; /* go to outer while to grab this */ } if( count == expected ) { count_5a = 0; in_sync = 1; write(outfd,ibuff, count); } if( (count == 2) && ( ibuff[0] == 1 && ((ibuff[1] & 0x0F) == 0x8) ) ) { syslog(LOG_ERR, "A hail request was received\n"); } } else { /* we did not get any response, * so let the outer loop handle it. */ continue; } } } else { count_5a = 0; } if(ibuff[0] == 0xa5 && count == 1) /* CM11A reports a power fail */ { if( powerfail == 0 ) /* set timestamp for the first poll */ { pfail_time = time(NULL); } if( (first_byte == 1) || (powerfail++ == 2) ) { if ( (powerfail == 3) && ((pfail_time != 0) && ((time(NULL) - pfail_time) > 2)) ) { /* 3 bytes of 'a5' in a row over a period of 3 seconds means a power failure*/ powerfail = 0; c_setclock(1, argv); read(tty, ibuff, 1); /* throw away the unused checksum */ pfail_time = 0; in_sync = 1; } } } else { powerfail = 0; pfail_time = 0; } first_byte = 0; ibuff[0] = '\0'; } return(0); } void alarmist() { signal(SIGALRM, alarmist); errno = EINTR; alarm(sleeptime); } void flag_intr() { extern int munlock(); extern char *make_lock_name(); interrupted = 1; (void) signal(SIGTERM, flag_intr); syslog(LOG_ERR, "interrupt received\n"); munlock(make_lock_name(monfile)); munlock(make_lock_name(x10_tty)); unlink(spoolfile); exit(0); }