4
0

parse_ini.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /*****************************************************************************
  2. *
  3. * Monitoring Plugins parse_ini library
  4. *
  5. * License: GPL
  6. * Copyright (c) 2007 Monitoring Plugins Development Team
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. *
  22. *****************************************************************************/
  23. #include "common.h"
  24. #include "idpriv.h"
  25. #include "utils_base.h"
  26. #include "parse_ini.h"
  27. #include <ctype.h>
  28. #include <sys/types.h>
  29. #include <sys/stat.h>
  30. #include <unistd.h>
  31. /* np_ini_info contains the result of parsing a "locator" in the format
  32. * [stanza_name][@config_filename] (check_foo@/etc/foo.ini, for example)
  33. */
  34. typedef struct {
  35. char *file;
  36. char *stanza;
  37. } np_ini_info;
  38. static char *default_ini_file_names[] = {
  39. "monitoring-plugins.ini",
  40. "plugins.ini",
  41. "nagios-plugins.ini",
  42. NULL
  43. };
  44. static char *default_ini_path_names[] = {
  45. "/usr/local/etc/monitoring-plugins/monitoring-plugins.ini",
  46. "/usr/local/etc/monitoring-plugins.ini",
  47. "/etc/monitoring-plugins/monitoring-plugins.ini",
  48. "/etc/monitoring-plugins.ini",
  49. /* deprecated path names (for backward compatibility): */
  50. "/etc/nagios/plugins.ini",
  51. "/usr/local/nagios/etc/plugins.ini",
  52. "/usr/local/etc/nagios/plugins.ini",
  53. "/etc/opt/nagios/plugins.ini",
  54. "/etc/nagios-plugins.ini",
  55. "/usr/local/etc/nagios-plugins.ini",
  56. "/etc/opt/nagios-plugins.ini",
  57. NULL
  58. };
  59. /* eat all characters from a FILE pointer until n is encountered */
  60. #define GOBBLE_TO(f, c, n) do { (c)=fgetc((f)); } while((c)!=EOF && (c)!=(n))
  61. /* internal function that returns the constructed defaults options */
  62. static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts);
  63. /* internal function that converts a single line into options format */
  64. static int add_option(FILE *f, np_arg_list **optlst);
  65. /* internal functions to find default file */
  66. static char *default_file(void);
  67. static char *default_file_in_path(void);
  68. /*
  69. * Parse_locator decomposes a string of the form
  70. * [stanza][@filename]
  71. * into its seperate parts.
  72. */
  73. static void
  74. parse_locator(const char *locator, const char *def_stanza, np_ini_info *i)
  75. {
  76. size_t locator_len = 0, stanza_len = 0;
  77. /* if locator is NULL we'll use default values */
  78. if (locator != NULL) {
  79. locator_len = strlen(locator);
  80. stanza_len = strcspn(locator, "@");
  81. }
  82. /* if a non-default stanza is provided */
  83. if (stanza_len > 0) {
  84. i->stanza = malloc(sizeof(char) * (stanza_len + 1));
  85. strncpy(i->stanza, locator, stanza_len);
  86. i->stanza[stanza_len] = '\0';
  87. } else /* otherwise we use the default stanza */
  88. i->stanza = strdup(def_stanza);
  89. if (i->stanza == NULL)
  90. die(STATE_UNKNOWN, _("malloc() failed!\n"));
  91. /* check whether there's an @file part */
  92. i->file = stanza_len == locator_len
  93. ? default_file()
  94. : strdup(&(locator[stanza_len + 1]));
  95. if (i->file == NULL || i->file[0] == '\0')
  96. die(STATE_UNKNOWN,
  97. _("Cannot find config file in any standard location.\n"));
  98. }
  99. /*
  100. * This is the externally visible function used by extra_opts.
  101. */
  102. np_arg_list *
  103. np_get_defaults(const char *locator, const char *default_section)
  104. {
  105. FILE *inifile = NULL;
  106. np_arg_list *defaults = NULL;
  107. np_ini_info i;
  108. int is_suid_plugin = mp_suid();
  109. if (is_suid_plugin && idpriv_temp_drop() == -1)
  110. die(STATE_UNKNOWN, _("Cannot drop privileges: %s\n"),
  111. strerror(errno));
  112. parse_locator(locator, default_section, &i);
  113. inifile = strcmp(i.file, "-") == 0 ? stdin : fopen(i.file, "r");
  114. if (inifile == NULL)
  115. die(STATE_UNKNOWN, _("Can't read config file: %s\n"),
  116. strerror(errno));
  117. if (read_defaults(inifile, i.stanza, &defaults) == FALSE)
  118. die(STATE_UNKNOWN,
  119. _("Invalid section '%s' in config file '%s'\n"), i.stanza,
  120. i.file);
  121. free(i.file);
  122. if (inifile != stdin)
  123. fclose(inifile);
  124. free(i.stanza);
  125. if (is_suid_plugin && idpriv_temp_restore() == -1)
  126. die(STATE_UNKNOWN, _("Cannot restore privileges: %s\n"),
  127. strerror(errno));
  128. return defaults;
  129. }
  130. /*
  131. * The read_defaults() function is where the meat of the parsing takes place.
  132. *
  133. * Note that this may be called by a setuid binary, so we need to
  134. * be extra careful about user-supplied input (i.e. avoiding possible
  135. * format string vulnerabilities, etc).
  136. */
  137. static int
  138. read_defaults(FILE *f, const char *stanza, np_arg_list **opts)
  139. {
  140. int c, status = FALSE;
  141. size_t i, stanza_len;
  142. enum { NOSTANZA, WRONGSTANZA, RIGHTSTANZA } stanzastate = NOSTANZA;
  143. stanza_len = strlen(stanza);
  144. /* our little stanza-parsing state machine */
  145. while ((c = fgetc(f)) != EOF) {
  146. /* gobble up leading whitespace */
  147. if (isspace(c))
  148. continue;
  149. switch (c) {
  150. /* globble up coment lines */
  151. case ';':
  152. case '#':
  153. GOBBLE_TO(f, c, '\n');
  154. break;
  155. /* start of a stanza, check to see if it matches */
  156. case '[':
  157. stanzastate = WRONGSTANZA;
  158. for (i = 0; i < stanza_len; i++) {
  159. c = fgetc(f);
  160. /* strip leading whitespace */
  161. if (i == 0)
  162. for (; isspace(c); c = fgetc(f))
  163. continue;
  164. /* nope, read to the end of the line */
  165. if (c != stanza[i]) {
  166. GOBBLE_TO(f, c, '\n');
  167. break;
  168. }
  169. }
  170. /* if it matched up to here and the next char is ']'... */
  171. if (i == stanza_len) {
  172. c = fgetc(f);
  173. /* strip trailing whitespace */
  174. for (; isspace(c); c = fgetc(f))
  175. continue;
  176. if (c == ']')
  177. stanzastate = RIGHTSTANZA;
  178. }
  179. break;
  180. /* otherwise, we're in the body of a stanza or a parse error */
  181. default:
  182. switch (stanzastate) {
  183. /* we never found the start of the first stanza, so
  184. * we're dealing with a config error
  185. */
  186. case NOSTANZA:
  187. die(STATE_UNKNOWN, "%s\n",
  188. _("Config file error"));
  189. /* we're in a stanza, but for a different plugin */
  190. case WRONGSTANZA:
  191. GOBBLE_TO(f, c, '\n');
  192. break;
  193. /* okay, this is where we start taking the config */
  194. case RIGHTSTANZA:
  195. ungetc(c, f);
  196. if (add_option(f, opts)) {
  197. die(STATE_UNKNOWN, "%s\n",
  198. _("Config file error"));
  199. }
  200. status = TRUE;
  201. break;
  202. }
  203. break;
  204. }
  205. }
  206. return status;
  207. }
  208. /*
  209. * Read one line of input in the format
  210. * ^option[[:space:]]*(=[[:space:]]*value)?
  211. * and create it as a cmdline argument
  212. * --option[=value]
  213. * appending it to the linked list optbuf.
  214. */
  215. static int
  216. add_option(FILE *f, np_arg_list **optlst)
  217. {
  218. np_arg_list *opttmp = *optlst, *optnew;
  219. char *linebuf = NULL, *lineend = NULL, *optptr = NULL, *optend = NULL;
  220. char *eqptr = NULL, *valptr = NULL, *valend = NULL;
  221. short done_reading = 0, equals = 0, value = 0;
  222. size_t cfg_len = 0, read_sz = 8, linebuf_sz = 0, read_pos = 0;
  223. size_t opt_len = 0, val_len = 0;
  224. /* read one line from the file */
  225. while (!done_reading) {
  226. /* grow if necessary */
  227. if (linebuf == NULL || read_pos + read_sz >= linebuf_sz) {
  228. linebuf_sz = linebuf_sz > 0 ? linebuf_sz << 1 : read_sz;
  229. linebuf = realloc(linebuf, linebuf_sz);
  230. if (linebuf == NULL)
  231. die(STATE_UNKNOWN, _("malloc() failed!\n"));
  232. }
  233. if (fgets(&linebuf[read_pos], (int)read_sz, f) == NULL)
  234. done_reading = 1;
  235. else {
  236. read_pos = strlen(linebuf);
  237. if (linebuf[read_pos - 1] == '\n') {
  238. linebuf[--read_pos] = '\0';
  239. done_reading = 1;
  240. }
  241. }
  242. }
  243. lineend = &linebuf[read_pos];
  244. /* all that to read one line, isn't C fun? :) now comes the parsing :/ */
  245. /* skip leading whitespace */
  246. for (optptr = linebuf; optptr < lineend && isspace(*optptr); optptr++)
  247. continue;
  248. /* continue to '=' or EOL, watching for spaces that might precede it */
  249. for (eqptr = optptr; eqptr < lineend && *eqptr != '='; eqptr++) {
  250. if (isspace(*eqptr) && optend == NULL)
  251. optend = eqptr;
  252. else
  253. optend = NULL;
  254. }
  255. if (optend == NULL)
  256. optend = eqptr;
  257. --optend;
  258. /* ^[[:space:]]*=foo is a syntax error */
  259. if (optptr == eqptr)
  260. die(STATE_UNKNOWN, "%s\n", _("Config file error"));
  261. /* continue from '=' to start of value or EOL */
  262. for (valptr = eqptr + 1; valptr < lineend && isspace(*valptr);
  263. valptr++)
  264. continue;
  265. /* continue to the end of value */
  266. for (valend = valptr; valend < lineend; valend++)
  267. continue;
  268. --valend;
  269. /* finally trim off trailing spaces */
  270. for (; isspace(*valend); valend--)
  271. continue;
  272. /* calculate the length of "--foo" */
  273. opt_len = (size_t)(1 + optend - optptr);
  274. /* 1-character params needs only one dash */
  275. if (opt_len == 1)
  276. cfg_len = 1 + (opt_len);
  277. else
  278. cfg_len = 2 + (opt_len);
  279. /* if valptr<lineend then we have to also allocate space for "=bar" */
  280. if (valptr < lineend) {
  281. equals = value = 1;
  282. val_len = (size_t)(1 + valend - valptr);
  283. cfg_len += 1 + val_len;
  284. }
  285. /* if valptr==valend then we have "=" but no "bar" */
  286. else if (valptr == lineend) {
  287. equals = 1;
  288. cfg_len += 1;
  289. }
  290. /* a line with no equal sign isn't valid */
  291. if (equals == 0)
  292. die(STATE_UNKNOWN, "%s\n", _("Config file error"));
  293. /* okay, now we have all the info we need, so we create a new np_arg_list
  294. * element and set the argument...
  295. */
  296. optnew = malloc(sizeof(np_arg_list));
  297. optnew->next = NULL;
  298. read_pos = 0;
  299. optnew->arg = malloc(cfg_len + 1);
  300. /* 1-character params needs only one dash */
  301. if (opt_len == 1) {
  302. strncpy(&optnew->arg[read_pos], "-", 1);
  303. read_pos += 1;
  304. } else {
  305. strncpy(&optnew->arg[read_pos], "--", 2);
  306. read_pos += 2;
  307. }
  308. strncpy(&optnew->arg[read_pos], optptr, opt_len);
  309. read_pos += opt_len;
  310. if (value) {
  311. optnew->arg[read_pos++] = '=';
  312. strncpy(&optnew->arg[read_pos], valptr, val_len);
  313. read_pos += val_len;
  314. }
  315. optnew->arg[read_pos] = '\0';
  316. /* ...and put that to the end of the list */
  317. if (*optlst == NULL)
  318. *optlst = optnew;
  319. else {
  320. while (opttmp->next != NULL)
  321. opttmp = opttmp->next;
  322. opttmp->next = optnew;
  323. }
  324. free(linebuf);
  325. return 0;
  326. }
  327. static char *
  328. default_file(void)
  329. {
  330. char **p, *ini_file;
  331. if ((ini_file = getenv("MP_CONFIG_FILE")) != NULL ||
  332. (ini_file = default_file_in_path()) != NULL)
  333. return ini_file;
  334. for (p = default_ini_path_names; *p != NULL; p++)
  335. if (access(*p, F_OK) == 0)
  336. return *p;
  337. return NULL;
  338. }
  339. static char *
  340. default_file_in_path(void)
  341. {
  342. char *config_path, **file;
  343. char *dir, *ini_file, *tokens;
  344. if ((config_path = getenv("NAGIOS_CONFIG_PATH")) == NULL)
  345. return NULL;
  346. /* shall we spit out a warning that NAGIOS_CONFIG_PATH is deprecated? */
  347. if ((tokens = strdup(config_path)) == NULL)
  348. die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory"));
  349. for (dir = strtok(tokens, ":"); dir != NULL; dir = strtok(NULL, ":")) {
  350. for (file = default_ini_file_names; *file != NULL; file++) {
  351. if ((asprintf(&ini_file, "%s/%s", dir, *file)) < 0)
  352. die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory"));
  353. if (access(ini_file, F_OK) == 0) {
  354. free(tokens);
  355. return ini_file;
  356. }
  357. }
  358. }
  359. free(tokens);
  360. return NULL;
  361. }