// log_any.c // $Id: log_any.c 124 2011-09-11 16:05:51Z csherwood $ // Testing using standard library calls to write to a file // Developing UNSAFE signal interupt handler // // requires one argument- // the configuration file name- format expected port#_instid_baud.conf // // has a function that just adds the Moxa time, since serialByteRead() gets parts of // a logical data message, we only want to add the time to the entire line, once read // // csherwood@usgs.gov, emontgomery@usgs.gov, July-Aug 2011 #include #include #include #include #include #include "serial.h" #include "moxadevice.h" #include #include #define MAX_FNAME_SIZE 255 // Global variables or structure for passing clean-up info to signal interrupt handler? FILE *fdp, *fdp_log, *fdp_cfg, *fdp_data; /* file stream pointer for stdio library calls */ int fds, fds_log, fds_cfg, fds_data, fds_port; /* file descriptor for system calls */ char tmp[255], instid[12]; char bufd[1024]; char datafilename[127]="/var/sda/testdata/instrument_YYYYMMDDHHMMSS.dat"; unsigned int baud_rate=9600; int parity=0, data_bits=8, stop_bit=1, flow; fd_set readfds; int tmpfd, maxfd = -1; int portnum; // function declarations static void my_exit(int sig); int tsout( FILE *fd, char *buf ); int main(int argc, char *argv[]) { int len, ier; ssize_t iw; char buf[255]; /* may be too small */ char nbuf[1024]; char logfname[255]; time_t now, tt; struct tm gm, *gmp; unsigned int iseconds = 30; pid_t my_pid, parent_pid; // params from Sam's code to read config file //char tmp[300]; // name of the symlinked log file (in get_data) //char outarchfilename[255]; // name of the archive file (in archive) //char syscmdbuf[512]; // a buffer to hold system commands //int serport = 99; // the "linux" name of the port (e.g. tty0) char portname[30]; unsigned int sermode, baudrate; int parity, databits, stopbits, flowcontrol; int result, nchars, tstat, knt=0; char str1[10], str2[10], str3[10], str4[10], str5[10], str6[10], str7[10], str8[10]; char *p, *p2, *lastc; struct timeval tv; // Set to check every 2 seconds to see if that fixes getting YSI in 2 parts tv.tv_sec = 2; tv.tv_usec = 0; // on dell that has an actual db9 serial port, use /dev/ttyS0 // on lenovo with just USB, not sure what is right- maybe /dev/ttyUSB0? // on Moxa ?? /dev/ttyM0 signal(SIGINT,my_exit); /* terminal interuppt (e.g., ctrl-C) */ signal(SIGTERM,my_exit); /* terminate process */ now=time(NULL); gmp = gmtime(&now); gm = *gmp; my_pid = getpid(); parent_pid = getppid(); // Required argument: name of config file if( argc != 2 ){ snprintf(buf,sizeof(buf),"Config file name required: argc: %2d",argc); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); my_exit( SIGUSR1 ); } // config file name second section has the string to use in logging p=index(argv[1],'_'); p2=rindex(argv[1],'_'); nchars=(int)p2-(int)p; strncpy(instid,p+1, nchars-1); // open a log file with a name that includes the PID and date //snprintf(logfname,sizeof(logfname),"./%s_%d.log", instid, my_pid); // for use with cron, use absolute path to open a log file with a name that includes the PID snprintf(logfname,MAX_FNAME_SIZE,"/var/sda/logs/%s_%04d%02d%02d%02d%02d%02d_%06d.log", instid,1900+gm.tm_year,gm.tm_mon+1,gm.tm_mday,gm.tm_hour,gm.tm_min,gm.tm_sec,(int)my_pid); fdp_log = fopen(logfname,"wt"); if(fdp_log == NULL) { // Send this message to a better place? stdout and stderr? printf("Problem opening log file\n"); my_exit(SIGUSR1); } // Get system fd of log file. See TLPI p. 248 printf("System i/o file descriptor for logfile: %d\n", (fds_log = fileno(fdp_log))); // Log program name and PID to log file snprintf(buf,sizeof(buf),"%s PID : %d",argv[0],my_pid ); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); // Open the config file (based on Sam Laney's code) snprintf(buf,sizeof(buf),"Config file name: %s",argv[1]); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); if( (fdp_cfg=fopen( argv[1], "rt")) == NULL ) { snprintf(buf,sizeof(buf),"Could not open config file: %s",argv[1] ); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); my_exit( SIGUSR1 ); } else { // read and log the header snprintf(buf,sizeof(buf),"Opened config file: %s",argv[1] ); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); /* config file format: 1 header line, followed by a csv list- 1 line with 8 things: * port #, inst_identifier, serial mode (232), baud, parity, length, stop bits, flow ctl * they go into str1-str8 */ fgets(tmp, 200, fdp_cfg); //read header snprintf(buf,sizeof(buf), "conf header: %s", tmp); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); // read the info we use from conf file if (fgets(tmp, 200, fdp_cfg) == NULL){ // if EOF is reached on an fgets snprintf(buf,sizeof(buf),"No data in config file %s",argv[1]); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); my_exit( SIGUSR1 ); } // put the config data into the right records e.g.: 4,ysi,232,9600,N,8,1,N result = sscanf(tmp,"%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^',']", str1, str2, str3, str4, str5, str6, str7, str8); printf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", str1, str2, str3, str4,str5,str6,str7,str8); portnum = atoi(str1); // save the name string to the global variable for later use strcpy(portname, str2); sermode = (unsigned int) atoi(str3); baudrate = (unsigned int) atoi(str4); parity = atoi(str5); databits = atoi(str6); stopbits = atoi(str7); flowcontrol = atoi(str8); printf("File label %s port %d BAUD %d PARITY %d DATA %d STOP %d FLOW %d\n", portname, portnum, baudrate, parity, databits, stopbits, flowcontrol); if ((portnum < 0) | (portnum > 7)) { snprintf(buf,sizeof(buf),"Bad value requested for serial port number."); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); my_exit( SIGUSR1 ); } // close the config file fclose(fdp_cfg); snprintf(buf,sizeof(buf),"Closed config file: %s",argv[1]); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); } // Make a data file name // months in time structure are jan==0, so we need to add one to display correctly // this needs to be the full path name for when run in cron snprintf(datafilename,sizeof(buf),"/var/sda/data/%s/%s_%04d%02d%02d%02d%02d%02d_%06d.dat", instid,instid,1900+gm.tm_year,gm.tm_mon+1,gm.tm_mday,gm.tm_hour,gm.tm_min,gm.tm_sec,(int)my_pid); // open data file fdp_data = fopen(datafilename,"w"); if(fdp_data == NULL) { snprintf(buf,sizeof(buf),"Problem opening data file: %s",datafilename); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); my_exit( SIGUSR1 ); } snprintf(buf,sizeof(buf),"Opened data file: %s",datafilename); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); // Get system fd of log file. See TLPI p. 248 fds_data = fileno(fdp_data); snprintf(buf,sizeof(buf),"System fds for datafile: %2d",fds_data); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); snprintf(buf,sizeof(buf),"Trying to open serial port %4d",portnum); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); /* need something here to deal with not being able to open the port * it can get into a state where SerialOpen() just doesn't return*/ ier = SerialOpen( portnum ); snprintf(buf,sizeof(buf),"SerialOpen: %4d",ier); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); fds_port = FindFD( portnum ); snprintf(buf,sizeof(buf),"Port open with fds_port=%4d",fds_port); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); /* // TODO: This does not work right if( (unsigned int)SerialGetMode( portnum ) !=sermode) { printf("Mode error!!\n"); exit( -1); } if( SerialSetMode( portnum, sermode) < 0){ printf("SerialSetMode error!!\n"); exit(-1); } if( SerialGetMode( portnum ) !=sermode) { printf("Mode error!!\n"); exit( -1); } */ ier = SerialSetSpeed( portnum, baudrate); snprintf(buf,sizeof(buf),"Return from SerialSetSpeed: %2d",ier); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); ier = SerialSetParam( portnum, parity, databits, stopbits); snprintf(buf,sizeof(buf),"Return from SerialSetParam: %2d",ier); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); ier = SerialFlowControl( portnum, flow); snprintf(buf,sizeof(buf),"Return from SerialFlowControl: %2d",ier); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); //StartLogger(); snprintf(buf,sizeof(buf),"Starting to log..."); ier = tsout( fdp_log, buf ); ier = tsout( stdout, buf ); maxfd = fds_port; //This is where it waits until it get's something and then logs it while(1) { //sleep(1); FD_ZERO( &readfds); FD_SET( fds_port, &readfds); tmpfd= select( maxfd+1, &readfds, NULL, NULL, &tv); if( tmpfd <= 0) continue; tstat=gettimeofday(&tv, NULL); if( FD_ISSET( fds_port, &readfds)){ len= SerialBlockRead( portnum, bufd, 1024); bufd[len]= '\0'; // put the last char of each chunk in a place available to strncmp lastc=&bufd[len-1]; if ( knt > 0 && fds_data != -1){ // knt > 0 avoids partial first line if (strncmp(lastc,"\n",1)==0) { // use tsout only when there's a CR at the end of buffer strncat(nbuf,bufd,len); //write( fds_data, bufd, len); ier = tsout(fdp_data, nbuf); nbuf[0]='\0'; //fflush(nbuf); // empty the buffer accumulating the strings } else { // the write makes a sensible looking data record, but it's not written // as one long record, it's a series of chunks, so pre-pending moxa // time doesn't make sense. If concatenate into a buffer until a // is recieved, that works better. //write( fds_data, bufd, len); strncat(nbuf,bufd,len); } } if (strncmp(lastc,"\n",1)==0) // count lines read knt+=1; } } // Graceful exit my_exit( SIGUSR1 ); } // MY_EXIT - Clean up, close files, write to log, exit static void my_exit(int sig) { // sig is assigned by signal or passed by user // UNSAFE use of printf() flush(), close(), and exit() // TODO - Do this with async-safe calls? int ier; char buf[255]; // (0) identify cause of exit switch (sig) { case SIGINT : { sprintf(buf,"Caught SIGINT\n"); break; } case SIGTERM : { sprintf(buf,"Caught SIGTERM\n"); break; } case SIGUSR1 : { sprintf(buf,"Caught SIGUSR1\n"); break; } default : { sprintf(buf,"Unknown signal\n"); } } // Close files in order of importance: // (1) close serial port if( (ier=SerialClose(portnum)) != SERIAL_OK ) sprintf(buf,"Problem closing serial port %d; returned %d",portnum,ier); sprintf(buf,"Closed serial port %d",portnum); // (2) close data file if( fdp_data != NULL ){ write( fds_data, "\n", 1 ); fflush(fdp_data); fclose(fdp_data); } // (3) close log file if( fdp_log != NULL ){ ier = tsout(fdp_log,buf); fflush(fdp_log); fclose(fdp_log); } exit(0); } // TSOUT - prepend time stamp, print contents of buf, add CR int tsout( FILE *fd, char *buf ) { //time_t t; struct tm gm, *gmp; struct timeval tv; int iw=1, tstat, nlen=0; char tbuf[255]; char *lastc; tstat=gettimeofday(&tv,NULL); //gmp = gmtime(&t); // pointing at the tv seconds gets the time from gettimeofday, so usecs will be synched gmp = gmtime(&tv.tv_sec); gm = *gmp; // save local copy...see TLPI p. 192 snprintf(tbuf,29,"%4d %02d %02d %02d %02d %10.7f ", 1900+gm.tm_year,gm.tm_mon+1,gm.tm_mday,gm.tm_hour,gm.tm_min,(gm.tm_sec+((double)tv.tv_usec/1000000))); //printf("lengths of tbuf and buf are: %d, %d.\n", strlen(tbuf), strlen(buf)); nlen=strlen(buf); if ((strlen(tbuf) + strlen(buf)) < sizeof(tbuf)-1) { strncat(tbuf,buf,nlen); lastc=&buf[nlen-1]; if (strncmp(lastc,"\n",1) != 0) strncat(tbuf,"\n",1); iw = fprintf(fd,"%s",tbuf); fflush(fd); } else { printf("string too long- look for error in data!\n"); printf("%s\n",buf); //email manager that something's wrong iw=-1; } }