Coverage Report

Created: 2025-07-12 06:13

/src/opensips/cfg_pp.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * OpenSIPS configuration file pre-processing
3
 *
4
 * Copyright (C) 2019 OpenSIPS Solutions
5
 *
6
 * This file is part of opensips, a free SIP server.
7
 *
8
 * opensips 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 2 of the License, or
11
 * (at your option) any later version
12
 *
13
 * opensips 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, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
21
 */
22
23
#define _WITH_GETLINE
24
25
#include <stdlib.h>
26
#include <stdio.h>
27
#include <fcntl.h>
28
#include <errno.h>
29
#include <libgen.h>
30
#include <sys/stat.h>
31
32
#include "config.h"
33
#include "globals.h"
34
#include "cfg_pp.h"
35
#include "ut.h"
36
37
extern char *finame;
38
extern int startline;
39
extern int column;
40
41
extern FILE *yyin;
42
extern int yyparse();
43
extern int yyrestart(FILE*);
44
#ifdef DEBUG_PARSER
45
extern int yydebug;
46
#endif
47
48
str include_v1 = str_init("include_file");
49
str include_v2 = str_init("import_file");
50
51
str cfgtok_line = str_init("__OSSPP_LINE__");
52
str cfgtok_filebegin = str_init("__OSSPP_FILEBEGIN__");
53
str cfgtok_fileend = str_init("__OSSPP_FILEEND__");
54
55
static int flatten_opensips_cfg(FILE *cfg, const char *cfg_path, str *out);
56
static int exec_preprocessor(FILE *flat_cfg, const char *preproc_cmdline,
57
                             str *out);
58
59
static struct cfg_context *cfg_context_new_file(const char *path);
60
static void cfg_context_reset_all(void);
61
static void cfg_context_append_line(struct cfg_context *con,
62
                                    char *line, int len);
63
64
int parse_opensips_cfg(const char *cfg_file, const char *preproc_cmdline,
65
                              str *ret_buffer)
66
0
{
67
0
  FILE *cfg_stream;
68
0
  str cfg_buf, pp_buf;
69
70
  /* fill missing arguments with the default values*/
71
0
  if (!cfg_file)
72
0
    cfg_file = CFG_FILE;
73
74
0
  if (strlen(cfg_file) == 1 && cfg_file[0] == '-') {
75
0
    cfg_stream = stdin;
76
0
  } else {
77
    /* load config file or die */
78
0
    cfg_stream = fopen(cfg_file, "r");
79
0
    if (!cfg_stream) {
80
0
      LM_ERR("loading config file %s: %s\n", cfg_file,
81
0
             strerror(errno));
82
0
      return -1;
83
0
    }
84
0
  }
85
86
0
  cfg_context_reset_all();
87
88
0
  if (flatten_opensips_cfg(cfg_stream,
89
0
        cfg_stream == stdin ? "stdin" : cfg_file, &cfg_buf) < 0) {
90
0
    LM_ERR("failed to resolve file imports for %s\n", cfg_file);
91
0
    return -1;
92
0
  }
93
94
0
  cfg_stream = fmemopen(cfg_buf.s, cfg_buf.len, "r");
95
0
  if (!cfg_stream) {
96
0
    LM_ERR("failed to open file for flattened cfg buffer\n");
97
0
    goto out_free;
98
0
  }
99
100
0
  if (preproc_cmdline) {
101
0
    if (exec_preprocessor(cfg_stream, preproc_cmdline, &pp_buf) < 0) {
102
0
      LM_ERR("failed to exec preprocessor cmd: '%s'\n", preproc_cmdline);
103
0
      goto out_free;
104
0
    }
105
0
    free(cfg_buf.s);
106
0
    cfg_buf = pp_buf;
107
108
0
    cfg_stream = fmemopen(cfg_buf.s, cfg_buf.len, "r");
109
0
    if (!cfg_stream) {
110
0
      LM_ERR("failed to open file for processed cfg buffer\n");
111
0
      goto out_free;
112
0
    }
113
0
  }
114
115
#ifdef DEBUG_PARSER
116
  /* used for parser debugging */
117
  yydebug = 1;
118
#endif
119
120
  /* parse the config file, prior to this only default values
121
     e.g. for debugging settings will be used */
122
0
  yyin = cfg_stream;
123
0
  yyrestart(yyin);
124
0
  cfg_errors = 0;
125
0
  if (yyparse() != 0 || cfg_errors) {
126
0
    LM_ERR("bad config file (%d errors)\n", cfg_errors);
127
0
    fclose(cfg_stream);
128
0
    goto out_free;
129
0
  }
130
131
0
  fclose(cfg_stream);
132
133
  /* do we have to return the cfg buffer? */
134
0
  if (ret_buffer)
135
0
    *ret_buffer = cfg_buf;
136
0
  else
137
0
    free(cfg_buf.s);
138
139
0
  return 0;
140
141
0
out_free:
142
0
  free(cfg_buf.s);
143
0
  return -1;
144
0
}
145
146
static int extend_cfg_buf(char **buf, int *sz, int *bytes_left, int needed)
147
0
{
148
0
  if (needed < 4096)
149
0
    needed = 4096;
150
151
0
  *buf = realloc(*buf, *sz + needed);
152
0
  if (!*buf) {
153
0
    LM_ERR("failed to extend cfg buf to %d\n", *sz + needed);
154
0
    return -1;
155
0
  }
156
157
0
  *sz += needed;
158
0
  *bytes_left += needed;
159
0
  return 0;
160
0
}
161
162
/* search for '(include|import)_file "filepath"' patterns */
163
int mk_included_file_path(char *line, int line_len, const char *current_dir,
164
                          char **out_path)
165
0
{
166
0
  #define MAX_INCLUDE_FNAME   256
167
0
  static char full_path[MAX_INCLUDE_FNAME];
168
0
  struct stat _;
169
0
  char *p = NULL, enclose = 0;
170
0
  int len1, len2, fplen;
171
172
0
  while (line_len > 0 && is_ws(*line)) {
173
0
    line_len--;
174
0
    line++;
175
0
  }
176
177
0
  if (line_len > include_v1.len &&
178
0
          !memcmp(line, include_v1.s, include_v1.len)) {
179
0
    p = line + include_v1.len;
180
0
    line_len -= include_v1.len;
181
0
  } else if (line_len > include_v2.len &&
182
0
          !memcmp(line, include_v2.s, include_v2.len)) {
183
0
    p = line + include_v2.len;
184
0
    line_len -= include_v2.len;
185
0
  }
186
187
0
  if (!p)
188
0
    return 1;
189
190
0
  while (line_len > 0 && isspace(*p)) {
191
0
    line_len--;
192
0
    p++;
193
0
  }
194
195
0
  if (line_len < 3) // "f"
196
0
    return -1;
197
198
0
  if (*p != '"' && *p != '\'')
199
0
    return -1;
200
201
0
  enclose = *p++;
202
0
  line_len--;
203
204
0
  *out_path = p;
205
206
0
  while (line_len > 0 && *p != enclose) {
207
0
    line_len--;
208
0
    p++;
209
0
  }
210
211
0
  if (line_len == 0 || p - *out_path < 2) // ""_
212
0
    return -1;
213
214
0
  *p = '\0';
215
216
  /* is it a relative-path import? */
217
0
  if (**out_path != '/' && stat(*out_path, &_) < 0) {
218
0
    LM_DBG("%s not found (%d, %s), assuming it's relative to source cfg\n",
219
0
           *out_path, errno, strerror(errno));
220
221
    /* this relative path is not inside the startup dir,
222
     * so maybe it's relative to the importing file */
223
0
    len1 = strlen(current_dir);
224
0
    len2 = strlen(*out_path);
225
226
0
    if (len1 + 1 + len2 + 1 > MAX_INCLUDE_FNAME) {
227
0
      LM_ERR("file path too long (max %d): '%s' + '%s'\n",
228
0
             MAX_INCLUDE_FNAME, current_dir, *out_path);
229
0
      return -1;
230
0
    }
231
232
0
    memcpy(full_path, current_dir, len1);
233
0
    fplen = len1;
234
235
    /* this test can only fail when opensips runs from '/' */
236
0
    if (current_dir[len1 - 1] != '/')
237
0
      full_path[fplen++] = '/';
238
239
0
    memcpy(full_path + fplen, *out_path, len2);
240
0
    fplen += len2;
241
242
0
    full_path[fplen] = '\0';
243
0
    *out_path = full_path;
244
0
  }
245
246
0
  LM_DBG("preparing to include %s\n", *out_path);
247
0
  return 0;
248
0
}
249
250
static struct cfg_context {
251
  const char *path;
252
  const char *dirname; /* useful for relative path includes */
253
  int loc;
254
  char **lines;
255
  int bufsz;
256
  struct cfg_context *next;
257
} *__ccon;
258
259
static void cfg_context_reset_all(void)
260
0
{
261
0
  struct cfg_context *pos = NULL, *it = __ccon;
262
263
0
  while ( it && (it != __ccon || !pos) ) {
264
0
    pos = it;
265
0
    it = it->next;
266
0
    free((char*)pos->path);
267
0
    free((char*)pos->dirname);
268
0
    free(pos->lines);
269
0
    free(pos);
270
0
  };
271
0
  __ccon = NULL;
272
0
}
273
274
static struct cfg_context *cfg_context_new_file(const char *path)
275
0
{
276
0
  struct cfg_context *con, *it;
277
0
  char *cpy;
278
279
0
  for (it = __ccon; it; it = it->next)
280
0
    if (!strcmp(it->path, path))
281
0
      return it;
282
283
0
  con = malloc(sizeof *con);
284
0
  memset(con, 0, sizeof *con);
285
286
0
  con->path = strdup(path);
287
288
0
  cpy = strdup(path);
289
0
  con->dirname = strdup(dirname(cpy));
290
0
  free(cpy);
291
292
0
  con->lines = malloc(32 * sizeof *con->lines);
293
0
  con->bufsz = 32;
294
295
0
  add_last(con, __ccon);
296
0
  return con;
297
0
}
298
299
static void cfg_context_append_line(struct cfg_context *con,
300
                                    char *line, int len)
301
0
{
302
0
  if (con->loc == con->bufsz) {
303
0
    con->bufsz *= 2;
304
0
    con->lines = realloc(con->lines, con->bufsz * sizeof *con->lines);
305
0
    if (!con->lines)
306
0
      return;
307
0
  }
308
309
0
  con->lines[con->loc] = malloc(len + 1);
310
0
  memcpy(con->lines[con->loc], line, len);
311
0
  con->lines[con->loc][len] = '\0';
312
313
0
  con->loc++;
314
0
}
315
316
static int __flatten_opensips_cfg(FILE *cfg, const char *cfg_path,
317
                        char **flattened, int *sz, int *bytes_left, int reclev)
318
0
{
319
0
  FILE *included_cfg;
320
0
  ssize_t line_len;
321
0
  char *line = NULL, *included_cfg_path;
322
0
  unsigned long line_buf_sz = 0;
323
0
  int cfg_path_len = strlen(cfg_path);
324
0
  int line_counter = 1, needed, printed;
325
0
  struct cfg_context *con = NULL;
326
327
0
  if (reclev > 50) {
328
0
    LM_ERR("Maximum import depth reached (50) or "
329
0
           "you have an infinite include_file loop!\n");
330
0
    goto out_err;
331
0
  }
332
333
0
  if (cfg_path_len >= 2048) {
334
0
    LM_ERR("file path too large: %.*s...\n", 2048, cfg_path);
335
0
    goto out_err;
336
0
  }
337
338
0
  con = cfg_context_new_file(cfg_path);
339
0
  needed = cfgtok_filebegin.len + 1 + 1+cfg_path_len+1 + 1 + 1;
340
0
  if (*bytes_left < needed) {
341
0
    if (extend_cfg_buf(flattened, sz, bytes_left, needed) < 0) {
342
0
      LM_ERR("oom\n");
343
0
      goto out_err;
344
0
    }
345
0
  }
346
347
  /* print "start of file" adnotation */
348
0
  printed = snprintf(*flattened + *sz - *bytes_left, *bytes_left, "%.*s \"%.*s\"\n",
349
0
          cfgtok_filebegin.len, cfgtok_filebegin.s, cfg_path_len, cfg_path);
350
0
  *bytes_left -= printed;
351
352
0
  for (;;) {
353
0
    line_len = getline(&line, (size_t*)&line_buf_sz, cfg);
354
0
    if (line_len == -1) {
355
0
      if (ferror(cfg)) {
356
0
        if (errno == EINTR) {
357
0
          continue;
358
0
        } else {
359
0
          LM_ERR("failed to read from cfg file %.*s: %d (%s)\n",
360
0
                 cfg_path_len, cfg_path, errno, strerror(errno));
361
0
          goto out_err;
362
0
        }
363
0
      }
364
365
0
      if (!feof(cfg)) {
366
0
        LM_ERR("unhandled read error in cfg file %.*s: %d (%s)\n",
367
0
               cfg_path_len, cfg_path, errno, strerror(errno));
368
0
        goto out_err;
369
0
      }
370
371
0
      line_len = 0;
372
0
      break;
373
374
0
    } else if (line_len == 0) {
375
0
      continue;
376
0
    }
377
378
    /* fix ending lines with a missing '\n' character ;) */
379
0
    if (feof(cfg)) {
380
0
      if (line[line_len - 1] != '\n') {
381
0
        if (line_buf_sz < line_len + 1) {
382
0
          line = realloc(line, line_len + 1);
383
0
          line_buf_sz = line_len + 1;
384
0
        }
385
386
0
        line[line_len] = '\n';
387
0
        line_len += 1;
388
0
      }
389
0
    }
390
391
    /* finally... we have a line! print "line number" adnotation */
392
0
    needed = cfgtok_line.len + 1 + 10 + 1 + 1;
393
0
    if (*bytes_left < needed) {
394
0
      if (extend_cfg_buf(flattened, sz, bytes_left, needed) < 0) {
395
0
        LM_ERR("oom\n");
396
0
        goto out_err;
397
0
      }
398
0
    }
399
400
0
    printed = snprintf(*flattened + *sz - *bytes_left, *bytes_left,
401
0
                "%.*s %d\n", cfgtok_line.len, cfgtok_line.s, line_counter);
402
0
    line_counter++;
403
0
    *bytes_left -= printed;
404
405
0
    if (con)
406
0
      cfg_context_append_line(con, line, line_len);
407
408
    /* if it's an include, skip printing the line, but do print the file */
409
0
    if (mk_included_file_path(line, line_len, con->dirname, &included_cfg_path) == 0) {
410
0
      included_cfg = fopen(included_cfg_path, "r");
411
0
      if (!included_cfg) {
412
0
        LM_ERR("failed to open %s: %d (%s)\n", included_cfg_path,
413
0
               errno, strerror(errno));
414
0
        goto out_err;
415
0
      }
416
417
0
      included_cfg_path = strdup(included_cfg_path);
418
0
      if (__flatten_opensips_cfg(included_cfg, included_cfg_path,
419
0
                                 flattened, sz, bytes_left, reclev + 1)) {
420
0
        free(included_cfg_path);
421
0
        LM_ERR("failed to flatten cfg file %s\n", cfg_path);
422
0
        goto out_err;
423
0
      }
424
0
      free(included_cfg_path);
425
0
    } else {
426
0
      needed = line_len + 1;
427
0
      if (*bytes_left < needed) {
428
0
        if (extend_cfg_buf(flattened, sz, bytes_left, needed) < 0) {
429
0
          LM_ERR("oom\n");
430
0
          goto out_err;
431
0
        }
432
0
      }
433
434
0
      printed = snprintf(*flattened + *sz - *bytes_left, *bytes_left,
435
0
              "%.*s", (int)line_len, line);
436
0
      *bytes_left -= printed;
437
0
    }
438
0
  }
439
440
0
  free(line);
441
0
  line = NULL;
442
443
0
  needed = cfgtok_fileend.len + 1 + 1;
444
0
  if (*bytes_left < needed) {
445
0
    if (extend_cfg_buf(flattened, sz, bytes_left, needed) < 0) {
446
0
      LM_ERR("oom\n");
447
0
      goto out_err;
448
0
    }
449
0
  }
450
451
  /* print "end of file" adnotation */
452
0
  printed = snprintf(*flattened + *sz - *bytes_left, *bytes_left, "%.*s\n",
453
0
          cfgtok_fileend.len, cfgtok_fileend.s);
454
0
  *bytes_left -= printed;
455
456
0
  fclose(cfg);
457
0
  return 0;
458
459
0
out_err:
460
0
  if (line)
461
0
    free(line);
462
0
  fclose(cfg);
463
0
  return -1;
464
0
}
465
466
/*
467
 * - flatten any recursive includes into one big resulting file
468
 * - adnotate each line of the final file
469
 * - close given FILE * and return a buffer corresponding to the new file
470
 */
471
static int flatten_opensips_cfg(FILE *cfg, const char *cfg_path, str *out)
472
0
{
473
0
  int sz = 0, bytes_left = 0;
474
0
  char *flattened = NULL;
475
476
0
  if (__flatten_opensips_cfg(cfg, cfg_path, &flattened, &sz, &bytes_left, 0)) {
477
0
    LM_ERR("failed to flatten cfg file %s\n", cfg_path);
478
0
    return -1;
479
0
  }
480
481
0
  out->s = flattened;
482
0
  out->len = sz - bytes_left;
483
484
0
  if (strlen(out->s) != out->len) {
485
0
    LM_BUG("preprocessed buffer check failed (%lu vs. %d)",
486
0
           (unsigned long)strlen(out->s), out->len);
487
0
    LM_ERR("either this is a bug or your script contains '\\0' chars, "
488
0
            "which are obviously NOT allowed!\n");
489
0
    return -1;
490
0
  }
491
492
0
  return 0;
493
0
}
494
495
static char *cfg_include_stack[CFG_MAX_INCLUDE_DEPTH];
496
static char **cfg_include_stackp;
497
int cfg_push(const str *cfg_file)
498
0
{
499
0
  if (!cfg_include_stackp) {
500
0
    cfg_include_stackp = cfg_include_stack;
501
0
  } else if (cfg_include_stackp - cfg_include_stack + 1 >=
502
0
             CFG_MAX_INCLUDE_DEPTH) {
503
0
    LM_ERR("max nested cfg files reached! (%d)\n", CFG_MAX_INCLUDE_DEPTH);
504
0
    return -1;
505
0
  } else {
506
0
    cfg_include_stackp++;
507
0
  }
508
509
0
  *cfg_include_stackp = malloc(cfg_file->len + 1);
510
0
  if (!*cfg_include_stackp) {
511
0
    LM_ERR("oom\n");
512
0
    return -1;
513
0
  }
514
0
  memcpy(*cfg_include_stackp, cfg_file->s, cfg_file->len);
515
0
  (*cfg_include_stackp)[cfg_file->len] = '\0';
516
517
0
  finame = *cfg_include_stackp;
518
0
  startline = 1;
519
0
  column = 1;
520
0
  return 0;
521
0
}
522
523
int cfg_pop(void)
524
0
{
525
0
  if (!cfg_include_stackp) {
526
0
    LM_ERR("no more files to pop!\n");
527
0
    return -1;
528
0
  }
529
530
  /* the file path MUST NOT be freed, as the lexer and parser work in tandem,
531
   * so by this point, there are plenty of structures referencing it */
532
533
0
  if (cfg_include_stackp == cfg_include_stack) {
534
0
    cfg_include_stackp = NULL;
535
0
  } else {
536
0
    cfg_include_stackp--;
537
0
    finame = *cfg_include_stackp;
538
0
    column = 1;
539
0
  }
540
541
0
  return 0;
542
0
}
543
544
void _cfg_dump_context(const char *file, int line, int colstart, int colend,
545
                       int run_once)
546
0
{
547
0
  static int called_before;
548
0
  struct cfg_context *con;
549
0
  int i, iter = 1, len;
550
0
  char *p, *end, *wsbuf, *wb, *hiline;
551
552
0
  if (!file)
553
0
    return;
554
555
0
  for (con = __ccon; con; con = con->next)
556
0
    if (!strcmp(con->path, file))
557
0
      break;
558
559
0
  if (!con || !con->lines[0] || (run_once && called_before))
560
0
    return;
561
562
0
  called_before = 1;
563
564
  /* 2 lines above */
565
0
  if (line >= 3) {
566
0
    startline = line - 2;
567
0
    iter += 2;
568
0
  } else {
569
0
    startline = 1;
570
0
    iter += line - 1;
571
0
  }
572
573
0
  for (i = startline - 1; iter > 0; i++, iter--)
574
0
    LM_GEN1(L_CRIT, "%s", con->lines[i]);
575
576
  /* error indicator line */
577
0
  len = strlen(con->lines[i-1]);
578
0
  wsbuf = malloc(len + 1);
579
0
  if (!wsbuf) {
580
0
    LM_ERR("oom\n");
581
0
    return;
582
0
  }
583
584
0
  wb = wsbuf;
585
0
  for (p = con->lines[i-1], end = p + len; p < end && is_ws(*p); p++)
586
0
    *wb++ = *p;
587
0
  *wb = '\0';
588
589
0
  if (colend < colstart) {
590
0
    hiline = NULL;
591
0
  } else {
592
0
    hiline = malloc(colend - colstart);
593
0
    if (!hiline) {
594
0
      LM_ERR("oom\n");
595
0
      free(wsbuf);
596
0
      return;
597
0
    }
598
0
    memset(hiline, '~', colend - colstart);
599
0
  }
600
601
0
  LM_GEN1(L_CRIT, "%s^%.*s\n", wsbuf,
602
0
          colend >= colstart ? colend - colstart : 0, hiline);
603
0
  free(hiline);
604
0
  free(wsbuf);
605
606
  /* 2 lines below */
607
0
  if (line <= con->loc - 2)
608
0
    iter = 2;
609
0
  else
610
0
    iter = line <= con->loc ? con->loc - line : 0;
611
612
0
  for (; iter > 0; i++, iter--)
613
0
    LM_GEN1(L_CRIT, "%s", con->lines[i]);
614
0
}
615
616
void cfg_dump_backtrace(void)
617
0
{
618
0
  static int called_before;
619
0
  char **it;
620
0
  int frame = 0;
621
622
0
  if (called_before || !cfg_include_stackp)
623
0
    return;
624
625
0
  called_before = 1;
626
0
  LM_GEN1(L_CRIT, "Traceback (last included file at the bottom):\n");
627
0
  for (it = cfg_include_stack; it <= cfg_include_stackp; it++)
628
0
    LM_GEN1(L_CRIT, "%2d. %s\n", frame++, *it);
629
0
}
630
631
static int exec_preprocessor(FILE *flat_cfg, const char *preproc_cmdline,
632
                             str *out)
633
0
{
634
0
  int parent_w[2], parent_r[2], cfgsz = 0, cfgbufsz = 0;
635
0
  char chunk[1024], *cfgbuf = NULL;
636
0
  ssize_t written, bytes;
637
0
  size_t bytes2write;
638
0
  char *p, *tok, *cmd, **argv = NULL, *pp_binary = NULL;
639
0
  int argv_len = 0, flags, have_input = 0, done_writing = 0;
640
641
0
  if (strlen(preproc_cmdline) == 0) {
642
0
    LM_ERR("preprocessor command (-p) is an empty string!\n");
643
0
    goto out_err;
644
0
  }
645
646
0
  if (pipe(parent_w) != 0 || pipe(parent_r) != 0) {
647
0
    LM_ERR("failed to create pipe: %d (%s)\n", errno, strerror(errno));
648
0
    goto out_err;
649
0
  }
650
651
  /* fork a data-hungry preprocessor beast! (a.k.a. some tiny sed) */
652
0
  if (fork() == 0) {
653
0
    close(parent_w[1]);
654
0
    if (dup2(parent_w[0], STDIN_FILENO) < 0) {
655
0
      LM_ERR("dup2 failed with: %d (%s)\n", errno, strerror(errno));
656
0
      exit(-1);
657
0
    }
658
0
    close(parent_w[0]);
659
660
0
    close(parent_r[0]);
661
0
    if (dup2(parent_r[1], STDOUT_FILENO) < 0) {
662
0
      LM_ERR("dup2 failed with: %d (%s)\n", errno, strerror(errno));
663
0
      exit(-1);
664
0
    }
665
0
    close(parent_w[1]);
666
667
0
    for (cmd = strdup(preproc_cmdline); ; cmd = NULL) {
668
0
      tok = strtok(cmd, " \t\r\n");
669
0
      if (!tok)
670
0
        break;
671
672
0
      if (!pp_binary)
673
0
        pp_binary = tok;
674
675
0
      argv = realloc(argv, (argv_len + 1) * sizeof *argv);
676
0
      argv[argv_len++] = tok;
677
0
    }
678
679
0
    argv = realloc(argv, (argv_len + 1) * sizeof *argv);
680
0
    argv[argv_len++] = NULL;
681
682
0
    if (pp_binary) {
683
0
      execvp(pp_binary, argv);
684
0
      LM_ERR("failed to exec preprocessor '%s': %d (%s)\n",
685
0
           preproc_cmdline, errno, strerror(errno));
686
0
    } else
687
0
      LM_ERR("no binary to run: '%s'\n", preproc_cmdline);
688
689
0
    exit(-1);
690
0
  }
691
692
0
  close(parent_w[0]);
693
0
  close(parent_r[1]);
694
695
0
  flags = fcntl(parent_w[1], F_GETFL);
696
0
  if (flags == -1) {
697
0
    LM_ERR("fcntl GET 1 failed: %d - %s\n", errno, strerror(errno));
698
0
    goto out_err_pipes;
699
0
  }
700
701
0
  if (fcntl(parent_w[1], F_SETFL, flags | O_NONBLOCK) == -1) {
702
0
    LM_ERR("fcntl SET 1 failed: %d - %s\n", errno, strerror(errno));
703
0
    goto out_err_pipes;
704
0
  }
705
706
0
  flags = fcntl(parent_r[0], F_GETFL);
707
0
  if (flags == -1) {
708
0
    LM_ERR("fcntl GET 2 failed: %d - %s\n", errno, strerror(errno));
709
0
    goto out_err_pipes;
710
0
  }
711
712
0
  if (fcntl(parent_r[0], F_SETFL, flags | O_NONBLOCK) == -1) {
713
0
    LM_ERR("fcntl SET 2 failed: %d - %s\n", errno, strerror(errno));
714
0
    goto out_err_pipes;
715
0
  }
716
717
  /* communicate with the preprocessor using alternating,
718
   * non-blocking writes and reads */
719
0
  while (!done_writing) {
720
    /* fetch bytes to write */
721
0
    bytes2write = fread(chunk, 1, 1024, flat_cfg);
722
0
    if (ferror(flat_cfg)) {
723
0
      LM_ERR("failed to read from flat cfg: %d (%s)\n",
724
0
             errno, strerror(errno));
725
0
      goto out_err_pipes;
726
0
    }
727
728
0
    if (bytes2write == 0) {
729
0
      done_writing = 1;
730
0
      close(parent_w[1]); /* signal EOF to the outside process! */
731
0
    } else {
732
0
      have_input = 1;
733
0
    }
734
735
0
    p = chunk;
736
737
0
send_bytes:
738
    /* write phase */
739
0
    while (bytes2write > 0) {
740
0
      written = write(parent_w[1], p, bytes2write);
741
0
      if (written < 0) {
742
0
        if (errno == EAGAIN || errno == EWOULDBLOCK)
743
0
          break;
744
0
        else if (errno == EINTR)
745
0
          continue;
746
0
        else
747
0
          goto out_err_pipes;
748
0
      }
749
750
0
      bytes2write -= written;
751
0
      p += written;
752
0
    }
753
754
    /* read phase */
755
0
    for (;;) {
756
0
      if (cfgsz + 1024 > cfgbufsz) {
757
0
        if (cfgbufsz == 0)
758
0
          cfgbufsz = 4096;
759
0
        else
760
0
          cfgbufsz *= 2;
761
762
0
        cfgbuf = realloc(cfgbuf, cfgbufsz);
763
0
        if (!cfgbuf) {
764
0
          LM_ERR("oom, failed to build config buffer\n");
765
0
          goto out_err;
766
0
        }
767
0
      }
768
769
0
      bytes = read(parent_r[0], cfgbuf + cfgsz, 1024);
770
0
      if (bytes < 0) {
771
0
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
772
0
          if (done_writing) {
773
0
            usleep(10);
774
0
            continue;
775
0
          } else {
776
0
            break;
777
0
          }
778
0
        } else if (errno == EINTR) {
779
0
          continue;
780
0
        } else {
781
0
          goto out_err_pipes;
782
0
        }
783
0
      } else if (bytes == 0) {
784
0
        bytes2write = 0;
785
0
        done_writing = 1;
786
0
        break;
787
0
      }
788
789
0
      cfgsz += bytes;
790
0
    }
791
792
0
    if (bytes2write > 0)
793
0
      goto send_bytes;
794
0
  }
795
796
0
  if (have_input && cfgsz == 0)
797
0
    LM_WARN("no output from the preprocessor! "
798
0
        "Does it print to standard output?\n");
799
800
0
  fclose(flat_cfg);
801
0
  close(parent_r[0]);
802
803
0
  out->s = cfgbuf;
804
0
  out->len = cfgsz;
805
0
  return 0;
806
807
0
out_err_pipes:
808
0
  close(parent_w[1]);
809
0
  close(parent_r[0]);
810
0
out_err:
811
0
  fclose(flat_cfg);
812
0
  free(cfgbuf);
813
0
  return -1;
814
0
}
815
816
int eatback_pp_tok(struct str_buf *buf)
817
0
{
818
0
  char *p;
819
0
  str last_line;
820
821
0
  if (!buf->s)
822
0
    return 0;
823
824
0
  for (p = buf->crt - 1; p >= buf->s; p--)
825
0
    if (*p == '\n') {
826
0
      p++;
827
0
      goto match_pp_tok;
828
0
    }
829
830
0
  return 0;
831
832
0
match_pp_tok:
833
0
  last_line.s = p;
834
0
  last_line.len = buf->crt - p;
835
836
0
  if (last_line.len < 0) {
837
0
    LM_BUG("negative line len");
838
0
    return 0;
839
0
  }
840
841
0
  if (last_line.len >= cfgtok_line.len &&
842
0
    !memcmp(last_line.s, cfgtok_line.s, cfgtok_line.len))
843
0
    goto clear_last_line;
844
845
0
  if (last_line.len >= cfgtok_filebegin.len &&
846
0
    !memcmp(last_line.s, cfgtok_filebegin.s, cfgtok_filebegin.len))
847
0
    goto clear_last_line;
848
849
0
  if (last_line.len >= cfgtok_fileend.len &&
850
0
    !memcmp(last_line.s, cfgtok_fileend.s, cfgtok_fileend.len))
851
0
    goto clear_last_line;
852
853
  /* don't touch anything, this is an actual script line! */
854
0
  return 0;
855
856
0
clear_last_line:
857
0
  LM_DBG("clearing pp token line: '%.*s'\n", (int)(buf->crt - p), p);
858
0
  buf->left += buf->crt - p;
859
0
  *p = '\0';
860
0
  buf->crt = p;
861
0
  return 1;
862
0
}