/* * main.c: Fermenetation temperature control. * * Greg Lehey, 30 May 2004 * * Copyright (c) 2004 by Greg Lehey * * This software is distributed under the so-called ``Berkeley * License'': * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * This software is provided ``as is'', and any express or implied * warranties, including, but not limited to, the implied warranties * of merchantability and fitness for a particular purpose are * disclaimed. In no event shall Greg Lehey be liable for any direct, * indirect, incidental, special, exemplary, or consequential damages * (including, but not limited to, procurement of substitute goods or * services; loss of use, data, or profits; or business interruption) * however caused and on any theory of liability, whether in contract, * strict liability, or tort (including negligence or otherwise) * arising in any way out of the use of this software, even if advised * of the possibility of such damage. * * $Id: main.c,v 1.16 2005/04/03 03:32:32 grog Exp grog $ */ #include "main.h" /* * This can't go into main.h because readline conflicts with termios * :-( */ #ifdef LIBEDIT #include #else #include #include #endif FILE *rcfile; /* .rc file */ char *rcfilename = DEFAULTRCFILE; /* and its name */ struct stat rcstat; /* and its status */ FILE *historylog; /* history file */ char *historyfile; /* and its name */ #ifdef LIBEDIT History *hist; EditLine *el; HistEvent he; #endif char *dateformat; /* format in which to store date */ char buffer [BUFSIZE]; /* buffer to read in to */ int file_line = 0; /* and line in input file (yes, this is tacky) */ int inerror; /* set to 1 to exit after end of config file */ /* flags */ int lflag = 0; /* -l flag, usage varies */ int verbose = 0; /* set to 1 or more to be more verbose */ #ifdef LIBEDIT char *prompt(EditLine *el); #endif char *token [MAXARGS]; /* pointers to individual tokens */ int tokens; /* number of tokens */ jmp_buf command_fail; /* return on a failed command */ int verbose; FILE *infile; void usage (char *myname) { fprintf (stderr, "Usage:\n" "%s \n" "XXX Writeme\n", myname ); } /* * Check if we have an rc file, or if we have already read one, * whether it has been changed. If so, read it in. Return 1 if we * read it in, 0 if we don't, including error conditions. */ int checkrcfile () { if (rcstat.st_ino) /* we've done this before */ { time_t lastmtime; lastmtime = rcstat.st_mtime; /* when was it last updated? */ stat (rcfilename, &rcstat); /* and what's it like now? */ if (rcstat.st_mtime == lastmtime) /* no change */ return 0; else if (rcstat.st_mtime < lastmtime) /* got older? */ fprintf (stderr, "rc file got older!\n"); } else if (errno = stat (rcfilename, &rcstat)) /* can't stat history file, */ { if (errno != ENOENT) /* not "not there", */ { fprintf (stderr, "Can't open %s: %s (%d)\n", rcfilename, strerror (errno), errno); return 0; /* just give up */ } } /* OK, got a .rc file we want to read in */ if ((rcstat.st_mode & S_IFMT) == S_IFREG) /* silently ignore funny kinds of .rc file */ { rcfile = fopen (rcfilename, "r"); if (rcfile != NULL) { if (historylog != NULL) { timestamp (); fprintf (historylog, "*** reading %s ***\n", rcfilename); } while (fgets (buffer, BUFSIZE, rcfile)) { if (setjmp (command_fail) == 2) /* come back here on catastrophic failure */ { setrelay (0); monitoring = 0; /* stop monitoring: */ fprintf (stderr, "*** interrupted ***\n"); /* interrupted */ } if (historylog != NULL) add_history (buffer); /* save it in the history */ file_line++; /* count the lines */ tokens = tokenize (buffer, token, MAXARGS); /* got something potentially worth parsing */ if (tokens) parseline (tokens, token); /* and do what he says */ } } if (historylog != NULL) { timestamp (); fprintf (historylog, "*** finished reading %s ***\n", rcfilename); fflush (historylog); } fclose (rcfile); file_line = 0; /* reset line count for user input */ return 1; } return 0; /* didn't read anything */ } int main (int argc, char *argv [], char *envp []) { struct stat histstat; #ifdef LIBEDIT int scratch; #endif /* Set times to start time */ time (&nextheateron); /* next time we can turn the heater on */ time (&nextcooleron); /* next time we can turn the cooler on */ time (&nextheateroff); /* next time we can turn the heater off */ time (&nextcooleroff); /* next time we can turn the cooler off */ dateformat = "%e %b %Y %H:%M:%S"; #ifdef LIBEDIT hist = history_init (); history (hist, &he, H_EVENT, 100); el = el_init ("temperaturecontrol", stdin, stdout, stderr); /* forgot what the first parm is; check. */ el_set (el, EL_EDITOR, "emacs"); el_set (el, EL_PROMPT, prompt); el_set (el, EL_HIST, history, hist); el_source (el, NULL); #endif historyfile = DEFAULT_HISTORYFILE; /* XXX fix this */ if (stat (historyfile, &histstat) == 0) /* history file exists */ { if ((histstat.st_mode & S_IFMT) != S_IFREG) { fprintf (stderr, "History file %s must be a regular file\n", historyfile); exit (1); } } else if (errno != ENOENT) /* not "not there", */ { fprintf (stderr, "Can't open %s: %s (%d)\n", historyfile, strerror (errno), errno); exit (1); } historylog = fopen (historyfile, "a+"); if (historylog != NULL) { timestamp (); fprintf (historylog, "*** temperaturecontrol started ***\n"); /* name of program */ fflush (historylog); /* before we start the daemon */ } setsigs (0); /* set signal handler, but not SIGINT */ checkrcfile (); if (argc > 1) /* we have a command on the line */ { if (setjmp (command_fail) != 0) /* long jumped out */ { setrelay (0); if (debugfd) fprintf (debugfd,"Terminating on command error\n"); return -1; } parseline (argc - 1, &argv [1]); /* do it */ } else { /* * Catch a possible race condition which could cause us to * longjmp() into nowhere if we receive a SIGINT in the next few * lines. */ if (setjmp (command_fail)) /* come back here on catastrophic failure */ { setrelay (0); if (debugfd) fprintf (debugfd,"Terminating on command error\n"); return 1; } setsigs (1); /* set signal handler, including SIGINT */ for (;;) /* ugh */ { #ifdef LIBEDIT const char *c; #else char *c; #endif int childstatus; /* from wait4 */ if (setjmp (command_fail) == 2) /* come back here on catastrophic failure */ { monitoring = 0; /* stop monitoring: */ fprintf (stderr, "*** interrupted ***\n"); /* interrupted */ } /* reset flags */ lflag = 0; /* -l flag, usage varies */ verbose = 0; /* set to 1 or more to be more verbose */ while (wait4 (-1, &childstatus, WNOHANG, NULL) > 0); /* wait for all dead children */ #ifdef LIBEDIT c = el_gets(el, &scratch); /* get an input */ #else c = readline ("temperaturecontrol -> "); /* get an input */ #endif if (c == NULL) /* EOF or error */ { if (ferror (stdin)) { fprintf (stderr, "Can't read input: %s (%d)\n", strerror (errno), errno); setrelay (0); if (debugfd) fprintf (debugfd,"Terminating on input read error\n"); return 1; } else /* EOF */ { printf ("\n"); if (debugfd) fprintf (debugfd,"Terminating normally from command line\n"); setrelay (0); return 0; } } else if (*c) /* got something there */ { #ifdef LIBEDIT history(hist, &he, H_ENTER, c); /* save it in the history */ strcpy (buffer, c); /* put it where we can munge it */ #else add_history (c); /* save it in the history */ strcpy (buffer, c); /* put it where we can munge it */ free (c); #endif file_line++; /* count the lines */ tokens = tokenize (buffer, token, MAXARGS); /* got something potentially worth parsing */ if (tokens) parseline (tokens, token); /* and do what he says */ } if (historylog != NULL) fflush (historylog); } } if (debugfd) fprintf (debugfd,"Terminating normally from command line\n"); setrelay (0); return 0; /* normal completion */ } void timestamp () { struct timeval now; struct tm *date; char datetext [MAXDATETEXT]; time_t sec; if (historylog != NULL) { if (gettimeofday (&now, NULL) != 0) { fprintf (stderr, "Can't get time: %s\n", strerror (errno)); return; } sec = now.tv_sec; date = localtime (&sec); strftime (datetext, MAXDATETEXT, dateformat, date), fprintf (historylog, "%s.%06ld ", datetext, now.tv_usec ); } } #ifdef LIBEDIT /* Return a prompt */ char *prompt (EditLine *el __unused) { return "temperaturecontrol -> "; /* replace with program name */ } #endif /* Set action on receiving a SIGINT */ void setsigs (int sigint) { struct sigaction act; act.sa_flags = 0; sigemptyset (&act.sa_mask); if (sigint) /* catching SIGINT too */ { act.sa_handler = catchsig; sigaction (SIGINT, &act, NULL); } act.sa_handler = diediedie; sigaction (SIGSEGV, &act, NULL); sigaction (SIGBUS, &act, NULL); act.sa_handler = dumpinfo; sigaction (SIGUSR1, &act, NULL); } /* Catch SIGINT */ void catchsig (int ignore) { longjmp (command_fail, 2); } /* Catch SIGSEGV and SIGBUS */ void diediedie (int ignore) { fprintf (stderr, "Bombed out!\n"); if (debugfd) fprintf (debugfd,"Terminating on SIGSEGV\n"); setrelay (0); exit (1); /* XXX */ kill (getpid (), SIGSEGV); /* and tear our segments apart, creating a dump */ } /* SIGUSR1 */ void dumpinfo (int ignore) { doinfodump = 1; /* set flag to dump info */ } #include "parserkeys.h" /* Take args arguments at argv and attempt to perform the operation specified */ void parseline (int args, char *argv []) { int i; int j; enum keyword command; /* command to execute */ if (historylog != NULL) /* save the command to history file */ { timestamp (); for (i = 0; i < args; i++) /* all args */ fprintf (historylog, "%s ", argv [i]); fputs ("\n", historylog); } if ((args == 0) /* empty line */ || (*argv [0] == '#') ) /* or a comment, */ return; if (args == MAXARGS) /* too many arguments, */ { fprintf (stderr, "Too many arguments to %s, this can't be right\n", argv [0]); return; } command = get_keyword (argv [0], &keyword_set); #if 0 /* * XXX We don't have any generic flags here. We should remove the * ones we know and leave the rest; certainly we shouldn't complain * if we don't recognize one. */ /* * First handle generic options. We don't use getopt(3) because * getopt doesn't allow merging flags (for example, -fr). */ for (i = 1; (i < args) && (argv [i] [0] == '-'); i++) /* while we have flags */ { for (j = 1; j < strlen (argv [i]); j++) switch (argv [i] [j]) { case 'l': /* -d: debug */ lflag = 1; break; case 'v': /* -f: force */ verbose = 1; break; default: fprintf (stderr, "Invalid flag: %s\n", argv [i]); } } #endif i = 0; /* Pass what we have left to the command to handle it */ for (j = 0; j < (sizeof (funkeys) / sizeof (struct funkey)); j++) { if (funkeys [j].kw == command) /* found the command */ { funkeys [j].fun (args - i, &argv [i], &argv [0]); return; } } fprintf (stderr, "Unknown command: %s\n", argv [0]); } #ifdef linux /* * Linux doesn't have strlcpy by default. I should include the * implementation here, but I'm too lazy, so I just ignore the dangers * and use strcpy. */ size_t strlcpy (char *dst, const char *src, size_t size) { strcpy (dst, src); return strlen (dst); } #endif