1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *  Written by: Eric Bollengier, December MMXIII
21  */
22 
23 #define OUTPUT_C                /* control dll export in output.h */
24 #include "output.h"
25 #include "plugins.h"
26 
27 /* use new output (lowercase, no special char) */
28 #define OF_USE_NEW_OUTPUT  1
29 
parse_options(const char * options)30 void OutputWriter::parse_options(const char *options)
31 {
32    int nb=0;
33    const char *p = options;
34    while (*p) {
35       nb=0;
36 
37       switch(*p) {
38       case 'C':
39          flags = 0;
40          set_time_format(OW_DEFAULT_TIMEFORMAT);
41          set_separator(OW_DEFAULT_SEPARATOR);
42          break;
43 
44       case 'S':                 /* object separator */
45          while(isdigit(*(p+1))) {
46             nb = nb*10 + (*(++p) - '0');
47          }
48          if (isascii(nb)) {
49             set_object_separator((char) nb);
50          }
51          break;
52 
53       case 'o':
54          flags |= OF_USE_NEW_OUTPUT; /* lowercase and only isalpha */
55          break;
56 
57       case 't':                 /* Time format */
58          if (isdigit(*(p+1))) {
59             nb = (*(++p) - '0');
60             set_time_format((OutputTimeType) nb);
61          }
62          break;
63 
64       case 's':                 /* Separator */
65          while(isdigit(*(p+1))) {
66             nb = nb*10 + (*(++p) - '0');
67          }
68          if (isascii(nb)) {
69             set_separator((char) nb);
70          }
71          break;
72       default:
73          break;
74       }
75       p++;
76    }
77 }
78 
get_options(char * dest)79 char *OutputWriter::get_options(char *dest)
80 {
81    char ed1[50];
82    *dest = *ed1 = 0;
83    if (separator != OW_DEFAULT_SEPARATOR) {
84       snprintf(dest, 50, "s%d", (int)separator);
85    }
86    if (object_separator) {
87       snprintf(ed1, sizeof(ed1), "S%d", (int) object_separator);
88       bstrncat(dest, ed1, sizeof(ed1));
89    }
90    if (timeformat != OW_DEFAULT_TIMEFORMAT) {
91       snprintf(ed1, sizeof(ed1), "t%d", (int) timeformat);
92       bstrncat(dest, ed1, sizeof(ed1));
93    }
94    if (flags & OF_USE_NEW_OUTPUT) {
95       bstrncat(dest, "o", 1);
96    }
97    return dest;
98 }
99 
get_buf(bool append)100 void OutputWriter::get_buf(bool append)
101 {
102    if (!buf) {
103       buf = get_pool_memory(PM_MESSAGE);
104       *buf = 0;
105 
106    } else if (!append) {
107       *buf = 0;
108    }
109 }
110 
start_group(const char * name,bool append)111 char *OutputWriter::start_group(const char *name, bool append)
112 {
113    get_buf(append);
114    pm_strcat(buf, name);
115    pm_strcat(buf, ":\n");
116    return buf;
117 }
118 
end_group(bool append)119 char *OutputWriter::end_group(bool append)
120 {
121    get_buf(append);
122    pm_strcat(buf, "\n");
123 
124    return buf;
125 }
126 
start_list(const char * name,bool append)127 char *OutputWriter::start_list(const char *name, bool append)
128 {
129    get_buf(append);
130    pm_strcat(buf, name);
131    pm_strcat(buf, ": [\n");
132    return buf;
133 }
134 
end_list(bool append)135 char *OutputWriter::end_list(bool append)
136 {
137    get_buf(append);
138    pm_strcat(buf, "]\n");
139 
140    return buf;
141 }
142 
143 /* Usage:
144  *   get_output(
145  *       OT_STRING,   "name",       "value",
146  *       OT_PINT32,   "age",        10,
147  *       OT_TIME,     "birth-date", 1120202002,
148  *       OT_PINT64,   "weight",     100,
149  *       OT_END);
150  *
151  *
152  *  "name=value\nage=10\nbirt-date=2012-01-12 10:20:00\nweight=100\n"
153  *
154  */
get_output(OutputType first,...)155 char *OutputWriter::get_output(OutputType first, ...)
156 {
157    char    *ret;
158    va_list  arg_ptr;
159 
160    get_buf(true);               /* Append to the current string */
161 
162    va_start(arg_ptr, first);
163    ret = get_output(arg_ptr, &buf, first);
164    va_end(arg_ptr);
165 
166    return ret;
167 }
168 
169 /* Usage:
170  *   get_output(&out,
171  *       OT_STRING,   "name",       "value",
172  *       OT_PINT32,   "age",        10,
173  *       OT_TIME,     "birth-date", 1120202002,
174  *       OT_PINT64,   "weight",     100,
175  *       OT_END);
176  *
177  *
178  *  "name=value\nage=10\nbirt-date=2012-01-12 10:20:00\nweight=100\n"
179  *
180  */
get_output(POOLMEM ** out,OutputType first,...)181 char *OutputWriter::get_output(POOLMEM **out, OutputType first, ...)
182 {
183    va_list arg_ptr;
184    char *ret;
185 
186    va_start(arg_ptr, first);
187    ret = get_output(arg_ptr, out, first);
188    va_end(arg_ptr);
189 
190    return ret;
191 }
192 
get_output(va_list ap,POOLMEM ** out,OutputType first)193 char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
194 {
195    char       ed1[MAX_TIME_LENGTH];
196    int        i;
197    int64_t    i64;
198    uint64_t   u64;
199    int32_t    i32;
200    double     d;
201    btime_t    bt;
202    char      *s = NULL, *k = NULL;
203    alist     *lst;
204    Plugin    *plug;
205    POOLMEM   *tmp2 = get_pool_memory(PM_FNAME);
206    POOLMEM   *tmp = get_pool_memory(PM_FNAME);
207    OutputType val = first;
208 
209    while (val != OT_END) {
210 
211       *tmp = 0;
212 
213       /* Some arguments are not using a keyword */
214       switch (val) {
215       case OT_END:
216       case OT_START_OBJ:
217       case OT_END_OBJ:
218       case OT_CLEAR:
219          break;
220 
221       default:
222          k = va_arg(ap, char *);          /* Get the variable name */
223 
224          /* If requested, we can put the keyword in lowercase */
225          if (flags & OF_USE_NEW_OUTPUT) {
226             tmp2 = check_pool_memory_size(tmp2, strlen(k)+1);
227             for (i = 0; k[i] ; i++) {
228                if (isalnum(k[i])) {
229                   tmp2[i] = tolower(k[i]);
230                } else {
231                   tmp2[i] = '_';
232                }
233             }
234             tmp2[i] = 0;
235             k = tmp2;
236          }
237       }
238 
239       //Dmsg2(000, "%d - %s\n", val, k);
240 
241       switch (val) {
242       case OT_ALIST_STR:
243          lst = va_arg(ap, alist *);
244          i = 0;
245          Mmsg(tmp, "%s=", k);
246          if (lst) {
247             foreach_alist(s, lst) {
248                if (i++ > 0) {
249                   pm_strcat(tmp, ",");
250                }
251                pm_strcat(tmp, s);
252             }
253          }
254          pm_strcat(tmp, separator_str);
255          break;
256       case OT_PLUGINS:
257          lst = va_arg(ap, alist *);
258          i = 0;
259          pm_strcpy(tmp, "plugins=");
260          if (lst) {
261             foreach_alist(plug, lst) {
262                if (i++ > 0) {
263                   pm_strcat(tmp, ",");
264                }
265                pm_strcat(tmp, plug->file);
266             }
267          }
268          pm_strcat(tmp, separator_str);
269          break;
270       case OT_RATIO:
271          d = va_arg(ap, double);
272          Mmsg(tmp, "%s=%.2f%c", k, d, separator);
273          break;
274 
275       case OT_STRING:
276          s = va_arg(ap, char *);
277          Mmsg(tmp, "%s=%s%c", k, NPRTB(s), separator) ;
278          break;
279 
280       case OT_INT32:
281          i32 = va_arg(ap, int32_t);
282          Mmsg(tmp, "%s=%d%c", k, i32, separator);
283          break;
284 
285       case OT_UTIME:
286       case OT_BTIME:
287          if (val == OT_UTIME) {
288             bt = va_arg(ap, utime_t);
289          } else {
290             bt = va_arg(ap, btime_t);
291          }
292          switch (timeformat) {
293          case OTT_TIME_NC: /* Formatted time for user display: dd-Mon hh:mm */
294             bstrftime_ny(ed1, sizeof(ed1), bt);
295             break;
296 
297          case OTT_TIME_UNIX:         /* unix timestamp */
298             bsnprintf(ed1, sizeof(ed1), "%lld", bt);
299             break;
300 
301          case OTT_TIME_ISO:
302             /* wanted fallback */
303          default:
304             bstrutime(ed1, sizeof(ed1), bt);
305          }
306          Mmsg(tmp, "%s_epoch=%lld%c%s=%s%c", k, bt, separator, k, ed1, separator);
307          break;
308 
309       case OT_DURATION:
310          bt = va_arg(ap, utime_t);
311          bstrutime(ed1, sizeof(ed1), bt);
312          Mmsg(tmp, "%s=%lld%c%s_str=%s%c", k, bt, separator, k, edit_utime(bt, ed1, sizeof(ed1)), separator);
313          break;
314 
315       case OT_SIZE:
316       case OT_INT64:
317          i64 = va_arg(ap, int64_t);
318          Mmsg(tmp, "%s=%lld%c", k, i64, separator);
319          break;
320 
321       case OT_PINT64:
322          u64 = va_arg(ap, uint64_t);
323          Mmsg(tmp, "%s=%llu%c", k, u64, separator);
324          break;
325 
326       case OT_INT:
327          i64 = va_arg(ap, int);
328          Mmsg(tmp, "%s=%lld%c", k, i64, separator);
329          break;
330 
331       case OT_JOBLEVEL:
332       case OT_JOBTYPE:
333       case OT_JOBSTATUS:
334          i32 = va_arg(ap, int32_t);
335          if (i32 == 0) {
336             Mmsg(tmp, "%s=%c", k, separator);
337          } else {
338             Mmsg(tmp, "%s=%c%c", k, (char)i32, separator);
339          }
340          break;
341 
342       case OT_CLEAR:
343          **out = 0;
344          break;
345 
346       case OT_END_OBJ:
347          pm_strcpy(tmp, "\n");
348          break;
349 
350       case OT_START_OBJ:
351          i=0;
352          if (object_separator) {
353             for(; i < 32 ; i++) {
354                tmp[i] = object_separator;
355             }
356          }
357          tmp[i++] = '\n';
358          tmp[i] = 0;
359          break;
360 
361       case OT_END:
362          /* wanted fallback */
363       default:
364          val = OT_END;
365       }
366 
367       if (val != OT_END) {
368          pm_strcat(out, tmp);
369          val = (OutputType) va_arg(ap, int); /* OutputType is promoted to int when using ... */
370       }
371    }
372 
373    free_pool_memory(tmp);
374    free_pool_memory(tmp2);
375    //Dmsg1(000, "%s", *out);
376    return *out;
377 }
378 
379 #ifndef TEST_PROGRAM
380 #define TEST_PROGRAM_A
381 #endif
382 
383 #ifdef TEST_PROGRAM
384 #include "unittests.h"
385 
main(int argc,char ** argv)386 int main(int argc, char **argv)
387 {
388    Unittests output_test("output_test");
389    char ed1[50];
390    OutputWriter wt;
391    POOLMEM *tmp = get_pool_memory(PM_FNAME);
392    *tmp = 0;
393 
394    int         nb   = 10000;
395    const char *ptr  = "my value";
396    char       *str  = bstrdup("ptr");
397    int32_t     nb32 = -1;
398    int64_t     nb64 = -1;
399    btime_t     t    = time(NULL);
400 
401    ok(strcmp(wt.get_options(ed1), "") == 0, "Default options");
402 
403    Pmsg1(000, "%s", wt.start_group("test"));
404 
405    wt.get_output(&tmp, OT_CLEAR,
406                  OT_STRING, "test", "my value",
407                  OT_STRING, "test2", ptr,
408                  OT_STRING, "test3", str,
409                  OT_INT,    "nb",   nb,
410                  OT_INT32,  "nb32", nb32,
411                  OT_INT64,  "nb64", nb64,
412                  OT_BTIME,  "now",  t,
413                  OT_END);
414 
415    Pmsg1(000, "%s", tmp);
416 
417    free_pool_memory(tmp);
418 
419    Pmsg1(000, "%s",
420          wt.get_output(OT_CLEAR,
421                  OT_START_OBJ,
422                  OT_STRING, "test", "my value",
423                  OT_STRING, "test2", ptr,
424                  OT_STRING, "test3", str,
425                  OT_INT,    "nb",   nb,
426                  OT_INT32,  "nb32", nb32,
427                  OT_INT64,  "nb64", nb64,
428                  OT_BTIME,  "now",  t,
429                  OT_END_OBJ,
430                  OT_END));
431 
432    wt.set_time_format(OTT_TIME_UNIX);
433    ok(strcmp("t1", wt.get_options(ed1)) == 0, "Check unix time format");
434 
435    Pmsg1(000, "%s",
436          wt.get_output(OT_CLEAR,
437                  OT_BTIME,  "now",  t,
438                  OT_END));
439 
440    wt.set_time_format(OTT_TIME_NC);
441    ok(strcmp("t2", wt.get_options(ed1)) == 0, "Check NC time format");
442 
443    Pmsg1(000, "%s",
444          wt.get_output(OT_CLEAR,
445                  OT_BTIME,  "now",  t,
446                  OT_END));
447 
448    Pmsg1(000, "%s", wt.end_group(false));
449 
450    wt.parse_options("s43t1O");
451    ok(strcmp(wt.get_options(ed1), "s43t1") == 0, "Check options after parsing");
452 
453    ok(strstr(
454          wt.get_output(OT_CLEAR,
455                  OT_BTIME,  "now",  t,
456                  OT_STRING, "brazil", "test",
457                  OT_END),
458          "+brazil=test+") != NULL,
459       "Check separator");
460 
461    wt.parse_options("CS35");
462    ok(strcmp(wt.get_options(ed1), "S35") == 0, "Check options after parsing");
463 
464    Pmsg1(000, "%s",
465          wt.get_output(OT_CLEAR,
466                  OT_START_OBJ,
467                  OT_STRING, "test", "my value",
468                  OT_STRING, "test2", ptr,
469                  OT_END_OBJ,
470                  OT_START_OBJ,
471                  OT_STRING, "test", "my value",
472                  OT_STRING, "test2", ptr,
473                  OT_END_OBJ,
474                  OT_END));
475 
476    free(str);
477 
478    return report();
479 }
480 #endif   /* TEST_PROGRAM */
481