1 /*
2  * Copyright (c) 2001-2010 Willem Dijkstra
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *    - Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *    - Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  */
30 
31 #include <sys/stat.h>
32 
33 #include <string.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 
37 #include "conf.h"
38 #include "data.h"
39 #include "error.h"
40 #include "lex.h"
41 #include "net.h"
42 #include "readconf.h"
43 #include "xmalloc.h"
44 
45 __BEGIN_DECLS
46 int read_mux(struct muxlist * mul, struct lex *);
47 int read_source(struct sourcelist * sol, struct lex *, int);
48 int insert_filename(char *, int, int, char *);
49 __END_DECLS
50 
51 const char *default_symux_port = SYMUX_PORT;
52 
53 int
insert_filename(char * path,int maxlen,int type,char * args)54 insert_filename(char *path, int maxlen, int type, char *args)
55 {
56     int i, result;
57     char *ts;
58     char *ta;
59     char *fta;
60 
61     fta = ts = ta = NULL;
62 
63     switch (type) {
64     case MT_CPU:
65         ts = "cpu";
66         ta = args;
67         break;
68     case MT_CPUIOW:
69         ts = "cpuiow";
70         ta = args;
71         break;
72     case MT_DF:
73         ts = "df_";
74         ta = args;
75         break;
76     case MT_IF1:  /* rrd stores 64bits, if1 and if2 are equivalent */
77     case MT_IF2:
78         ts = "if_";
79         ta = args;
80         break;
81     case MT_IO1:
82         ts = "io1_";
83         ta = args;
84         break;
85     case MT_IO2:
86         ts = "io_";
87         ta = args;
88         break;
89     case MT_MEM1: /* rrd stores 64bits, mem1 and mem2 are equivalent */
90     case MT_MEM2:
91         ts = "mem";
92         ta = "";
93         break;
94     case MT_PF:
95         ts = "pf";
96         ta = "";
97         break;
98     case MT_PFQ:
99         ts  = "pfq_";
100         ta = args;
101         break;
102     case MT_MBUF:
103         ts = "mbuf";
104         ta = "";
105         break;
106     case MT_DEBUG:
107         ts = "debug";
108         ta = "";
109         break;
110     case MT_PROC:
111         ts = "proc_";
112         ta = args;
113         break;
114     case MT_SENSOR:
115         ts = "sensor_";
116         ta = args;
117         break;
118     case MT_SMART:
119         ts = "smart_";
120         ta = args;
121         break;
122     case MT_LOAD:
123         ts = "load";
124         ta = "";
125         break;
126     case MT_FLUKSO:
127         ts = "flukso_";
128         ta = args;
129         break;
130 
131     default:
132         warning("%.200s:%d: internal error: type (%d) unknown",
133                 __FILE__, __LINE__, type);
134         return 0;
135     }
136 
137     /* ensure that no '/' remain in args */
138     fta = xstrdup(ta);
139 
140     for (i = 0; i < strlen(fta); i++) {
141         if (fta[i] == '/') fta[i] = '_';
142     }
143 
144     if ((snprintf(path, maxlen, "/%s%s.rrd", ts, fta)) >= maxlen) {
145         result = 0;
146     } else {
147         result = 1;
148     }
149 
150     xfree(fta);
151     return result;
152 }
153 /* parse "'mux' (ip4addr | ip6addr | hostname) [['port' | ',' portnumber]" */
154 int
read_mux(struct muxlist * mul,struct lex * l)155 read_mux(struct muxlist * mul, struct lex * l)
156 {
157     char muxname[_POSIX2_LINE_MAX];
158     struct mux *mux;
159 
160     if (!SLIST_EMPTY(mul)) {
161         warning("%.200s:%d: only one mux statement allowed",
162                 l->filename, l->cline);
163         return 0;
164     }
165 
166     lex_nexttoken(l);
167     if (!getip(l->token, AF_INET) && !getip(l->token, AF_INET6)) {
168         warning("%.200s:%d: could not resolve '%s'",
169                 l->filename, l->cline, l->token);
170         return 0;
171     }
172 
173     mux = add_mux(mul, SYMON_UNKMUX);
174     mux->addr = xstrdup((const char *) &res_host);
175 
176     /* check for port statement */
177     lex_nexttoken(l);
178 
179     if (l->op == LXT_PORT || l->op == LXT_COMMA)
180         lex_nexttoken(l);
181 
182     if (l->type != LXY_NUMBER) {
183         lex_ungettoken(l);
184         mux->port = xstrdup(default_symux_port);
185     } else {
186         mux->port = xstrdup((const char *) l->token);
187     }
188 
189     bzero(&muxname, sizeof(muxname));
190     snprintf(&muxname[0], sizeof(muxname), "%s %s", mux->addr, mux->port);
191 
192     if (rename_mux(mul, mux, muxname) == NULL)
193         fatal("%s:%d: internal error: dual mux", __FILE__, __LINE__);
194 
195     return 1;
196 }
197 /* parse "'source' host '{' accept-stmst [write-stmts] [datadir-stmts] '}'" */
198 int
read_source(struct sourcelist * sol,struct lex * l,int filecheck)199 read_source(struct sourcelist * sol, struct lex * l, int filecheck)
200 {
201     struct source *source;
202     struct stream *stream;
203     struct stat sb;
204     char path[_POSIX2_LINE_MAX];
205     char sn[_POSIX2_LINE_MAX];
206     char sa[_POSIX2_LINE_MAX];
207     int st;
208     int pc;
209     int fd;
210 
211     /* get hostname */
212     lex_nexttoken(l);
213     if (!getip(l->token, AF_INET) && !getip(l->token, AF_INET6)) {
214         warning("%.200s:%d: could not resolve '%s'",
215                 l->filename, l->cline, l->token);
216         return 0;
217     }
218 
219     source = add_source(sol, res_host);
220 
221     EXPECT(l, LXT_BEGIN);
222     while (lex_nexttoken(l)) {
223         switch (l->op) {
224             /* accept { cpu(x), ... } */
225         case LXT_ACCEPT:
226             EXPECT(l, LXT_BEGIN);
227             while (lex_nexttoken(l) && l->op != LXT_END) {
228                 switch (l->op) {
229                 case LXT_CPU:
230                 case LXT_CPUIOW:
231                 case LXT_DEBUG:
232                 case LXT_DF:
233                 case LXT_IF1:
234                 case LXT_IF:
235                 case LXT_IO1:
236                 case LXT_IO:
237                 case LXT_MBUF:
238                 case LXT_MEM1:
239                 case LXT_MEM:
240                 case LXT_PF:
241                 case LXT_PFQ:
242                 case LXT_PROC:
243                 case LXT_SENSOR:
244                 case LXT_SMART:
245                 case LXT_LOAD:
246                 case LXT_FLUKSO:
247                     st = token2type(l->op);
248                     strncpy(&sn[0], l->token, _POSIX2_LINE_MAX);
249 
250                     /* parse arg */
251                     lex_nexttoken(l);
252                     if (l->op == LXT_OPEN) {
253                         lex_nexttoken(l);
254                         if (l->op == LXT_CLOSE) {
255                             parse_error(l, "<stream argument>");
256                             return 0;
257                         }
258 
259                         strncpy(&sa[0], l->token, _POSIX2_LINE_MAX);
260                         lex_nexttoken(l);
261 
262                         if (l->op != LXT_CLOSE) {
263                             parse_error(l, ")");
264                             return 0;
265                         }
266                     } else {
267                         lex_ungettoken(l);
268                         sa[0] = '\0';
269                     }
270 
271                     if (strlen(sa) > (SYMON_PS_ARGLENV2 - 1)) {
272                         warning("%.200s:%d: argument '%.200s' too long for network format, "
273                                 "will accept initial " SYMON_PS_ARGLENSTRV2 " chars only",
274                                 l->filename, l->cline, sa);
275                         sa[SYMON_PS_ARGLENV2 - 1] = '\0';
276                     }
277 
278                     if ((stream = add_source_stream(source, st, sa)) == NULL) {
279                         warning("%.200s:%d: stream %.200s(%.200s) redefined",
280                                 l->filename, l->cline, sn, sa);
281                         return 0;
282                     }
283 
284                     break;      /* LXT_resource */
285                 case LXT_COMMA:
286                     break;
287                 default:
288                     parse_error(l, "{cpu|cpuiow|df|if|if1|io|io1|mem|mem1|pf|pfq|mbuf|debug|proc|sensor|smart|load|flukso}");
289                     return 0;
290 
291                     break;
292                 }
293             }
294             break;              /* LXT_ACCEPT */
295             /* datadir "path" */
296         case LXT_DATADIR:
297             lex_nexttoken(l);
298             /* is path absolute */
299             if (l->token && l->token[0] != '/') {
300                 warning("%.200s:%d: datadir path '%.200s' is not absolute",
301                         l->filename, l->cline, l->token);
302                 return 0;
303             }
304 
305             if (filecheck) {
306                 /* make sure that directory exists */
307                 bzero(&sb, sizeof(struct stat));
308 
309                 if (stat(l->token, &sb) == 0) {
310                     if (!(sb.st_mode & S_IFDIR)) {
311                         warning("%.200s:%d: datadir path '%.200s' is not a directory",
312                                 l->filename, l->cline, l->token);
313                         return 0;
314                     }
315                 } else {
316                     warning("%.200s:%d: could not stat datadir path '%.200s'",
317                             l->filename, l->cline, l->token);
318                     return 0;
319                 }
320             }
321 
322             strncpy(&path[0], l->token, _POSIX2_LINE_MAX);
323             path[_POSIX2_LINE_MAX - 1] = '\0';
324 
325             pc = strlen(path);
326 
327             if (path[pc - 1] == '/') {
328                 path[pc - 1] = '\0';
329                 pc--;
330             }
331 
332             /* add path to empty streams */
333             SLIST_FOREACH(stream, &source->sl, streams) {
334                 if (stream->file == NULL) {
335                     if (!(insert_filename(&path[pc],
336                                           _POSIX2_LINE_MAX - pc,
337                                           stream->type,
338                                           stream->arg))) {
339                         if (stream->arg && strlen(stream->arg)) {
340                             warning("%.200s:%d: failed to construct stream "
341                                     "%.200s(%.200s) filename using datadir '%.200s'",
342                                     l->filename, l->cline,
343                                     type2str(stream->type),
344                                     stream->arg, l->token);
345                         } else {
346                             warning("%.200s:%d: failed to construct stream "
347                                     "%.200s) filename using datadir '%.200s'",
348                                     l->filename, l->cline,
349                                     type2str(stream->type),
350                                     l->token);
351                         }
352                         return 0;
353                     }
354 
355                     if (filecheck) {
356                         /* try filename */
357                         if ((fd = open(path, O_RDWR | O_NONBLOCK, 0)) == -1) {
358                             /* warn, but allow */
359                             warning("%.200s:%d: file '%.200s', guessed by datadir,  cannot be opened",
360                                     l->filename, l->cline, path);
361                         } else {
362                             close(fd);
363                             stream->file = xstrdup(path);
364                         }
365                     } else {
366                         stream->file = xstrdup(path);
367                     }
368                 }
369             }
370             break;              /* LXT_DATADIR */
371             /* write cpu(0) in "filename" */
372         case LXT_WRITE:
373             lex_nexttoken(l);
374             switch (l->op) {
375             case LXT_CPU:
376             case LXT_CPUIOW:
377             case LXT_DEBUG:
378             case LXT_DF:
379             case LXT_IF1:
380             case LXT_IF:
381             case LXT_IO1:
382             case LXT_IO:
383             case LXT_MBUF:
384             case LXT_MEM1:
385             case LXT_MEM:
386             case LXT_PF:
387             case LXT_PFQ:
388             case LXT_PROC:
389             case LXT_SENSOR:
390             case LXT_SMART:
391             case LXT_LOAD:
392             case LXT_FLUKSO:
393                 st = token2type(l->op);
394                 strncpy(&sn[0], l->token, _POSIX2_LINE_MAX);
395 
396                 /* parse arg */
397                 lex_nexttoken(l);
398                 if (l->op == LXT_OPEN) {
399                     lex_nexttoken(l);
400                     if (l->op == LXT_CLOSE) {
401                         parse_error(l, "<stream argument>");
402                         return 0;
403                     }
404 
405                     strncpy(&sa[0], l->token, _POSIX2_LINE_MAX);
406                     lex_nexttoken(l);
407                     if (l->op != LXT_CLOSE) {
408                         parse_error(l, ")");
409                         return 0;
410                     }
411                 } else {
412                     lex_ungettoken(l);
413                     sa[0] = '\0';
414                 }
415 
416                 EXPECT(l, LXT_IN);
417 
418                 lex_nexttoken(l);
419 
420                 if ((stream = find_source_stream(source, st, sa)) == NULL) {
421                     if (strlen(sa)) {
422                         warning("%.200s:%d: stream %.200s(%.200s) is not accepted for %.200s",
423                                 l->filename, l->cline, sn, sa, source->addr);
424                         return 0;
425                     } else {
426                         warning("%.200s:%d: stream %.200s is not accepted for %.200s",
427                                 l->filename, l->cline, sn, source->addr);
428                         return 0;
429                     }
430                 } else {
431                     if (filecheck) {
432                         /* try filename */
433                         if ((fd = open(l->token, O_RDWR | O_NONBLOCK, 0)) == -1) {
434                             warning("%.200s:%d: file '%.200s' cannot be opened",
435                                     l->filename, l->cline, l->token);
436                             return 0;
437                         } else {
438                             close(fd);
439 
440                             if (stream->file != NULL) {
441                                 warning("%.200s:%d: file '%.200s' overwrites previous definition '%.200s'",
442                                         l->filename, l->cline, l->token, stream->file);
443                                 xfree(stream->file);
444                             }
445 
446                             stream->file = xstrdup(l->token);
447                         }
448                     } else {
449                         stream->file = xstrdup(l->token);
450                     }
451                 }
452                 break;          /* LXT_resource */
453             default:
454                 parse_error(l, "{cpu|cpuiow|df|if|if1|io|io1|mem|mem1|pf|pfq|mbuf|debug|proc|sensor|smart|load|flukso}");
455                 return 0;
456                 break;
457             }
458             break;              /* LXT_WRITE */
459         case LXT_END:
460             return 1;
461         default:
462             parse_error(l, "accept|datadir|write");
463             return 0;
464         }
465     }
466 
467     warning("%.200s:%d: missing close brace on source statement",
468             l->filename, l->cline);
469 
470     return 0;
471 }
472 /* Read symux.conf */
473 int
read_config_file(struct muxlist * mul,const char * filename,int filechecks)474 read_config_file(struct muxlist * mul, const char *filename, int filechecks)
475 {
476     struct lex *l;
477     struct source *source;
478     struct stream *stream;
479     struct mux *mux;
480     struct sourcelist sol;
481     SLIST_INIT(mul);
482     SLIST_INIT(&sol);
483 
484     if ((l = open_lex(filename)) == NULL)
485         return 0;
486 
487     while (lex_nexttoken(l)) {
488         /* expecting keyword now */
489         switch (l->op) {
490         case LXT_MUX:
491             if (!read_mux(mul, l)) {
492                 free_sourcelist(&sol);
493                 return 0;
494             }
495             break;
496         case LXT_SOURCE:
497             if (!read_source(&sol, l, filechecks)) {
498                 free_sourcelist(&sol);
499                 return 0;
500             }
501             break;
502         default:
503             parse_error(l, "mux|source");
504             free_sourcelist(&sol);
505             return 0;
506             break;
507         }
508     }
509 
510     /* sanity checks */
511     if (SLIST_EMPTY(mul)) {
512         free_sourcelist(&sol);
513         warning("%.200s: no mux statement seen",
514                 l->filename);
515         return 0;
516     } else {
517         mux = SLIST_FIRST(mul);
518         mux->sol = sol;
519         if (strncmp(SYMON_UNKMUX, mux->name, sizeof(SYMON_UNKMUX)) == 0) {
520             /* mux was not initialised for some reason */
521             return 0;
522         }
523     }
524 
525     if (SLIST_EMPTY(&sol)) {
526         warning("%.200s: no source section seen",
527                 l->filename);
528         return 0;
529     } else {
530         SLIST_FOREACH(source, &sol, sources) {
531             if (SLIST_EMPTY(&source->sl)) {
532                 warning("%.200s: no streams accepted for source '%.200s'",
533                         l->filename, source->addr);
534                 return 0;
535             } else {
536                 SLIST_FOREACH(stream, &source->sl, streams) {
537                     if (stream->file == NULL) {
538                         /* warn, but allow */
539                         warning("%.200s: no filename specified for stream '%.200s(%.200s)' in source '%.200s'",
540                                 l->filename, type2str(stream->type), stream->arg, source->addr);
541                     }
542                 }
543             }
544         }
545     }
546 
547     close_lex(l);
548 
549     return 1;
550 }
551