config.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. /*
  2. * config.c
  3. * Functions for the manipulation of configuration files.
  4. *
  5. * Matej Pfajfar <mp292@cam.ac.uk>
  6. */
  7. /*
  8. * Changes :
  9. * $Log$
  10. * Revision 1.5 2002/07/15 19:05:12 montrose
  11. * bug-fix. poptReadDefaultOptions() should find and load ~/.<cmd>rc files now.
  12. *
  13. * Revision 1.4 2002/07/09 19:51:41 montrose
  14. * Miscellaneous bug fixes / activated "make check" for src/or
  15. *
  16. * Revision 1.3 2002/07/03 16:31:22 montrose
  17. * Added getoptions() and made minor adjustment to poptReadDefaultOptions()
  18. *
  19. * Revision 1.2 2002/06/28 18:14:55 montrose
  20. * Added poptReadOptions() and poptReadDefaultOptions()
  21. *
  22. * Revision 1.1.1.1 2002/06/26 22:45:50 arma
  23. * initial commit: current code
  24. *
  25. * Revision 1.7 2002/04/02 14:27:11 badbytes
  26. * Final finishes.
  27. *
  28. * Revision 1.6 2002/01/27 19:23:03 mp292
  29. * Fixed a bug in parameter checking.
  30. *
  31. * Revision 1.5 2002/01/26 18:42:15 mp292
  32. * Reviewed according to Secure-Programs-HOWTO.
  33. *
  34. * Revision 1.4 2002/01/21 21:07:56 mp292
  35. * Parameter checking was missing in some functions.
  36. *
  37. * Revision 1.3 2001/12/07 09:38:03 badbytes
  38. * Tested.
  39. *
  40. * Revision 1.2 2001/12/06 15:43:50 badbytes
  41. * config.c compiles. Proceeding to test it.
  42. *
  43. * Revision 1.1 2001/11/22 01:20:27 mp292
  44. * Functions for dealing with configuration files.
  45. *
  46. *
  47. */
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <string.h>
  51. #include <errno.h>
  52. #include <ctype.h>
  53. #include <popt.h>
  54. #include <limits.h>
  55. #include "config.h"
  56. #include "log.h"
  57. /* open configuration file for reading */
  58. FILE *open_config(const unsigned char *filename)
  59. {
  60. FILE *f;
  61. if (filename) /* non-NULL filename */
  62. {
  63. if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) == strlen(filename)) /* filename consists of legal characters only */
  64. {
  65. f = fopen(filename, "r");
  66. return f;
  67. } /* filename consists of legal characters only */
  68. else /* illegal values in filename */
  69. {
  70. return NULL;
  71. } /* illegal values in filename */
  72. } /* non-NULL filename */
  73. else /* NULL filename */
  74. return NULL;
  75. }
  76. /* close configuration file */
  77. int close_config(FILE *f)
  78. {
  79. int retval = 0;
  80. if (f) /* valid file descriptor */
  81. {
  82. retval = fclose(f);
  83. return retval;
  84. } /* valid file descriptor */
  85. else
  86. return -1;
  87. }
  88. /* parse the config file and obtain the required option values */
  89. int parse_config(FILE *f, config_opt_t *option)
  90. {
  91. unsigned char keyword[CONFIG_KEYWORD_MAXLEN+1]; /* for storing the option keyword */
  92. unsigned char *buffer = NULL; /* option value */
  93. size_t buflen = 0;
  94. char *errtest = NULL; /* used for testing correctness of strtol() etc. */
  95. unsigned int i_keyword = 0; /* current position within keyword */
  96. unsigned int i_buf = 0; /* current position within buffer */
  97. char c=0; /* input char */
  98. unsigned int state=0; /* internal state
  99. * 0 - trying to find a keyword
  100. * 1 - reading a keyword
  101. * 2 - keyword read and recognized, looking for the option value
  102. * 3 - reading the option value
  103. * 4 - option value read
  104. * 5 - inside a comment
  105. */
  106. int retval=0; /* return value */
  107. int lineno=1; /* current line number */
  108. int curopt=-1; /* current option, as an indexed in config_opt_t */
  109. int i;
  110. if ( (f==NULL) || (option==NULL) ) /* invalid parameters */
  111. return -1;
  112. fseek(f,0,SEEK_SET); /* make sure we start at the beginning of file */
  113. for (;;) /* infinite loop */
  114. {
  115. c = getc(f);
  116. if ((c == '\n') || (c == EOF))
  117. {
  118. if (state == 1) /* reading a keyboard */
  119. {
  120. log(LOG_ERR,"Error parsing the configuration file on line %d.", lineno);
  121. i_keyword = 0;
  122. state = 0;
  123. retval = -1;
  124. break;
  125. } /* reading a keyboard */
  126. else if (state == 2) /* keyword read and recognized */
  127. {
  128. log(LOG_ERR,"Error parsing option %s on line %d.",option[curopt].keyword, lineno);
  129. i_keyword = 0;
  130. state = 0;
  131. option[curopt].err=-1;
  132. retval = -1;
  133. break;
  134. } /* keyboard read and recognized */
  135. else if (state == 3) /* reading the option value */
  136. {
  137. buffer[i_buf++] = 0; /* add NULL character to terminate the string */
  138. state = 4;
  139. /* conversion and copying the value into config_opt_t is done later on */
  140. } /* reading the option value */
  141. else if (state == 5) /* reached end of comment */
  142. state = 0;
  143. if (c == EOF)
  144. {
  145. log(LOG_DEBUG,"parse_config() : Reached eof on line %d.",lineno);
  146. break;
  147. }
  148. else
  149. {
  150. log(LOG_DEBUG,"parse_config() : Reached eol on line %d.", lineno);
  151. lineno++;
  152. }
  153. }
  154. else if ( (state==0) && (c == '#') ) /* lines beginning with # are ignored */
  155. {
  156. log(LOG_DEBUG,"parse_config() : Line %d begins with #.",lineno);
  157. state = 5;
  158. }
  159. else if ( (state==0) && (isspace(c)) ) /* leading whitespace is ignored */
  160. ;
  161. else if ( (state==1) && (isspace(c)) ) /* have apparently read in all of the keyword */
  162. {
  163. keyword[i_keyword++] = 0;
  164. curopt = -1;
  165. for (i=0;option[i].keyword != NULL;i++) /* try and identify the keyword */
  166. {
  167. if (!strncmp(keyword,option[i].keyword,CONFIG_KEYWORD_MAXLEN))
  168. {
  169. curopt = i;
  170. break;
  171. }
  172. } /* try and identify the keyword */
  173. if (curopt == -1) /* can't recognise the keyword */
  174. {
  175. log(LOG_ERR,"Error parsing the configuration file. Cannot recognize keyword %s on line %d.",keyword,lineno);
  176. retval=-1;
  177. break;
  178. }
  179. else
  180. state = 2;
  181. }
  182. else if ( (state==2) && (isspace(c)) ) /* whitespace separating keyword and value is ignored */
  183. ;
  184. else if ( (state==3) && (isspace(c)) ) /* have apparently finished reading the option value */
  185. {
  186. buffer[i_buf++]=0;
  187. state = 4;
  188. }
  189. else /* all other characters */
  190. {
  191. if (state == 0) /* first character of the keyword */
  192. {
  193. log(LOG_DEBUG, "parse_config() : %c is the start of a keyword on line %d.",c,lineno);
  194. state = 1;
  195. i_keyword = 0;
  196. keyword[i_keyword++] = c;
  197. }
  198. else if (state == 1) /* keep on reading the keyword */
  199. {
  200. log(LOG_DEBUG,"parse_config() : %c is a character in the keyword on line %d.",c,lineno);
  201. if (i_keyword < CONFIG_KEYWORD_MAXLEN) /* check for buffer overflow */
  202. keyword[i_keyword++] = c;
  203. else
  204. {
  205. log(LOG_ERR,"Error parsing the configuration file. Keyword on line %d exceeds %d characters.",lineno,CONFIG_KEYWORD_MAXLEN);
  206. retval=-1;
  207. break;
  208. }
  209. }
  210. else if (state == 2) /* first character of the value */
  211. {
  212. log(LOG_DEBUG,"parse_config() : %c is the first character of the option value on line %d.",c,lineno);
  213. state = 3;
  214. i_buf=0;
  215. buflen = CONFIG_VALUE_MAXLEN+1; /* allocate memory for the value buffer */
  216. buffer = (char *)malloc(buflen);
  217. if (!buffer)
  218. {
  219. log(LOG_ERR,"Could not allocate memory.");
  220. retval=-1;
  221. break;
  222. } else
  223. buffer[i_buf++]=c;
  224. }
  225. else if (state == 3) /* keep on reading the value */
  226. {
  227. log(LOG_DEBUG,"parse_config() : %c is a character in the value of the keyword on line %d.",c,lineno);
  228. if (i_buf >= buflen)
  229. {
  230. log(LOG_ERR,"Length of keyword value on line %u exceeds the length limit (%u).",lineno, CONFIG_VALUE_MAXLEN);
  231. retval=-1;
  232. break;
  233. }
  234. buffer[i_buf++]=c;
  235. }
  236. else if (state == 5)
  237. ; /* character is part of a comment, skip */
  238. else /* unexpected error */
  239. {
  240. log(LOG_ERR,"Unexpected error while parsing the configuration file.");
  241. log(LOG_DEBUG,"parse_config() : Encountered a non-delimiter character while not in states 0,1,2 or 3!");
  242. break;
  243. }
  244. }
  245. if (state==4) /* convert the value of the option to the appropriate type and write into OPT */
  246. {
  247. switch(option[curopt].r_type) /* consider each type separately */
  248. {
  249. case CONFIG_TYPE_STRING:
  250. /* resize the buffer to fit the data exactly */
  251. buffer = (char *)realloc(buffer,i_buf);
  252. if (!buffer)
  253. {
  254. log(LOG_ERR,"Could not allocate memory.");
  255. return -1;
  256. }
  257. option[curopt].r.str = buffer;
  258. option[curopt].err = 1;
  259. break;
  260. case CONFIG_TYPE_CHAR:
  261. option[curopt].r.c = *buffer;
  262. option[curopt].err = 1;
  263. break;
  264. case CONFIG_TYPE_INT:
  265. errtest = NULL;
  266. option[curopt].r.i = (int)strtol(buffer,&errtest,0);
  267. if ((unsigned char *)errtest == buffer)
  268. {
  269. log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
  270. option[curopt].err = -1;
  271. if (buffer)
  272. free(buffer);
  273. return -1;
  274. }
  275. else
  276. option[curopt].err = 1;
  277. break;
  278. case CONFIG_TYPE_LONG:
  279. errtest = NULL;
  280. option[curopt].r.l = strtol(buffer,&errtest,0);
  281. if ((unsigned char *)errtest == buffer)
  282. {
  283. log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
  284. option[curopt].err = -1;
  285. if (buffer)
  286. free(buffer);
  287. return -1;
  288. }
  289. else
  290. option[curopt].err = 1;
  291. break;
  292. case CONFIG_TYPE_DOUBLE:
  293. errtest = NULL;
  294. option[curopt].r.d = strtod(buffer,&errtest);
  295. if ((unsigned char *)errtest == buffer)
  296. {
  297. log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
  298. option[curopt].err = -1;
  299. if (buffer)
  300. free(buffer);
  301. return -1;
  302. }
  303. else
  304. option[curopt].err = 1;
  305. break;
  306. default: /* unexpected type */
  307. log(LOG_ERR, "Error parsing configuration file. Unrecognized option type!");
  308. if (buffer)
  309. free(buffer);
  310. return -1;
  311. }
  312. /* clean up */
  313. if (option[curopt].r_type != CONFIG_TYPE_STRING)
  314. {
  315. if (buffer)
  316. free(buffer);
  317. buflen=0;
  318. }
  319. state = 0;
  320. curopt = -1;
  321. i_buf=0;
  322. i_keyword=0;
  323. }
  324. } /* infinite loop */
  325. return retval;
  326. }
  327. int poptReadOptions(poptContext optCon, const unsigned char *fname)
  328. /**
  329. poptReadOptions reads popt-style options from the specified filename.
  330. RETURN VALUE: INT_MIN = problem opening config file, else standard poptGetNextOpt() return value
  331. **/
  332. {
  333. FILE *fp;
  334. int argc, c, n;
  335. char **argv;
  336. char line[1024];
  337. line[0] = line[1] = '-'; /* prepend expected long name option flag */
  338. fp = fopen(fname,"r");
  339. if ( fp == NULL ) return INT_MIN;
  340. c = 0;
  341. while ( c >= -1 )
  342. {
  343. if ( fscanf(fp,"%*[ \n]%n",&n) == EOF ) break; /* eat leading whitespace */
  344. if ( fscanf(fp, "%[^\n]",&line[2]) == EOF ) break; /* read a line */
  345. switch ( line[2] )
  346. {
  347. case '#': /* comments begin with this */
  348. case '[': /* section header. ignore for now. maybe do something special in future version... */
  349. continue;/* ignore */
  350. default: /* we got a bite, lets reel it in now */
  351. poptParseArgvString(line,&argc,(const char ***)&argv); /* Argv-ify what we found */
  352. poptStuffArgs(optCon,(const char **)argv); /* stuff new arguments so they can be interpreted */
  353. free(argv); /* free storage allocated by poptParseArgvString */
  354. c = poptGetNextOpt(optCon); /* interpret option read from config file */
  355. }
  356. }
  357. fclose(fp);
  358. return c;
  359. }
  360. int poptReadDefaultOptions(const char *cmd, poptContext optCon)
  361. /**
  362. reads popt-style options from /etc/<cmd>rc and ~/.<cmd>rc
  363. RETURN VALUE: same as poptReadOptions()
  364. **/
  365. {
  366. char fname[256];
  367. int c;
  368. sprintf(fname,"/etc/%src",cmd);
  369. c = poptReadOptions(optCon,fname);
  370. if ( c == INT_MIN || c >= -1 )
  371. {
  372. sprintf(fname,"%s/.%src",getenv("HOME"),cmd);
  373. c = poptReadOptions(optCon,fname);
  374. }
  375. return (c == INT_MIN) ? -1 : c;
  376. }