/* //- // ========================================================================== // Copyright (C) 1995 - 2005 Alias Systems Corp. and/or its licensors. All // rights reserved. // // The coded instructions, statements, computer programs, and/or related // material (collectively the "Data") in these files are provided by Alias // Systems Corp. ("Alias") and/or its licensors for the exclusive use of the // Customer (as defined in the Alias Software License Agreement that // accompanies this Alias software). Such Customer has the right to use, // modify, and incorporate the Data into other products and to distribute such // products for use by end-users. // // THE DATA IS PROVIDED "AS IS". ALIAS HEREBY DISCLAIMS ALL WARRANTIES // RELATING TO THE DATA, INCLUDING, WITHOUT LIMITATION, ANY AND ALL EXPRESS OR // IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. IN NO EVENT SHALL ALIAS BE LIABLE FOR ANY DAMAGES // WHATSOEVER, WHETHER DIRECT, INDIRECT, SPECIAL, OR PUNITIVE, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, OR IN EQUITY, // ARISING OUT OF ACCESS TO, USE OF, OR RELIANCE UPON THE DATA. // ========================================================================== //+ */ #include #ifndef _WIN32 #include #endif #include #include #include #include #include #include #include #ifndef _WIN32 #include #include #else #include #include #include #endif #include #ifndef _WIN32 #include #endif #ifdef IRIX #include #endif #include #include "channelParse.h" #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif /* * Missing prototypes */ #ifndef _WIN32 #include #include #else #include #include #endif #ifdef LINUX #define GETOPTHUH '?' #define FNONBLK O_NONBLOCK #endif /* LINUX */ #ifdef _WIN32 #define PATH_MAX _MAX_PATH char * optarg = NULL; int optind = 1; int opterr = 0; int optlast = 0; #define GETOPTHUH 0 int getopt(int argc, char **argv, char *pargs) { if (optind >= argc) return EOF; if (optarg==NULL || optlast==':') { optarg = argv[optind]; if (*optarg!='-' && *optarg!='/') return EOF; } if (*optarg=='-' || *optarg=='/') optarg++; pargs = strchr(pargs, *optarg); if (*optarg) optarg++; if (*optarg=='\0') { optind++; optarg = NULL; } if (pargs == NULL) return 0; //error if (*(pargs+1)==':') { if (optarg==NULL) { if (optind >= argc) return EOF; // we want a second paramter optarg = argv[optind]; } optind++; } optlast = *(pargs+1); return *pargs; } int gettimeofday(struct timeval* tp) { unsigned int dw = timeGetTime(); tp->tv_sec = dw/1000; tp->tv_usec = dw%1000; return 0; } #else #define closesocket close #endif #ifndef lengthof #define lengthof(array) (sizeof(array) / sizeof(array[0])) #endif /* lengthof */ static int gDaemonMode = 0; static int gInetdMode = 0; static int gShowUsage = 0; static int gVerbose = 0; static int gDebugMode = 0; static int gReadReopen = FALSE; static int gReadRewind = FALSE; static int gReadNext = FALSE; static int gBufferSize = 4096; static float gMinRecordRate = 1.; static float gMaxRecordRate = 300.; static float gDefRecordRate = 60.; static channelInfo *gChannels = NULL; static char gProgramName[PATH_MAX] = { 0 }; static char gConfigFile[PATH_MAX] = { 0 }; static char gServerName[PATH_MAX] = { 0 }; static char gDataPath[PATH_MAX] = { 0 }; static FILE *gDataFile = NULL; static char *gBuffers[2] = { NULL, NULL }; static int gReadBuffer = 0; static int gBufferOffset= 0; static void parseArgs(int argc, char **argv); static void printUsage(); static void nonBlockFifo(int client_fd); static int handle_client(int client_fd); static void get_data(int client_fd); static channelInfo * create_channels(char *config, int client_fd); double toDouble(const struct timeval t) { return (double)(t.tv_sec) + (double)(t.tv_usec) / 1000000.0; } struct timeval toTimeval(double sec) { struct timeval tim; tim.tv_sec = sec; tim.tv_usec = (sec - tim.tv_sec) * 1000000; return tim; } int main(int argc, char **argv) { int status; int client_fd = -1; /* * on err, this exits... */ parseArgs( argc, argv); /* * Initialization... */ gBuffers[0] = malloc(gBufferSize); gBuffers[1] = malloc(gBufferSize); if ( ( NULL == gBuffers[0] ) || ( NULL == gBuffers[1] ) ) { CapError(-1, CAP_SEV_FATAL, gProgramName, "out of memory, -b %d failed",gBufferSize); exit(1); } *(gBuffers[0]) = '\0'; *(gBuffers[1]) = '\0'; if (gInetdMode) { /* * The socket to the client is open on file descriptor 0. * Handle one client and then exit. */ client_fd = 0; } if (gDaemonMode) { /* * Convert this process into a standard unix type daemon process. * It will be running in the background with no controlling terminal * and using syslog to report error messages. */ status = CapDaemonize(); if (status < 0) { CapError(-1, CAP_SEV_FATAL, gProgramName, NULL); exit(1); } } /* * open the configuration and data files */ gChannels = create_channels(gConfigFile, client_fd); if ( NULL == gChannels ) { CapError(-1, CAP_SEV_FATAL, gProgramName, NULL); exit(1); } if ( 0 == strcmp(gDataPath,"-") ) { gDataFile = stdin; } else { /* * RFE: delay opening until first read */ gDataFile = fopen(gDataPath, "r"); if ( !gReadReopen && (NULL == gDataFile) ) { CapError(-1, CAP_SEV_FATAL, gProgramName, NULL); exit(1); } } if ( NULL != gDataFile ) nonBlockFifo(-1); if (gInetdMode) { /* * The socket to the client is already open on file descriptor 0. * Handle one client and then exit. */ status = handle_client(client_fd); exit(status); } else { while (1) { /* * Set up the server socket and wait for a connection. */ client_fd = CapServe(gServerName); if (client_fd < 0) { CapError(-1, CAP_SEV_FATAL, gProgramName, NULL); exit(1); } /* Handle client requests */ status = handle_client(client_fd); if (status < 0) { CapError(-1, CAP_SEV_FATAL, gProgramName, NULL); } /* Shutdown the client */ closesocket(client_fd); client_fd = -1; /* Go back and wait for another connection request */ continue; } } } static int handle_client(int client_fd) { int status; size_t size, nitems; FILE *serial_file; char buf[1024]; /* buffer for serial IO */ CapCommand cmd; char ruser[64], rhost[64], realhost[64]; double rate, time; fd_set rd_fds; struct timeval timeout, now, *timeoutPtr; int recording = 0; double recordNow = 0.; double recordNext = 0.; double recordPeriod = 0.; double recordDelta = 0.; #ifdef _WIN32 int ms; #endif while (1) { FD_ZERO(&rd_fds); FD_SET(client_fd, &rd_fds); if ( !recording ) { timeoutPtr = NULL; } else { /* record and schedule the next recording */ #ifdef LINUX gettimeofday(&now, NULL); #else gettimeofday(&now); #endif recordNow = toDouble(now); if ( recordNext == 0.0 ) /* The first record */ recordNext = recordNow; while ( recordNext <= recordNow ) { get_data(client_fd); recordNext = recordNext + recordPeriod; } timeout = toTimeval( recordNext - recordNow ); timeoutPtr = &timeout; } /* * wait for commands or a timeout */ #ifndef _WIN32 status = select(FD_SETSIZE, &rd_fds, NULL, NULL, timeoutPtr); #else /* Should really use CapWaitTimeout when possible */ ms = -1; if (timeoutPtr) ms = timeoutPtr->tv_sec*1000 + timeoutPtr->tv_usec/1000; status = CapWaitTimeout(client_fd, ms); #endif if (status < 0 ) { if (errno == EINTR) { /* Ignore signals and try again */ continue; } /* Otherwise, give a fatal error message */ CapError(client_fd, CAP_SEV_FATAL, gProgramName, "select failed"); CapError(client_fd, CAP_SEV_FATAL, "select", NULL); exit(1); } else if (status == 0) { /* Try again */ continue; } /* There is data on the client file descriptor */ cmd = CapGetCommand(client_fd); switch (cmd) { case CAP_CMD_QUIT: return 0; case CAP_CMD_AUTHORIZE: status = CapGetAuthInfo(client_fd, ruser, rhost, realhost); if (status < 0) { return -1; } /* * If user@host is not authorized to use this server then: * * status = CapAuthorize(client_fd, 0); */ status = CapAuthorize(client_fd, 1); break; case CAP_CMD_INIT: /* Initial client/server handshake */ status = CapInitialize(client_fd, gProgramName); break; case CAP_CMD_VERSION: /* Send version information */ status = CapVersion(client_fd, gProgramName, "1.0", "Extern server (example) - v1.0"); break; case CAP_CMD_INFO: if (NULL == gChannels) { /* Only create the channel data once */ gChannels = create_channels(gConfigFile, client_fd); if ( NULL == gChannels) { status = CapError(client_fd, CAP_SEV_ERROR, gProgramName, "Missing or empty config file"); } } /* Return the recording information. gMaxRecordRate <= 0. * says recording is not supported */ { size_t buf_size = 0; if ( 0. < gMaxRecordRate) buf_size = INT_MAX; status = CapInfo(client_fd, gMinRecordRate, gMaxRecordRate, gDefRecordRate, buf_size, 1); } break; case CAP_CMD_DATA: /* Send frame data */ if (!recording ) get_data(client_fd); status = CapData(client_fd); break; case CAP_CMD_START_RECORD: /* Start recording */ rate = CapGetRequestedRecordRate(client_fd); size = CapGetRequestedRecordSize(client_fd); /* * Set up for recording operations */ if (rate < gMinRecordRate ) rate = gMinRecordRate; else if (rate > gMaxRecordRate ) rate = gMaxRecordRate; status = CapStartRecord(client_fd, rate, size); if (status != -1) { recordPeriod = 1.0 / rate; recordNext = 0.; recording = 1; } break; case CAP_CMD_STOP_RECORD: /* Stop recording */ status = CapStopRecord(client_fd); recording = 0; break; default: /* Ignore unknown commands */ status = CapError(client_fd, CAP_SEV_ERROR, gProgramName, "Unknown server command."); break; } if (status < 0) { return -1; } } /* return 0; */ } static void get_data(int client_fd) { channelInfo *chan = gChannels; char *tokBuffer; char *lp; int i; char *errOverFlow = "Input buffer overflow line length greater than -b %d"; if ( gReadReopen ) { if ( NULL != gDataFile ) fclose(gDataFile); gDataFile = fopen(gDataPath, "r"); if (NULL == gDataFile) { CapError(client_fd, CAP_SEV_FATAL, gProgramName, NULL); exit(1); } nonBlockFifo(client_fd); } else if ( gReadRewind ) rewind(gDataFile); /* * find the data record */ while ( lp = fgets(gBuffers[gReadBuffer] + gBufferOffset, gBufferSize - gBufferOffset, gDataFile) ) { gBufferOffset = strlen(lp); if ( '\n' == lp[gBufferOffset-1] ) { /* This completes a full record */ gReadBuffer = 1 - gReadBuffer; gBufferOffset = 0; /* * RFE: add support for leading character "comments" */ if (gReadNext) break; } else if ( gBufferSize <= gBufferOffset ) { CapError(client_fd, CAP_SEV_FATAL, gProgramName, errOverFlow, gBufferSize ); exit(1); } } /* * gReadBuffer is the buffer to read or in process 1-grb is complete! */ tokBuffer = gBuffers[1 - gReadBuffer]; while (chan != NULL) { float val; channelInfo *next = chan->next; for ( i = 0; i< chan->info->p1.dim; i++) { char *tok = strtok(tokBuffer," \n\t"); tokBuffer = NULL; /* how strtok works ;-o */ if ( NULL == tok || ( 1 != sscanf(tok,"%g", &val) )) { next = NULL; break; } chan->data[i] = val; } chan = next; } channelInfoSetData(gChannels, 1, NULL); return; } static channelInfo *create_channels(char *config, int client_fd) { channelInfo *channels = NULL; FILE *channel_file; char *channel_file_name = getenv("MAYA_EXTERN_SERVER_CFG"); /* figure out a filename to use */ if ( (NULL == config) || (strlen(config) < 1) ) { if ( NULL == channel_file_name ) channel_file_name = "externServer.cfg"; } else channel_file_name = config; channel_file = fopen(channel_file_name, "r"); channels = channelInfoCreate(channel_file, 1, NULL); if ( NULL != channel_file ) fclose(channel_file); return channels; } void printUsage() { fprintf(stderr, "Usage:\n"); #ifdef DEVEL fprintf(stderr, " %s [-hdNRrvDi] [-b size] [-c config] [-n name] [-f file]\n", gProgramName); #else /* DEVEL */ fprintf(stderr, " %s [-hdNRr] [-b size] [-c config] [-n name] [-f file]\n", gProgramName); #endif /* DEVEL */ fprintf(stderr, "\n"); fprintf(stderr, " -h Print this help message\n"); fprintf(stderr, " -d Run as a daemon in the background\n"); fprintf(stderr, " -N read next record, instead of last\n"); fprintf(stderr, " -r rewind file on reread\n"); fprintf(stderr, " -R reopen file on reread\n"); fprintf(stderr, " -t minimum record frequency in Hz\n"); fprintf(stderr, " -T maximum record frequency in Hz\n"); fprintf(stderr, " -H default record frequency in Hz\n"); #ifdef DEVEL fprintf(stderr, " -i Run from inetd\n"); fprintf(stderr, " -D Set the debug flag\n"); fprintf(stderr, " -v Set the vebose flag\n"); #endif /* DEVEL */ fprintf(stderr, " -b size max line length for input file\n"); fprintf(stderr, " -c config Use as the config file\n"); fprintf(stderr, " -f file file to read (default is stdin)\n"); fprintf(stderr, " -n name Set the UNIX socket name to name\n"); fprintf(stderr, "\n"); fprintf(stderr, "Defaults:\n"); fprintf(stderr, " %s -b %d\n", gProgramName, 4096); fprintf(stderr, " %s -c %s.cfg\n", gProgramName, gProgramName); fprintf(stderr, " %s -n %s\n", gProgramName, gProgramName); fprintf(stderr, "\nDefault input file is stdin\n"); fprintf(stderr, "\n"); exit(1); } void parseArgs(int argc, char **argv) { int opt; char *cptr; float optFloat; /* * Grab a copy of the program name */ #ifdef _WIN32 _splitpath (argv[0], NULL, NULL, gProgramName, NULL); #else cptr = strrchr(argv[0], '/'); if (cptr) { strcpy(gProgramName, (cptr + 1)); } else { strcpy(gProgramName, argv[0]); } #endif /* * Set default values */ gReadReopen = FALSE; gReadRewind = FALSE; gReadNext = FALSE; gBufferSize = 4096; gDaemonMode = 0; gShowUsage = 0; strcpy(gConfigFile, gProgramName); strcat(gConfigFile,".cfg"); strcpy(gDataPath,"-"); strcpy(gServerName, gProgramName); /* * Parse the options */ #ifdef DEVEL while ((opt = getopt(argc, argv, "hdNRrb:c:f:n:t:T:H:Div")) != -1) #else /* DEVEL */ while ((opt = getopt(argc, argv, "hdNRrb:c:f:n:t:T:H:")) != -1) #endif /* DEVEL */ { switch (opt) { case 'h': gShowUsage++; break; case 'd': gDaemonMode++; break; case 'N' : gReadNext = TRUE; break; case 'R' : gReadReopen = TRUE; break; case 'r' : gReadRewind = TRUE; break; case 'b' : gBufferSize = atoi(optarg); break; case 'c' : strcpy(gConfigFile, optarg); break; case 'f' : strcpy(gDataPath, optarg); break; case 'n': strcpy(gServerName, optarg); break; case 't': if ( 1 == sscanf( optarg ,"%f", &optFloat) ) { gMinRecordRate = optFloat; } else { gShowUsage++; } break; case 'T': if ( 1 == sscanf( optarg ,"%f", &optFloat) ) { gMaxRecordRate = optFloat; } else { gShowUsage++; } break; case 'H': if ( 1 == sscanf( optarg ,"%f", &optFloat) ) { gDefRecordRate = optFloat; } else { gShowUsage++; } break; /* DEVEL FLAGS */ case 'D': gDebugMode++; break; case 'i': gInetdMode++; break; case 'v': gVerbose++; break; case GETOPTHUH: gShowUsage++; } } /* * Check for errors * * KL: RFE: Should stat file type for "IFREG" and make sure that * gReadReopen and gReadRewind make sense here. */ if ( ( 0 == strcmp(gDataPath,"-") ) && ( gReadReopen || gReadRewind || gDaemonMode) ) { fprintf(stderr, "\nInvalid options for read from standard input.\n"); gShowUsage++; } if (gDaemonMode && gInetdMode) gShowUsage++; if (optind < argc) gShowUsage++; if ( gBufferSize < 1 ) gShowUsage++; if (gShowUsage) printUsage(); } #ifdef _WIN32 #define S_IFIFO _S_IFIFO #endif void nonBlockFifo(int clientFd) /* * See if the input is a fifo, and setup the fcntl correctly */ { struct stat info; int flags; int dataFd = fileno(gDataFile); fstat(dataFd, &info); if ( info.st_mode & S_IFIFO ) { if ( gReadRewind ) { CapError(clientFd, CAP_SEV_FATAL, gProgramName, "Cannot -r (rewind) FIFO"); exit(1); } #ifndef _WIN32 flags = fcntl(dataFd,F_GETFL); fcntl(dataFd,F_SETFL,flags|FNONBLK); #endif } }