home *** CD-ROM | disk | FTP | other *** search
/ kermit.columbia.edu / kermit.columbia.edu.tar / kermit.columbia.edu / sredird / telnetcpcd-1.09.tar.gz / telnetcpcd-1.09.tar / config.c < prev    next >
C/C++ Source or Header  |  2003-08-12  |  14KB  |  563 lines

  1. /*
  2.     config.c
  3.  
  4.     Copyright (c) 2002,2003 Thomas J Pinkl <tom@pinkl.com>
  5.  
  6.     Functions which deal with the configuration file.
  7.  
  8.     Version 1.00    11/30/2001        
  9.     Version 1.01    12/04/2001        Added LOCKDIR and LOCKTEMPLATE
  10.     Version 1.02    12/06/2001        Added MODEM, SPEED, etc.
  11.     Version 1.03    12/13/2001        Added FLOWCONTROL
  12.     Version 1.04    01/14/2002        Added POLLINTERVAL
  13.     Version 1.05    02/19/2002        Added DESCRIPTION and TRACKCARRIER
  14.     Version 1.06    02/22/2002        Changed POLLINTERVAL to MS_POLLINTERVAL 
  15.                                     and added LS_POLLINTERVAL.
  16.     Version 1.07    03/06/2002        Added REPLYPURGEDATA
  17.     Version 1.08    03/07/2002        Renamed MODEM to DEVICE
  18.     Version 1.09    04/24/2002        Added CONNFLUSH and DISCFLUSH.  Removed 
  19.                                     TRACKCARRIER.
  20.     Version 1.10    10/24/2002        Added IDLETIMER.
  21.     Version 1.11    12/03/2002        Added SENDLOGOUT.
  22. */
  23.  
  24. #include "telnetcpcd.h"
  25. unsigned long def_speed = 9600L;
  26. char *def_description = "Generic modem";
  27.  
  28. /*
  29.     opens and parses the configuration file.  the members of 
  30.     the config structure are filled in from the contents of 
  31.     the config file.
  32.  
  33.     returns 0 on success, 1 on error.
  34. */
  35. int read_config_file(char *file,struct config_t *conf)
  36. {
  37.     struct cf_entry entry;
  38.     unsigned long token;
  39.     int error = 0;
  40.     int lines;
  41.     FILE *fp;
  42.     char linebuff[BUFSIZ];
  43.     SERIAL_INFO *modem;
  44.     SERIAL_INFO defaults;
  45.  
  46.     fp = fopen(file,"r");
  47.     if (fp == NULL) {
  48.         syslog(LOG_ERR,"cannot open config file %s",file);
  49.         return(1);
  50.     }
  51.  
  52.     /*
  53.         init modem defaults
  54.     */
  55.     memset(&defaults,(int) 0, sizeof(SERIAL_INFO));
  56.     defaults.description = strdup(def_description);    /* MUST alloc memory */
  57.     defaults.speed = def_speed;
  58.     defaults.databits = 8;
  59.     defaults.parity = PARITY_NONE;
  60.     defaults.stopbits = 1;
  61.     defaults.flowcontrol = HARDWARE_FLOW;
  62.     defaults.conn_flush = 0;        /* don't flush serial port on connect */
  63.     defaults.disc_flush = 1;        /* do flush serial port on discconnect */
  64.  
  65.     /*
  66.         parse the config file and save the info within the 
  67.         config_t structure
  68.     */
  69.     lines = 0;
  70.     modem = &defaults;                    /* ptr to modem defaults */
  71.     while (fgets(linebuff,sizeof(linebuff),fp) != NULL) {
  72.         ++lines;                        /* incr config file line count */
  73.         token = parse_entry(linebuff,&entry);
  74.         switch (token) {
  75.         case COMMENT:
  76.         case BLANKLINE:
  77.             break;                        /* these are no-ops */
  78.         case DIRECTORY:
  79.             error = save_value(entry.value,entry.type,&(conf->directory));
  80.             break;
  81.         case TMPDIR:
  82.             error = save_value(entry.value,entry.type,&(conf->tmpdir));
  83.             break;
  84.         case LOCKDIR:
  85.             error = save_value(entry.value,entry.type,&(conf->lockdir));
  86.             break;
  87.         case LOCKTEMPLATE:
  88.             error = save_value(entry.value,entry.type,&(conf->locktemplate));
  89.             break;
  90.         case DEBUGLOG:
  91.             error = save_value(entry.value,entry.type,&(conf->debuglog));
  92.             break;
  93.         case DEBUGLEVEL:
  94.             error = save_value(entry.value,entry.type,&(conf->debuglevel));
  95.             break;
  96.         case PIDFILE:
  97.             error = save_value(entry.value,entry.type,&(conf->pidfile));
  98.             break;
  99.         case TIMEOUT:
  100.             error = save_value(entry.value,entry.type,&(conf->timeout));
  101.             break;
  102.         case USERNAME:
  103.             error = save_value(entry.value,entry.type,&(conf->user));
  104.             break;
  105.         case GROUP:
  106.             error = save_value(entry.value,entry.type,&(conf->group));
  107.             break;
  108.         case SERVERTYPE:
  109.             error = save_value(entry.value,entry.type,&(conf->servertype));
  110.             break;
  111.         case DEVICE:
  112.             /*
  113.                 add modem to conf->modems[] array.  increments conf->nmodems 
  114.                 and dynamically allocates memory for the device name.
  115.             */
  116.             error = add_modem(conf,entry.value);
  117.             if (! error) {
  118.                 modem = conf->modems[conf->nmodems - 1];    /* ptr to current modem */
  119.                 modem->speed = defaults.speed;                /* copy defaults */
  120.                 modem->databits = defaults.databits;
  121.                 modem->parity = defaults.parity;
  122.                 modem->stopbits = defaults.stopbits;
  123.                 modem->flowcontrol = defaults.flowcontrol;
  124.                 modem->description = strdup(defaults.description);
  125.                 modem->conn_flush = defaults.conn_flush;
  126.                 modem->disc_flush = defaults.disc_flush;
  127.             }
  128.             break;
  129.         case DESCRIPTION:
  130.             if (modem->description != NULL) {
  131.                 free(modem->description);
  132.                 modem->description = NULL;
  133.             }
  134.             error = save_value(entry.value,entry.type,&(modem->description));
  135.             break;
  136.         case SPEED:
  137.             error = save_value(entry.value,entry.type,&(modem->speed));
  138.             if (! error) {
  139.                 if (! valid_baudrate(modem->speed)) {
  140.                     syslog(LOG_ERR,"invalid modem speed at line %d: %s",lines,entry.value);
  141.                     error = 1;
  142.                 }
  143.             }
  144.             break;
  145.         case DATABITS:
  146.             error = save_value(entry.value,entry.type,&(modem->databits));
  147.             if (! error) {
  148.                 switch (modem->databits) {
  149.                 case 8:
  150.                 case 7:
  151.                 case 6:
  152.                 case 5:
  153.                     break;
  154.                 default:
  155.                     syslog(LOG_ERR,"invalid databits value at line %d: %s",lines,entry.value);
  156.                     error = 1;
  157.                     break;
  158.                 }
  159.             }
  160.             break;
  161.         case PARITY:
  162.             if (strcasecmp(entry.value,"none") == 0) {
  163.                 modem->parity = PARITY_NONE;
  164.             } else if (strcasecmp(entry.value,"odd") == 0) {
  165.                 modem->parity = PARITY_ODD;
  166.             } else if (strcasecmp(entry.value,"even") == 0) {
  167.                 modem->parity = PARITY_EVEN;
  168.             } else {
  169.                 syslog(LOG_ERR,"invalid parity value at line %d: %s",lines,entry.value);
  170.                 error = 1;
  171.             }
  172.             break;
  173.         case STOPBITS:
  174.             error = save_value(entry.value,entry.type,&(modem->stopbits));
  175.             if (! error) {
  176.                 switch (modem->stopbits) {
  177.                 case 1:
  178.                 case 2:
  179.                     break;
  180.                 default:
  181.                     syslog(LOG_ERR,"invalid stopbits value at line %d: %s",lines,entry.value);
  182.                     error = 1;
  183.                     break;
  184.                 }
  185.             }
  186.             break;
  187.         case FLOWCONTROL:
  188.             if (strcasecmp(entry.value,"hardware") == 0) {
  189.                 modem->flowcontrol = HARDWARE_FLOW;
  190.             } else if (strcasecmp(entry.value,"rts/cts") == 0) {
  191.                 modem->flowcontrol = HARDWARE_FLOW;
  192.             } else if (strcasecmp(entry.value,"software") == 0) {
  193.                 modem->flowcontrol = SOFTWARE_FLOW;
  194.             } else if (strcasecmp(entry.value,"xon/xoff") == 0) {
  195.                 modem->flowcontrol = SOFTWARE_FLOW;
  196.             } else if (strcasecmp(entry.value,"none") == 0) {
  197.                 modem->flowcontrol = NO_FLOWCONTROL;
  198.             } else {
  199.                 syslog(LOG_ERR,"invalid flow control value at line %d: %s",lines,entry.value);
  200.                 error = 1;
  201.             }
  202.             break;
  203.         case CONNFLUSH:
  204.             error = save_value(entry.value,entry.type,&(modem->conn_flush));
  205.             break;
  206.         case DISCFLUSH:
  207.             error = save_value(entry.value,entry.type,&(modem->disc_flush));
  208.             break;
  209.         case REPLYPURGEDATA:
  210.             error = save_value(entry.value,entry.type,&(conf->reply_purge_data));
  211.             break;
  212.         case MS_POLLINTERVAL:
  213.             error = save_value(entry.value,entry.type,&(conf->ms_pollinterval));
  214.             break;
  215.         case LS_POLLINTERVAL:
  216.             error = save_value(entry.value,entry.type,&(conf->ls_pollinterval));
  217.             break;
  218.         case IDLETIMER:
  219.             error = save_value(entry.value,entry.type,&(conf->idletimer));
  220.             break;
  221.         case SENDLOGOUT:
  222.             error = save_value(entry.value,entry.type,&(conf->send_logout));
  223.             break;
  224.         case INVALID:
  225.             syslog(LOG_ERR,"invalid or missing value at line %d: %s",lines,linebuff);
  226.             /* we won't treat this as an error */
  227.             break;
  228.         case UNKNOWN:
  229.         default:
  230.             syslog(LOG_ERR,"unknown keyword at line %d: %s",lines,linebuff);
  231.             /* we won't treat this as an error */
  232.             break;
  233.         }
  234.         if (error != 0)
  235.             break;
  236.     } /* while */
  237.  
  238.     fclose(fp);                            /* close config file */
  239.     if (error != 0) {
  240.         syslog(LOG_ERR,"error in configuration file near line %d",lines);
  241.     }
  242.     return(error);
  243. }
  244.  
  245. /*
  246.     skip over "white space" characters at the beginning of the 
  247.     buffer.
  248.  
  249.     returns a pointer to the first non-space character.
  250. */
  251. char *skip_white_space(char *buffer)
  252. {
  253.     while ((*buffer != '\0') && (isspace(*buffer)))
  254.         ++buffer;
  255.     return(buffer);
  256. }
  257.  
  258. /*
  259.     build a hash table from our config file keyword table.
  260.     returns the number of elements in the keyword table or -1 
  261.     on error.
  262. */
  263. int kw_init(struct cf_entry kw_table[])
  264. {
  265.     int e;                                /* elements in kw table */
  266.     int i;                                /* gp int */
  267.     ENTRY node;                            /* hash table entry */
  268.  
  269.     e = 0;
  270.     while ((kw_table[e].name != NULL) && (kw_table[e].name[0] != '\0')) {
  271.         lowercase(kw_table[e].name);    /* fold keyword to lower case */
  272.         ++e;                            /* count elements in table */
  273.     }
  274.     if (hcreate(e) == 0) {
  275.         syslog(LOG_ERR,"kw_init(): unable to create hash table");
  276.         return(-1);
  277.     }
  278.     for (i = 0; i < e; i++) {
  279.         node.key = kw_table[i].name;
  280.         node.data = (char *) &kw_table[i];
  281.         if (hsearch(node,ENTER) == NULL) {
  282.             syslog(LOG_ERR,"kw_init(): failed to build hash table"); 
  283.             hdestroy();
  284.             return(-1);
  285.         }
  286.     }
  287.     return(e);
  288. }
  289.  
  290. /*
  291.     destroy the hash table
  292. */
  293. void kw_done(void)
  294. {
  295.     hdestroy();
  296. }
  297.  
  298. /*
  299.     parse the buffer, which contains an entry from the config 
  300.     file.  if the entry contains a valid keyword, we copy the 
  301.     keyword into cf->name, place the keyword's token into 
  302.     cf->token and place the keyword's token type into cf->type.
  303.     finally, cf->value is set to point to the value of the 
  304.     keyword, within the buffer.
  305.  
  306.     returns the keyword token, UNKNOWN, or INVALID
  307. */
  308. unsigned long parse_entry(char *buffer,struct cf_entry *cf)
  309. {
  310.     char *p;
  311.     char *s;
  312.     int c;
  313.     ENTRY *node;                        /* ptr to hash table entry */
  314.     ENTRY item;                            /* hash table entry */
  315.     struct cf_entry *entry;                /* keyword table entry */
  316.  
  317.     /* remove trailing '\n' from buffer */
  318.     if ((s = strchr(buffer,(int) '\n')) != NULL) {
  319.         *s = '\0';                        /* replace '\n' with '\0' */
  320.     }
  321.  
  322.     /* remove trailing '\r' from buffer */
  323.     if ((s = strchr(buffer,(int) '\r')) != NULL) {
  324.         *s = '\0';                        /* replace '\r' with '\0' */
  325.     }
  326.  
  327.     p = skip_white_space(buffer);
  328.     if (strlen(p) <= 0)                    /* blank line */
  329.         return(BLANKLINE);
  330.     if (strchr("#;",(int) *p) != NULL)    /* comment */
  331.         return(COMMENT);
  332.     s = p;                                /* save ptr to keyword */
  333.  
  334.     while ((*p != '=') && (*p != '\0'))
  335.         ++p;                            /* search for '=' */
  336.     if (*p == '\0')
  337.         return(UNKNOWN);
  338.     /* p now points to '=' */
  339.     if (s == p)                            /* missing keyword */
  340.         return(UNKNOWN);
  341.     memset(cf->name,(int) ' ',SZ_KEYWORD);
  342.     c = p - s;                            /* number of chars */
  343.     if (c >= SZ_KEYWORD)                /* bounds check */
  344.         c = SZ_KEYWORD - 1;
  345.     strncpy(cf->name,s,c);
  346.     cf->name[c] = '\0';                    /* null terminate */
  347.     trimz(cf->name);                    /* remove trailing spaces */
  348.     lowercase(cf->name);                /* fold to lower case */
  349.     syslog(LOG_DEBUG,"keyword \"%s\"",cf->name);
  350.  
  351.     /*
  352.         look up this entry's keyword in the hash table to see if 
  353.         it's valid.  if it is valid, this gives us the keyword type.
  354.         if it is not valid, we use the STRING keyword type.
  355.     */
  356.     item.key = cf->name;
  357.     node = hsearch(item,FIND);
  358.     if (node != NULL) {
  359.         /* node.data can be used as a ptr to struct cf_entry */
  360.         entry = (struct cf_entry *) node->data;
  361.         cf->type = entry->type;            /* keyword token type */
  362.         cf->token = entry->token;        /* keyword token */
  363.     } else {
  364.         cf->type = STRING;                /* keyword token type */
  365.         cf->token = UNKNOWN;            /* keyword token */
  366.     }
  367.     syslog(LOG_DEBUG,"  token type: %s",tokentype2str(cf->type));
  368.     syslog(LOG_DEBUG,"  token 0x%08x", (unsigned int) cf->token);
  369.  
  370.     /*
  371.         parse errors from here on are due to a missing value 
  372.         for the keyword.
  373.     */
  374.     ++p;                                /* skip past equals sign */
  375.     if (*p == '\0')
  376.         return(INVALID);
  377.     s = p;
  378.     p = skip_white_space(s);            /* skip spaces after equals sign */
  379.     if (*p == '\0')
  380.         return(INVALID);
  381.     trimz(p);                            /* remove trailing spaces */
  382.     if (*p == '\0')                        /* shouldn't happen */
  383.         return(INVALID);
  384.  
  385.     cf->value = p;                        /* ptr to value within buffer */
  386.     syslog(LOG_DEBUG,"  value \"%s\"",cf->value);
  387.     return(cf->token);
  388. }
  389.  
  390. char *tokentype2str(int tokentype)
  391. {
  392.     char *str;
  393.  
  394.     switch (tokentype) {
  395.     case LONGVALUE:
  396.         str = "number";
  397.         break;
  398.     case BOOLEAN:
  399.         str = "boolean";
  400.         break;
  401.     case PRINTFSTRING:
  402.         str = "printf string";
  403.         break;
  404.     case STRING:
  405.         str = "string";
  406.         break;
  407.     case VALUE:
  408.         str = "number";
  409.         break;
  410.     default:
  411.         str = "unknown";
  412.         break;
  413.     }
  414.     return(str);
  415. }
  416.  
  417. /*
  418.     fold a string to lower case
  419. */
  420. void lowercase(char *str)
  421. {
  422.     int c;
  423.     char *p;
  424.  
  425.     if (str == NULL) return;
  426.  
  427.     p = str;
  428.     while (*p != '\0') {
  429.         c = tolower((int) *p);
  430.         *p++ = (char) c;
  431.     }
  432. }
  433.  
  434. /*
  435.     save the value associated with a config file entry.
  436.     returns 0 on success, 1 on error.
  437. */
  438. int save_value(char *value,int type,void *target)
  439. {
  440.     int error = 0;
  441.     char **str;
  442.     int *i;
  443.     unsigned long *u;
  444.  
  445.     switch (type) {
  446.     case STRING:
  447.     case PRINTFSTRING:
  448.         str = (char **) target;            /* ptr cast */
  449.         if (*str == NULL)
  450.             *str = malloc(strlen(value) + 1);
  451.         if (*str != NULL) {
  452.             if (type == STRING) {
  453.                 strcpy(*str,value);        /* regular string, copy it */
  454.             } else {                    /* else, printf string */
  455.                 escstrcpy(*str,value);    /* allow character escapes */
  456.             }
  457.         } else {
  458.             syslog(LOG_ERR,"cannot malloc space for string %s",value);
  459.             error = 1;
  460.         }
  461.         break;
  462.     case VALUE:
  463.         i = (int *) target;                /* ptr cast */
  464.         *i = atoi(value);                /* copy value */
  465.         break;
  466.     case BOOLEAN:
  467.         i = (int *) target;                /* ptr cast */
  468.         *i = booleantoi(value);            /* boolean string to integer */
  469.         if (*i == -1) error = 1;
  470.         break;
  471.     case LONGVALUE:
  472.         u = (unsigned long *) target;    /* ptr cast */
  473.         *u = atol(value);                /* copy value */
  474.         break;
  475.     default:                            /* unknown */
  476.         syslog(LOG_ERR,"bad token type passed to save_value(): %d",type);
  477.         error = 1;
  478.         break;
  479.     }
  480.     return(error);
  481. }
  482.  
  483. /*
  484.     convert a "boolean string" value to an integer.  these strings 
  485.     cause a return of 0:
  486.  
  487.         0 off false no
  488.  
  489.     and these cause a return of 1:
  490.  
  491.         1 on true yes
  492.  
  493.     anything else causes a return of -1
  494. */
  495. int booleantoi(char *value)
  496. {
  497.     int ret;                            /* return value */
  498.     int i,j;                            /* gp ints */
  499.     char *str;                            /* ptr to copy of value */
  500.     char *words[2][5] = {
  501.         { "0", "off", "false", "no",  NULL },
  502.         { "1", "on",  "true",  "yes", NULL }
  503.     };
  504.  
  505.     ret = -1;
  506.     str = strdup(value);                /* make a copy of value */
  507.     if (str != NULL) {
  508.         char *p = str;
  509.         while (*p != '\0') {
  510.             *p = tolower(*p);            /* lower case the value */
  511.             p++;
  512.         }
  513.         for (i = 0; i < 2; i++) {
  514.             for (j = 0; words[i][j] != NULL; j++) {
  515.                 if (strcmp(str,words[i][j]) == 0) {
  516.                     ret = i;        /* use 0/1 array index as return value */
  517.                     break;
  518.                 }
  519.             }
  520.             if (ret != -1) break;
  521.         }
  522.         free(str);                        /* release memory */
  523.     }
  524.     return(ret);
  525. }
  526.  
  527. /*
  528.     returns 1 if the specified baud rate is valid.
  529.     otherwise it returns 0.
  530. */
  531. int valid_baudrate(unsigned long speed)
  532. {
  533.     int valid;
  534.  
  535.     switch (speed) {
  536.     case 0:
  537.     case 50:
  538.     case 75:
  539.     case 110:
  540.     case 134:
  541.     case 150:
  542.     case 200:
  543.     case 300:
  544.     case 600:
  545.     case 1200:
  546.     case 2400:
  547.     case 4800:
  548.     case 9600:
  549.     case 19200:
  550.     case 38400:
  551.     case 57600:
  552.     case 115200:
  553.     case 230400:
  554.     case 460800:
  555.         valid = 1;
  556.         break;
  557.     default:
  558.         valid = 0;
  559.         break;
  560.     }
  561.     return(valid);
  562. }
  563.