1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero 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
21    02110-1301, USA.
22 */
23 /*
24  * BAREOS message handling routines
25  *
26  * NOTE: don't use any Jmsg or Qmsg calls within this file,
27  * except in q_msg or j_msg (setup routines),
28  * otherwise you may get into recursive calls if there are
29  * errors, and that can lead to looping or deadlocks.
30  *
31  * Kern Sibbald, April 2000
32  */
33 
34 #include "include/bareos.h"
35 #include "include/jcr.h"
36 #include "lib/util.h"
37 
38 db_log_insert_func p_db_log_insert = NULL;
39 
40 #define FULL_LOCATION 1               /* set for file:line in Debug messages */
41 
42 /*
43  * This is where we define "Globals" because all the daemons include this file.
44  */
45 const char *working_directory = NULL; /* working directory path stored here */
46 const char *assert_msg = (char *)NULL; /* ASSERT2 error message */
47 int verbose = 0;                      /* increase User messages */
48 int debug_level = 0;                  /* debug level */
49 bool dbg_timestamp = false;           /* print timestamp in debug output */
50 bool prt_kaboom = false;              /* Print kaboom output */
51 utime_t daemon_start_time = 0;        /* Daemon start time */
52 const char *version = VERSION " (" BDATE ")";
53 const char *dist_name = DISTNAME " " DISTVER;
54 char my_name[128] = {0};              /* daemon name is stored here */
55 char host_name[256] = {0};            /* host machine name */
56 char *exepath = (char *)NULL;
57 char *exename = (char *)NULL;
58 int console_msg_pending = false;
59 char con_fname[500];                  /* Console filename */
60 FILE *con_fd = NULL;                  /* Console file descriptor */
61 brwlock_t con_lock;                   /* Console lock structure */
62 job_code_callback_t message_job_code_callback = NULL; /* Job code callback. Only used by director. */
63 
64 /* Forward referenced functions */
65 
66 /* Imported functions */
67 void setup_tsd_key();
68 
69 /* Static storage */
70 
71 /* Exclude spaces but require .mail at end */
72 #define MAIL_REGEX "^[^ ]+\\.mail$"
73 
74 /*
75  * Allow only one thread to tweak d->fd at a time
76  */
77 static pthread_mutex_t fides_mutex = PTHREAD_MUTEX_INITIALIZER;
78 static MessagesResource *daemon_msgs;          /* Global messages */
79 static char *catalog_db = NULL;       /* Database type */
80 static const char *log_timestamp_format = "%d-%b %H:%M";
81 static void (*message_callback)(int type, char *msg) = NULL;
82 static FILE *trace_fd = NULL;
83 #if defined(HAVE_WIN32)
84 static bool trace = true;
85 #else
86 static bool trace = false;
87 #endif
88 static bool hangup = false;
89 
90 /* Constants */
91 const char *host_os = HOST_OS;
92 const char *distname = DISTNAME;
93 const char *distver = DISTVER;
94 
95 /*
96  * Walk back in a string from end looking for a
97  * path separator.
98  *
99  * This routine is passed the start of the string and
100  * the end of the string, it returns either the beginning
101  * of the string or where it found a path separator.
102  */
bstrrpath(const char * start,const char * end)103 static const char *bstrrpath(const char *start, const char *end)
104 {
105    while ( end > start ) {
106       end--;
107       if (IsPathSeparator(*end)) {
108          break;
109       }
110    }
111    return end;
112 }
113 
114 /*
115  * Some message class methods
116  */
lock()117 void MessagesResource::lock()
118 {
119    P(fides_mutex);
120 }
121 
unlock()122 void MessagesResource::unlock()
123 {
124    V(fides_mutex);
125 }
126 
127 /*
128  * Wait for not in use variable to be clear
129  */
WaitNotInUse()130 void MessagesResource::WaitNotInUse()    /* leaves fides_mutex set */
131 {
132    lock();
133    while (in_use_ || closing_) {
134       unlock();
135       Bmicrosleep(0, 200);         /* wait */
136       lock();
137    }
138 }
139 
140 /*
141  * Handle message delivery errors
142  */
DeliveryError(const char * fmt,...)143 static void DeliveryError(const char *fmt,...)
144 {
145    va_list ap;
146    int i, len, maxlen;
147    POOLMEM *pool_buf;
148    char dt[MAX_TIME_LENGTH];
149 
150    pool_buf = GetPoolMemory(PM_EMSG);
151 
152    bstrftime(dt, sizeof(dt), time(NULL), log_timestamp_format);
153    bstrncat(dt, " ", sizeof(dt));
154 
155    i = Mmsg(pool_buf, "%s Message delivery ERROR: ", dt);
156 
157    while (1) {
158       maxlen = SizeofPoolMemory(pool_buf) - i - 1;
159       va_start(ap, fmt);
160       len = Bvsnprintf(pool_buf+i, maxlen, fmt, ap);
161       va_end(ap);
162 
163       if (len < 0 || len >= (maxlen - 5)) {
164          pool_buf = ReallocPoolMemory(pool_buf, maxlen + i + maxlen / 2);
165          continue;
166       }
167 
168       break;
169    }
170 
171    fputs(pool_buf, stdout);        /* print this here to INSURE that it is printed */
172    fflush(stdout);
173    syslog(LOG_DAEMON | LOG_ERR, "%s", pool_buf);
174    FreeMemory(pool_buf);
175 }
176 
RegisterMessageCallback(void msg_callback (int type,char * msg))177 void RegisterMessageCallback(void msg_callback(int type, char *msg))
178 {
179    message_callback = msg_callback;
180 }
181 
182 /*
183  * Set daemon name. Also, find canonical execution
184  * path.  Note, exepath has spare room for tacking on
185  * the exename so that we can reconstruct the full name.
186  *
187  * Note, this routine can get called multiple times
188  * The second time is to put the name as found in the
189  * Resource record. On the second call, generally,
190  * argv is NULL to avoid doing the path code twice.
191  */
MyNameIs(int argc,char * argv[],const char * name)192 void MyNameIs(int argc, char *argv[], const char *name)
193 {
194    char *l, *p, *q;
195    char cpath[1024];
196    int len;
197 
198    if (gethostname(host_name, sizeof(host_name)) != 0) {
199       bstrncpy(host_name, "Hostname unknown", sizeof(host_name));
200    }
201    bstrncpy(my_name, name, sizeof(my_name));
202    if (argc>0 && argv && argv[0]) {
203       /*
204        * Strip trailing filename and save exepath
205        */
206       for (l=p=argv[0]; *p; p++) {
207          if (IsPathSeparator(*p)) {
208             l = p;                 /* set pos of last slash */
209          }
210       }
211       if (IsPathSeparator(*l)) {
212          l++;
213       } else {
214          l = argv[0];
215 #if defined(HAVE_WIN32)
216          /*
217           * On Windows allow c: drive specification
218           */
219          if (l[1] == ':') {
220             l += 2;
221          }
222 #endif
223       }
224       len = strlen(l) + 1;
225       if (exename) {
226          free(exename);
227       }
228       exename = (char *)malloc(len);
229       strcpy(exename, l);
230 
231       if (exepath) {
232          free(exepath);
233       }
234       exepath = (char *)malloc(strlen(argv[0]) + 1 + len);
235       for (p=argv[0],q=exepath; p < l; ) {
236          *q++ = *p++;
237       }
238       *q = 0;
239       if (strchr(exepath, '.') || !IsPathSeparator(exepath[0])) {
240          if (getcwd(cpath, sizeof(cpath))) {
241             free(exepath);
242             exepath = (char *)malloc(strlen(cpath) + 1 + len);
243             strcpy(exepath, cpath);
244          }
245       }
246       Dmsg2(500, "exepath=%s\nexename=%s\n", exepath, exename);
247    }
248 }
249 
SetDbType(const char * name)250 void SetDbType(const char *name)
251 {
252    if (catalog_db != NULL) {
253       free(catalog_db);
254    }
255    catalog_db = bstrdup(name);
256 }
257 
258 /*
259  * Initialize message handler for a daemon or a Job
260  * We make a copy of the MessagesResource resource passed, so it belongs
261  * to the job or daemon and thus can be modified.
262  *
263  * NULL for jcr -> initialize global messages for daemon
264  * non-NULL     -> initialize jcr using Message resource
265  */
InitMsg(JobControlRecord * jcr,MessagesResource * msg,job_code_callback_t job_code_callback)266 void InitMsg(JobControlRecord *jcr, MessagesResource *msg, job_code_callback_t job_code_callback)
267 {
268    DEST *d, *dnew, *temp_chain = NULL;
269    int i;
270 
271    if (jcr == NULL && msg == NULL) {
272       InitLastJobsList();
273       /*
274        * Setup a daemon key then set invalid jcr
275        * Maybe we should give the daemon a jcr???
276        */
277       setup_tsd_key();
278       SetJcrInTsd(INVALID_JCR);
279    }
280 
281    message_job_code_callback = job_code_callback;
282 
283 #if !defined(HAVE_WIN32)
284    /*
285     * Make sure we have fd's 0, 1, 2 open
286     * If we don't do this one of our sockets may open
287     * there and if we then use stdout, it could
288     * send total garbage to our socket.
289     */
290    int fd;
291    fd = open("/dev/null", O_RDONLY, 0644);
292    if (fd > 2) {
293       close(fd);
294    } else {
295       for(i=1; fd + i <= 2; i++) {
296          dup2(fd, fd+i);
297       }
298    }
299 #endif
300 
301    /*
302     * If msg is NULL, initialize global chain for STDOUT and syslog
303     */
304    if (msg == NULL) {
305       daemon_msgs = (MessagesResource *)malloc(sizeof(MessagesResource));
306       memset(daemon_msgs, 0, sizeof(MessagesResource));
307       for (i=1; i<=M_MAX; i++) {
308          AddMsgDest(daemon_msgs, MD_STDOUT, i, NULL, NULL, NULL);
309       }
310       Dmsg1(050, "Create daemon global message resource %p\n", daemon_msgs);
311       return;
312    }
313 
314    /*
315     * Walk down the message resource chain duplicating it for the current Job.
316     */
317    for (d = msg->dest_chain; d; d = d->next) {
318       dnew = (DEST *)malloc(sizeof(DEST));
319       memcpy(dnew, d, sizeof(DEST));
320       dnew->next = temp_chain;
321       dnew->fd = NULL;
322       dnew->mail_filename = NULL;
323       if (d->mail_cmd) {
324          dnew->mail_cmd = bstrdup(d->mail_cmd);
325       }
326       if (d->where) {
327          dnew->where = bstrdup(d->where);
328       }
329       temp_chain = dnew;
330    }
331 
332    if (jcr) {
333       jcr->jcr_msgs = (MessagesResource *)malloc(sizeof(MessagesResource));
334       memset(jcr->jcr_msgs, 0, sizeof(MessagesResource));
335       jcr->jcr_msgs->dest_chain = temp_chain;
336       memcpy(jcr->jcr_msgs->SendMsg, msg->SendMsg, sizeof(msg->SendMsg));
337    } else {
338       /*
339        * If we have default values, release them now
340        */
341       if (daemon_msgs) {
342          FreeMsgsRes(daemon_msgs);
343       }
344       daemon_msgs = (MessagesResource *)malloc(sizeof(MessagesResource));
345       memset(daemon_msgs, 0, sizeof(MessagesResource));
346       daemon_msgs->dest_chain = temp_chain;
347       memcpy(daemon_msgs->SendMsg, msg->SendMsg, sizeof(msg->SendMsg));
348    }
349 
350    Dmsg2(250, "Copy message resource %p to %p\n", msg, temp_chain);
351 }
352 
353 /*
354  * Initialize so that the console (User Agent) can receive messages -- stored in a file.
355  */
InitConsoleMsg(const char * wd)356 void InitConsoleMsg(const char *wd)
357 {
358    int fd;
359 
360    Bsnprintf(con_fname, sizeof(con_fname), "%s%c%s.conmsg", wd, PathSeparator, my_name);
361    fd = open(con_fname, O_CREAT|O_RDWR|O_BINARY, 0600);
362    if (fd == -1) {
363       BErrNo be;
364       Emsg2(M_ERROR_TERM, 0, _("Could not open console message file %s: ERR=%s\n"),
365             con_fname, be.bstrerror());
366    }
367    if (lseek(fd, 0, SEEK_END) > 0) {
368       console_msg_pending = 1;
369    }
370    close(fd);
371    con_fd = fopen(con_fname, "a+b");
372    if (!con_fd) {
373       BErrNo be;
374       Emsg2(M_ERROR, 0, _("Could not open console message file %s: ERR=%s\n"),
375             con_fname, be.bstrerror());
376    }
377    if (RwlInit(&con_lock) != 0) {
378       BErrNo be;
379       Emsg1(M_ERROR_TERM, 0, _("Could not get con mutex: ERR=%s\n"),
380             be.bstrerror());
381    }
382 }
383 
384 /*
385  * Called only during parsing of the config file.
386  *
387  * Add a message destination. I.e. associate a message type with
388  * a destination (code).
389  *
390  * Note, where in the case of dest_code FILE is a filename,
391  * but in the case of MAIL is a space separated list of
392  * email addresses, ...
393  */
AddMsgDest(MessagesResource * msg,int dest_code,int msg_type,char * where,char * mail_cmd,char * timestamp_format)394 void AddMsgDest(MessagesResource *msg, int dest_code, int msg_type,
395                   char *where, char *mail_cmd, char *timestamp_format)
396 {
397    DEST *d;
398 
399    /*
400     * First search the existing chain and see if we
401     * can simply add this msg_type to an existing entry.
402     */
403    for (d = msg->dest_chain; d; d = d->next) {
404       if (dest_code == d->dest_code && ((where == NULL && d->where == NULL) ||
405           bstrcmp(where, d->where))) {
406          Dmsg4(850, "Add to existing d=%p msgtype=%d destcode=%d where=%s\n",
407                d, msg_type, dest_code, NPRT(where));
408          SetBit(msg_type, d->msg_types);
409          SetBit(msg_type, msg->SendMsg);  /* Set msg_type bit in our local */
410          return;
411       }
412    }
413 
414    /*
415     * Not found, create a new entry
416     */
417    d = (DEST *)malloc(sizeof(DEST));
418    memset(d, 0, sizeof(DEST));
419    d->next = msg->dest_chain;
420    d->dest_code = dest_code;
421    SetBit(msg_type, d->msg_types);      /* Set type bit in structure */
422    SetBit(msg_type, msg->SendMsg);     /* Set type bit in our local */
423 
424    if (where) {
425       d->where = bstrdup(where);
426    }
427 
428    if (mail_cmd) {
429       d->mail_cmd = bstrdup(mail_cmd);
430    }
431 
432    if (timestamp_format) {
433       d->timestamp_format = bstrdup(timestamp_format);
434    }
435 
436    Dmsg6(850, "add new d=%p msgtype=%d destcode=%d where=%s mailcmd=%s timestampformat=%s\n",
437          d, msg_type, dest_code, NPRT(where), NPRT(d->mail_cmd), NPRT(d->timestamp_format));
438    msg->dest_chain = d;
439 }
440 
441 /*
442  * Called only during parsing of the config file.
443  *
444  * Remove a message destination
445  */
RemMsgDest(MessagesResource * msg,int dest_code,int msg_type,char * where)446 void RemMsgDest(MessagesResource *msg, int dest_code, int msg_type, char *where)
447 {
448    DEST *d;
449 
450    for (d = msg->dest_chain; d; d = d->next) {
451       Dmsg2(850, "Remove_msg_dest d=%p where=%s\n", d, NPRT(d->where));
452       if (BitIsSet(msg_type, d->msg_types) && (dest_code == d->dest_code) &&
453           ((where == NULL && d->where == NULL) ||
454           bstrcmp(where, d->where))) {
455          Dmsg3(850, "Found for remove d=%p msgtype=%d destcode=%d\n",
456                d, msg_type, dest_code);
457          ClearBit(msg_type, d->msg_types);
458          Dmsg0(850, "Return RemMsgDest\n");
459          return;
460       }
461    }
462 }
463 
464 /*
465  * Create a unique filename for the mail command
466  */
MakeUniqueMailFilename(JobControlRecord * jcr,POOLMEM * & name,DEST * d)467 static void MakeUniqueMailFilename(JobControlRecord *jcr, POOLMEM *&name, DEST *d)
468 {
469    if (jcr) {
470       Mmsg(name, "%s/%s.%s.%d.mail", working_directory, my_name,
471                  jcr->Job, (int)(intptr_t)d);
472    } else {
473       Mmsg(name, "%s/%s.%s.%d.mail", working_directory, my_name,
474                  my_name, (int)(intptr_t)d);
475    }
476    Dmsg1(850, "mailname=%s\n", name);
477 }
478 
479 /*
480  * Open a mail pipe
481  */
open_mail_pipe(JobControlRecord * jcr,POOLMEM * & cmd,DEST * d)482 static Bpipe *open_mail_pipe(JobControlRecord *jcr, POOLMEM *&cmd, DEST *d)
483 {
484    Bpipe *bpipe;
485 
486    if (d->mail_cmd) {
487       cmd = edit_job_codes(jcr, cmd, d->mail_cmd, d->where, message_job_code_callback);
488    } else {
489       Mmsg(cmd, "/usr/lib/sendmail -F BAREOS %s", d->where);
490    }
491 
492    if ((bpipe = OpenBpipe(cmd, 120, "rw"))) {
493       /*
494        * If we had to use sendmail, add subject
495        */
496       if (!d->mail_cmd) {
497          fprintf(bpipe->wfd, "Subject: %s\r\n\r\n", _("BAREOS Message"));
498       }
499    } else {
500       BErrNo be;
501       DeliveryError(_("open mail pipe %s failed: ERR=%s\n"),
502                      cmd, be.bstrerror());
503    }
504 
505    return bpipe;
506 }
507 
508 /*
509  * Close the messages for this Messages resource, which means to close
510  * any open files, and dispatch any pending email messages.
511  */
CloseMsg(JobControlRecord * jcr)512 void CloseMsg(JobControlRecord *jcr)
513 {
514    MessagesResource *msgs;
515    DEST *d;
516    Bpipe *bpipe;
517    POOLMEM *cmd, *line;
518    int len, status;
519 
520    Dmsg1(580, "Close_msg jcr=%p\n", jcr);
521 
522    if (jcr == NULL) {                /* NULL -> global chain */
523       msgs = daemon_msgs;
524    } else {
525       msgs = jcr->jcr_msgs;
526       jcr->jcr_msgs = NULL;
527    }
528    if (msgs == NULL) {
529       return;
530    }
531 
532    /*
533     * Wait for item to be not in use, then mark closing
534     */
535    if (msgs->IsClosing()) {
536       return;
537    }
538    msgs->WaitNotInUse();          /* leaves fides_mutex set */
539 
540    /*
541     * Note GetClosing() does not lock because we are already locked
542     */
543    if (msgs->GetClosing()) {
544       msgs->unlock();
545       return;
546    }
547    msgs->SetClosing();
548    msgs->unlock();
549 
550    Dmsg1(850, "===Begin close msg resource at %p\n", msgs);
551    cmd = GetPoolMemory(PM_MESSAGE);
552    for (d = msgs->dest_chain; d; ) {
553       if (d->fd) {
554          switch (d->dest_code) {
555          case MD_FILE:
556          case MD_APPEND:
557             if (d->fd) {
558                fclose(d->fd);        /* close open file descriptor */
559                d->fd = NULL;
560             }
561             break;
562          case MD_MAIL:
563          case MD_MAIL_ON_ERROR:
564          case MD_MAIL_ON_SUCCESS:
565             Dmsg0(850, "Got MD_MAIL, MD_MAIL_ON_ERROR or MD_MAIL_ON_SUCCESS\n");
566             if (!d->fd) {
567                break;
568             }
569 
570             switch (d->dest_code) {
571             case MD_MAIL_ON_ERROR:
572                if (jcr) {
573                   switch (jcr->JobStatus) {
574                   case JS_Terminated:
575                   case JS_Warnings:
576                      goto rem_temp_file;
577                   default:
578                      break;
579                   }
580                }
581                break;
582             case MD_MAIL_ON_SUCCESS:
583                if (jcr) {
584                   switch (jcr->JobStatus) {
585                   case JS_Terminated:
586                   case JS_Warnings:
587                      break;
588                   default:
589                      goto rem_temp_file;
590                   }
591                }
592                break;
593             default:
594                break;
595             }
596 
597             if (!(bpipe=open_mail_pipe(jcr, cmd, d))) {
598                Pmsg0(000, _("open mail pipe failed.\n"));
599                goto rem_temp_file;
600             }
601 
602             Dmsg0(850, "Opened mail pipe\n");
603             len = d->max_len+10;
604             line = GetMemory(len);
605             rewind(d->fd);
606             while (fgets(line, len, d->fd)) {
607                fputs(line, bpipe->wfd);
608             }
609             if (!CloseWpipe(bpipe)) {       /* close write pipe sending mail */
610                BErrNo be;
611                Pmsg1(000, _("close error: ERR=%s\n"), be.bstrerror());
612             }
613 
614             /*
615              * Since we are closing all messages, before "recursing"
616              * make sure we are not closing the daemon messages, otherwise
617              * kaboom.
618              */
619             if (msgs != daemon_msgs) {
620                /*
621                 * Read what mail prog returned -- should be nothing
622                 */
623                while (fgets(line, len, bpipe->rfd)) {
624                   DeliveryError(_("Mail prog: %s"), line);
625                }
626             }
627 
628             status = CloseBpipe(bpipe);
629             if (status != 0 && msgs != daemon_msgs) {
630                BErrNo be;
631                be.SetErrno(status);
632                Dmsg1(850, "Calling emsg. CMD=%s\n", cmd);
633                DeliveryError(_("Mail program terminated in error.\n"
634                                  "CMD=%s\n"
635                                  "ERR=%s\n"), cmd, be.bstrerror());
636             }
637             FreeMemory(line);
638 rem_temp_file:
639             /*
640              * Remove temp file
641              */
642             if (d->fd) {
643                fclose(d->fd);
644                d->fd = NULL;
645             }
646             if (d->mail_filename) {
647                /*
648                 * Exclude spaces in mail_filename
649                 */
650                SaferUnlink(d->mail_filename, MAIL_REGEX);
651                FreePoolMemory(d->mail_filename);
652                d->mail_filename = NULL;
653             }
654             Dmsg0(850, "end mail or mail on error\n");
655             break;
656          default:
657             break;
658          }
659          d->fd = NULL;
660       }
661       d = d->next;                    /* point to next buffer */
662    }
663    FreePoolMemory(cmd);
664    Dmsg0(850, "Done walking message chain.\n");
665    if (jcr) {
666       FreeMsgsRes(msgs);
667       msgs = NULL;
668    } else {
669       msgs->ClearClosing();
670    }
671    Dmsg0(850, "===End close msg resource\n");
672 }
673 
674 /*
675  * Free memory associated with Messages resource
676  */
FreeMsgsRes(MessagesResource * msgs)677 void FreeMsgsRes(MessagesResource *msgs)
678 {
679    DEST *d, *old;
680 
681    /*
682     * Walk down the message chain releasing allocated buffers
683     */
684    for (d = msgs->dest_chain; d; ) {
685       if (d->where) {
686          free(d->where);
687       }
688       if (d->mail_cmd) {
689          free(d->mail_cmd);
690       }
691       if (d->timestamp_format) {
692          free(d->timestamp_format);
693       }
694       old = d;                        /* save pointer to release */
695       d = d->next;                    /* point to next buffer */
696       free(old);                      /* free the destination item */
697    }
698    msgs->dest_chain = NULL;
699    free(msgs);                        /* free the head */
700 }
701 
702 /*
703  * Terminate the message handler for good.
704  * Release the global destination chain.
705  *
706  * Also, clean up a few other items (cons, exepath). Note,
707  * these really should be done elsewhere.
708  */
TermMsg()709 void TermMsg()
710 {
711    Dmsg0(850, "Enter TermMsg\n");
712    CloseMsg(NULL);                   /* close global chain */
713    FreeMsgsRes(daemon_msgs);        /* free the resources */
714    daemon_msgs = NULL;
715    if (con_fd) {
716       fflush(con_fd);
717       fclose(con_fd);
718       con_fd = NULL;
719    }
720    if (exepath) {
721       free(exepath);
722       exepath = NULL;
723    }
724    if (exename) {
725       free(exename);
726       exename = NULL;
727    }
728    if (trace_fd) {
729       fclose(trace_fd);
730       trace_fd = NULL;
731    }
732    if (catalog_db) {
733       free(catalog_db);
734       catalog_db = NULL;
735    }
736    TermLastJobsList();
737 }
738 
OpenDestFile(DEST * d,const char * mode)739 static inline bool OpenDestFile(DEST *d, const char *mode)
740 {
741    d->fd = fopen(d->where, mode);
742    if (!d->fd) {
743       BErrNo be;
744       DeliveryError(_("fopen %s failed: ERR=%s\n"), d->where, be.bstrerror());
745       return false;
746    }
747 
748    return true;
749 }
750 
751 static struct syslog_facility_name {
752    const char *name;
753    int facility;
754 } syslog_facility_names[] = {
755     { "kern", LOG_KERN },
756     { "user", LOG_USER },
757     { "mail", LOG_MAIL },
758     { "daemon", LOG_DAEMON },
759     { "auth", LOG_AUTH },
760     { "syslog", LOG_SYSLOG },
761     { "lpr", LOG_LPR },
762     { "news", LOG_NEWS },
763     { "uucp", LOG_UUCP },
764 #ifdef LOG_CRON
765     { "cron", LOG_CRON },
766 #endif
767 #ifdef LOG_AUTHPRIV
768     { "authpriv", LOG_AUTHPRIV },
769 #endif
770 #ifdef LOG_FTP
771     { "ftp", LOG_FTP },
772 #endif
773 #ifdef LOG_NTP
774     { "ntp", LOG_NTP },
775 #endif
776 #ifdef LOG_AUDIT
777     { "audit", LOG_AUDIT },
778 #endif
779 #ifdef LOG_SECURITY
780     { "security", LOG_SECURITY },
781 #endif
782 #ifdef LOG_CONSOLE
783     { "console", LOG_CONSOLE },
784 #endif
785     { "local0", LOG_LOCAL0 },
786     { "local1", LOG_LOCAL1 },
787     { "local2", LOG_LOCAL2 },
788     { "local3", LOG_LOCAL3 },
789     { "local4", LOG_LOCAL4 },
790     { "local5", LOG_LOCAL5 },
791     { "local6", LOG_LOCAL6 },
792     { "local7", LOG_LOCAL7 },
793     { NULL, -1 }
794 };
795 
SetSyslogFacility(JobControlRecord * jcr,DEST * d)796 static inline bool SetSyslogFacility(JobControlRecord *jcr, DEST *d)
797 {
798    int i;
799 
800    if (d->where) {
801       for (i = 0; syslog_facility_names[i].name; i++) {
802          if (Bstrcasecmp(d->where, syslog_facility_names[i].name)) {
803             d->syslog_facility = syslog_facility_names[i].facility;
804             i = 0;
805             break;
806          }
807       }
808 
809       /*
810        * Make sure we got a match otherwise fallback to LOG_DAEMON
811        */
812       if (i != 0) {
813          d->syslog_facility = LOG_DAEMON;
814       }
815    } else {
816       d->syslog_facility = LOG_DAEMON;
817    }
818 
819    return true;
820 }
821 
822 /*
823  * Split the output for syslog (it converts \n to ' ' and is
824  * limited to 1024 characters per syslog message
825  */
SendToSyslog(int mode,const char * msg)826 static inline void SendToSyslog(int mode, const char *msg)
827 {
828    int len;
829    char buf[1024];
830    const char *p2;
831    const char *p = msg;
832 
833    while (*p && ((p2 = strchr(p, '\n')) != NULL)) {
834       len = MIN((int)sizeof(buf) - 1, p2 - p + 1); /* Add 1 to keep \n */
835       strncpy(buf, p, len);
836       buf[len] = 0;
837       syslog(mode, "%s", buf);
838       p = p2+1;                 /* skip \n */
839    }
840    if (*p != 0) {               /* no \n at the end ? */
841       syslog(mode, "%s", p);
842    }
843 }
844 
845 /*
846  * Handle sending the message to the appropriate place
847  */
DispatchMessage(JobControlRecord * jcr,int type,utime_t mtime,char * msg)848 void DispatchMessage(JobControlRecord *jcr, int type, utime_t mtime, char *msg)
849 {
850    DEST *d;
851    char dt[MAX_TIME_LENGTH];
852    POOLMEM *mcmd;
853    int len, dtlen;
854    MessagesResource *msgs;
855    Bpipe *bpipe;
856    const char *mode;
857    bool dt_conversion = false;
858 
859    Dmsg2(850, "Enter DispatchMessage type=%d msg=%s", type, msg);
860 
861    /*
862     * Most messages are prefixed by a date and time. If mtime is
863     * zero, then we use the current time.  If mtime is 1 (special
864     * kludge), we do not prefix the date and time. Otherwise,
865     * we assume mtime is a utime_t and use it.
866     */
867    if (mtime == 0) {
868       mtime = time(NULL);
869    }
870 
871    *dt = 0;
872    dtlen = 0;
873    if (mtime == 1) {
874       mtime = time(NULL);      /* Get time for SQL log */
875    } else {
876       dt_conversion = true;
877    }
878 
879    /*
880     * If the program registered a callback, send it there
881     */
882    if (message_callback) {
883       message_callback(type, msg);
884       return;
885    }
886 
887    /*
888     * For serious errors make sure message is printed or logged
889     */
890    if (type == M_ABORT || type == M_ERROR_TERM) {
891       fputs(dt, stdout);
892       fputs(msg, stdout);
893       fflush(stdout);
894       if (type == M_ABORT) {
895          syslog(LOG_DAEMON | LOG_ERR, "%s", msg);
896       }
897    }
898 
899    /*
900     * Now figure out where to send the message
901     */
902    msgs = NULL;
903    if (!jcr) {
904       jcr = get_jcr_from_tsd();
905    }
906 
907    if (jcr) {
908       /*
909        * See if we need to suppress the messages.
910        */
911       if (jcr->suppress_output) {
912          /*
913           * See if this JobControlRecord has a controlling JobControlRecord and if so redirect
914           * this message to the controlling JobControlRecord.
915           */
916          if (jcr->cjcr) {
917             jcr = jcr->cjcr;
918          } else {
919             /*
920              * Ignore this Job Message.
921              */
922             return;
923          }
924       }
925       msgs = jcr->jcr_msgs;
926    }
927 
928    if (msgs == NULL) {
929       msgs = daemon_msgs;
930    }
931 
932    if (!msgs) {
933      Dmsg1(100, "Could not dispatch message: %s", msg);
934      return;
935    }
936 
937    /*
938     * If closing this message resource, print and send to syslog, then get out.
939     */
940    if (msgs->IsClosing()) {
941       if (dt_conversion) {
942          bstrftime(dt, sizeof(dt), mtime, log_timestamp_format);
943          bstrncat(dt, " ", sizeof(dt));
944       }
945       fputs(dt, stdout);
946       fputs(msg, stdout);
947       fflush(stdout);
948       syslog(LOG_DAEMON | LOG_ERR, "%s", msg);
949       return;
950    }
951 
952    for (d = msgs->dest_chain; d; d = d->next) {
953       if (BitIsSet(type, d->msg_types)) {
954          /*
955           * See if a specific timestamp format was specified for this log resource.
956           * Otherwise apply the global setting in log_timestamp_format.
957           */
958          if (dt_conversion) {
959             if (d->timestamp_format) {
960                bstrftime(dt, sizeof(dt), mtime, d->timestamp_format);
961             } else {
962                bstrftime(dt, sizeof(dt), mtime, log_timestamp_format);
963             }
964             bstrncat(dt, " ", sizeof(dt));
965             dtlen = strlen(dt);
966          }
967 
968          switch (d->dest_code) {
969          case MD_CATALOG:
970             if (!jcr || !jcr->db) {
971                break;
972             }
973 
974             if (p_db_log_insert) {
975                if (!p_db_log_insert(jcr, mtime, msg)) {
976                   DeliveryError(_("Msg delivery error: Unable to store data in database.\n"));
977                }
978             }
979             break;
980          case MD_CONSOLE:
981             Dmsg1(850, "CONSOLE for following msg: %s", msg);
982             if (!con_fd) {
983                con_fd = fopen(con_fname, "a+b");
984                Dmsg0(850, "Console file not open.\n");
985             }
986             if (con_fd) {
987                Pw(con_lock);      /* get write lock on console message file */
988                errno = 0;
989                if (dtlen) {
990                   (void)fwrite(dt, dtlen, 1, con_fd);
991                }
992                len = strlen(msg);
993                if (len > 0) {
994                   (void)fwrite(msg, len, 1, con_fd);
995                   if (msg[len-1] != '\n') {
996                      (void)fwrite("\n", 2, 1, con_fd);
997                   }
998                } else {
999                   (void)fwrite("\n", 2, 1, con_fd);
1000                }
1001                fflush(con_fd);
1002                console_msg_pending = true;
1003                Vw(con_lock);
1004             }
1005             break;
1006          case MD_SYSLOG:
1007             Dmsg1(850, "SYSLOG for following msg: %s\n", msg);
1008 
1009             if (!d->syslog_facility && !SetSyslogFacility(jcr, d)) {
1010                msgs->ClearInUse();
1011                break;
1012             }
1013 
1014             /*
1015              * Dispatch based on our internal message type to a matching syslog one.
1016              */
1017             switch (type) {
1018             case M_ERROR:
1019             case M_ERROR_TERM:
1020                SendToSyslog(d->syslog_facility | LOG_ERR, msg);
1021                break;
1022             case M_ABORT:
1023             case M_FATAL:
1024                SendToSyslog(d->syslog_facility | LOG_CRIT, msg);
1025                break;
1026             case M_WARNING:
1027                SendToSyslog(d->syslog_facility | LOG_WARNING, msg);
1028                break;
1029             case M_DEBUG:
1030                SendToSyslog(d->syslog_facility | LOG_DEBUG, msg);
1031                break;
1032             case M_INFO:
1033             case M_NOTSAVED:
1034             case M_RESTORED:
1035             case M_SAVED:
1036             case M_SKIPPED:
1037             case M_TERM:
1038                SendToSyslog(d->syslog_facility | LOG_INFO, msg);
1039                break;
1040             case M_ALERT:
1041             case M_AUDIT:
1042             case M_MOUNT:
1043             case M_SECURITY:
1044             case M_VOLMGMT:
1045                SendToSyslog(d->syslog_facility | LOG_NOTICE, msg);
1046                break;
1047             default:
1048                SendToSyslog(d->syslog_facility | LOG_ERR, msg);
1049                break;
1050             }
1051             break;
1052          case MD_OPERATOR:
1053             Dmsg1(850, "OPERATOR for following msg: %s\n", msg);
1054             mcmd = GetPoolMemory(PM_MESSAGE);
1055             if ((bpipe=open_mail_pipe(jcr, mcmd, d))) {
1056                int status;
1057                fputs(dt, bpipe->wfd);
1058                fputs(msg, bpipe->wfd);
1059                /*
1060                 * Messages to the operator go one at a time
1061                 */
1062                status = CloseBpipe(bpipe);
1063                if (status != 0) {
1064                   BErrNo be;
1065                   be.SetErrno(status);
1066                   DeliveryError(_("Msg delivery error: Operator mail program terminated in error.\n"
1067                                    "CMD=%s\nERR=%s\n"), mcmd, be.bstrerror());
1068                }
1069             }
1070             FreePoolMemory(mcmd);
1071             break;
1072          case MD_MAIL:
1073          case MD_MAIL_ON_ERROR:
1074          case MD_MAIL_ON_SUCCESS:
1075             Dmsg1(850, "MAIL for following msg: %s", msg);
1076             if (msgs->IsClosing()) {
1077                break;
1078             }
1079             msgs->SetInUse();
1080             if (!d->fd) {
1081                POOLMEM *name = GetPoolMemory(PM_MESSAGE);
1082                MakeUniqueMailFilename(jcr, name, d);
1083                d->fd = fopen(name, "w+b");
1084                if (!d->fd) {
1085                   BErrNo be;
1086                   DeliveryError(_("Msg delivery error: fopen %s failed: ERR=%s\n"), name,
1087                            be.bstrerror());
1088                   FreePoolMemory(name);
1089                   msgs->ClearInUse();
1090                   break;
1091                }
1092                d->mail_filename = name;
1093             }
1094             fputs(dt, d->fd);
1095             len = strlen(msg) + dtlen;
1096             if (len > d->max_len) {
1097                d->max_len = len;      /* keep max line length */
1098             }
1099             fputs(msg, d->fd);
1100             msgs->ClearInUse();
1101             break;
1102          case MD_APPEND:
1103             Dmsg1(850, "APPEND for following msg: %s", msg);
1104             mode = "ab";
1105             goto send_to_file;
1106          case MD_FILE:
1107             Dmsg1(850, "FILE for following msg: %s", msg);
1108             mode = "w+b";
1109 send_to_file:
1110             if (msgs->IsClosing()) {
1111                break;
1112             }
1113             msgs->SetInUse();
1114             if (!d->fd && !OpenDestFile(d, mode)) {
1115                msgs->ClearInUse();
1116                break;
1117             }
1118             fputs(dt, d->fd);
1119             fputs(msg, d->fd);
1120             /*
1121              * On error, we close and reopen to handle log rotation
1122              */
1123             if (ferror(d->fd)) {
1124                fclose(d->fd);
1125                d->fd = NULL;
1126                if (OpenDestFile(d, mode)) {
1127                   fputs(dt, d->fd);
1128                   fputs(msg, d->fd);
1129                }
1130             }
1131             fflush(d->fd);
1132             msgs->ClearInUse();
1133             break;
1134          case MD_DIRECTOR:
1135             Dmsg1(850, "DIRECTOR for following msg: %s", msg);
1136             if (jcr && jcr->dir_bsock && !jcr->dir_bsock->errors) {
1137                jcr->dir_bsock->fsend("Jmsg Job=%s type=%d level=%lld %s",
1138                                      jcr->Job, type, mtime, msg);
1139             } else {
1140                Dmsg1(800, "no jcr for following msg: %s", msg);
1141             }
1142             break;
1143          case MD_STDOUT:
1144             Dmsg1(850, "STDOUT for following msg: %s", msg);
1145             if (type != M_ABORT && type != M_ERROR_TERM) { /* already printed */
1146                fputs(dt, stdout);
1147                fputs(msg, stdout);
1148                fflush(stdout);
1149             }
1150             break;
1151          case MD_STDERR:
1152             Dmsg1(850, "STDERR for following msg: %s", msg);
1153             fputs(dt, stderr);
1154             fputs(msg, stderr);
1155             fflush(stdout);
1156             break;
1157          default:
1158             break;
1159          }
1160       }
1161    }
1162 }
1163 
1164 /*
1165  * This subroutine returns the filename portion of a path.
1166  * It is used because some compilers set __FILE__
1167  * to the full path.  Try to return base + next higher path.
1168  */
get_basename(const char * pathname)1169 const char *get_basename(const char *pathname)
1170 {
1171    const char *basename;
1172 
1173    if ((basename = bstrrpath(pathname, pathname+strlen(pathname))) == pathname) {
1174       /* empty */
1175    } else if ((basename = bstrrpath(pathname, basename-1)) == pathname) {
1176       /* empty */
1177    } else {
1178       basename++;
1179    }
1180    return basename;
1181 }
1182 
1183 /*
1184  * Print or write output to trace file
1185  */
pt_out(char * buf)1186 static void pt_out(char *buf)
1187 {
1188    /*
1189     * Used the "trace on" command in the console to turn on
1190     * output to the trace file.  "trace off" will close the file.
1191     */
1192    if (trace) {
1193       if (!trace_fd) {
1194          PoolMem fn(PM_FNAME);
1195 
1196          Mmsg(fn, "%s/%s.trace", TRACEFILEDIRECTORY, my_name);
1197          trace_fd = fopen(fn.c_str(), "a+b");
1198       }
1199       if (trace_fd) {
1200          fputs(buf, trace_fd);
1201          fflush(trace_fd);
1202          return;
1203       } else {
1204          /*
1205           * Some problem, turn off tracing
1206           */
1207          trace = false;
1208       }
1209    }
1210 
1211    /*
1212     * Not tracing
1213     */
1214    fputs(buf, stdout);
1215    fflush(stdout);
1216 }
1217 
1218 /*
1219  *  This subroutine prints a debug message if the level number is less than or
1220  *  equal the debug_level. File and line numbers are included for more detail if
1221  *  desired, but not currently printed.
1222  *
1223  *  If the level is negative, the details of file and line number are not printed.
1224  */
d_msg(const char * file,int line,int level,const char * fmt,...)1225 void d_msg(const char *file, int line, int level, const char *fmt,...)
1226 {
1227    va_list ap;
1228    char ed1[50];
1229    int len, maxlen;
1230    btime_t mtime;
1231    uint32_t usecs;
1232    bool details = true;
1233    PoolMem buf(PM_EMSG),
1234             more(PM_EMSG);
1235 
1236    if (level < 0) {
1237       details = false;
1238       level = -level;
1239    }
1240 
1241    if (level <= debug_level) {
1242       if (dbg_timestamp) {
1243          mtime = GetCurrentBtime();
1244          usecs = mtime % 1000000;
1245          Mmsg(buf, "%s.%06d ", bstrftimes(ed1, sizeof(ed1), BtimeToUtime(mtime)), usecs);
1246          pt_out(buf.c_str());
1247       }
1248 
1249 #ifdef FULL_LOCATION
1250       if (details) {
1251          Mmsg(buf, "%s (%d): %s:%d-%u ", my_name, level, get_basename(file), line, GetJobidFromTsd());
1252       }
1253 #endif
1254 
1255       while (1) {
1256          maxlen = more.MaxSize() - 1;
1257          va_start(ap, fmt);
1258          len = Bvsnprintf(more.c_str(), maxlen, fmt, ap);
1259          va_end(ap);
1260 
1261          if (len < 0 || len >= (maxlen - 5)) {
1262             more.ReallocPm(maxlen + maxlen / 2);
1263             continue;
1264          }
1265 
1266          break;
1267       }
1268 
1269 #ifdef FULL_LOCATION
1270       if (details) {
1271          pt_out(buf.c_str());
1272       }
1273 #endif
1274 
1275       pt_out(more.c_str());
1276    }
1277 }
1278 
1279 /*
1280  * Set trace flag on/off. If argument is negative, there is no change
1281  */
SetTrace(int trace_flag)1282 void SetTrace(int trace_flag)
1283 {
1284    if (trace_flag < 0) {
1285       return;
1286    } else if (trace_flag > 0) {
1287       trace = true;
1288    } else {
1289       trace = false;
1290    }
1291 
1292    if (!trace && trace_fd) {
1293       FILE *ltrace_fd = trace_fd;
1294       trace_fd = NULL;
1295       Bmicrosleep(0, 100000);         /* yield to prevent seg faults */
1296       fclose(ltrace_fd);
1297    }
1298 }
1299 
SetHangup(int hangup_value)1300 void SetHangup(int hangup_value)
1301 {
1302    if (hangup_value < 0) {
1303       return;
1304    } else {
1305       hangup = hangup_value;
1306    }
1307 }
1308 
SetTimestamp(int timestamp_flag)1309 void SetTimestamp(int timestamp_flag)
1310 {
1311    if (timestamp_flag < 0) {
1312       return;
1313    } else if (timestamp_flag > 0) {
1314       dbg_timestamp = true;
1315    } else {
1316       dbg_timestamp = false;
1317    }
1318 }
1319 
GetHangup(void)1320 bool GetHangup(void)
1321 {
1322    return hangup;
1323 }
1324 
GetTrace(void)1325 bool GetTrace(void)
1326 {
1327    return trace;
1328 }
1329 
GetTimestamp(void)1330 bool GetTimestamp(void)
1331 {
1332    return dbg_timestamp;
1333 }
1334 
1335 /*
1336  * This subroutine prints a message regardless of the debug level
1337  *
1338  * If the level is negative, the details of file and line number are not printed.
1339  */
p_msg(const char * file,int line,int level,const char * fmt,...)1340 void p_msg(const char *file, int line, int level, const char *fmt,...)
1341 {
1342    va_list ap;
1343    int len, maxlen;
1344    PoolMem buf(PM_EMSG),
1345             more(PM_EMSG);
1346 
1347 #ifdef FULL_LOCATION
1348    if (level >= 0) {
1349       Mmsg(buf,  "%s: %s:%d-%u ", my_name, get_basename(file), line, GetJobidFromTsd());
1350    }
1351 #endif
1352 
1353    while (1) {
1354       maxlen = more.MaxSize() - 1;
1355       va_start(ap, fmt);
1356       len = Bvsnprintf(more.c_str(), maxlen, fmt, ap);
1357       va_end(ap);
1358 
1359       if (len < 0 || len >= (maxlen - 5)) {
1360          more.ReallocPm(maxlen + maxlen / 2);
1361          continue;
1362       }
1363 
1364       break;
1365    }
1366 
1367 #ifdef FULL_LOCATION
1368    if (level >= 0) {
1369       pt_out(buf.c_str());
1370    }
1371 #endif
1372 
1373    pt_out(more.c_str());
1374 }
1375 
1376 /*
1377  * This subroutine prints a message regardless of the debug level
1378  *
1379  * If the level is negative, the details of file and line number are not printed.
1380  * Special version of p_msg used with fixed buffers.
1381  */
p_msg_fb(const char * file,int line,int level,const char * fmt,...)1382 void p_msg_fb(const char *file, int line, int level, const char *fmt,...)
1383 {
1384    char buf[256];
1385    int len = 0;
1386    va_list arg_ptr;
1387 
1388 #ifdef FULL_LOCATION
1389    if (level >= 0) {
1390       len = Bsnprintf(buf, sizeof(buf), "%s: %s:%d-%u ",
1391                       my_name, get_basename(file), line,
1392                       GetJobidFromTsd());
1393    }
1394 #endif
1395 
1396    va_start(arg_ptr, fmt);
1397    Bvsnprintf(buf + len, sizeof(buf) - len, (char *)fmt, arg_ptr);
1398    va_end(arg_ptr);
1399 
1400    pt_out(buf);
1401 }
1402 
1403 /*
1404  * Subroutine writes a debug message to the trace file if the level number is less than
1405  * or equal the debug_level. File and line numbers are included for more detail if desired,
1406  * but not currently printed.
1407  *
1408  * If the level is negative, the details of file and line number are not printed.
1409  */
t_msg(const char * file,int line,int level,const char * fmt,...)1410 void t_msg(const char *file, int line, int level, const char *fmt,...)
1411 {
1412    va_list ap;
1413    int len, maxlen;
1414    bool details = true;
1415    PoolMem buf(PM_EMSG),
1416             more(PM_EMSG);
1417 
1418    if (level < 0) {
1419       details = false;
1420       level = -level;
1421    }
1422 
1423    if (level <= debug_level) {
1424       if (!trace_fd) {
1425          PoolMem fn(PM_FNAME);
1426 
1427          Mmsg(fn, "%s/%s.trace", TRACEFILEDIRECTORY, my_name);
1428          trace_fd = fopen(fn.c_str(), "a+b");
1429       }
1430 
1431 #ifdef FULL_LOCATION
1432       if (details) {
1433          Mmsg(buf,  "%s: %s:%d-%u ", my_name, get_basename(file), line, GetJobidFromTsd());
1434       }
1435 #endif
1436 
1437       while (1) {
1438          maxlen = more.MaxSize() - 1;
1439          va_start(ap, fmt);
1440          len = Bvsnprintf(more.c_str(), maxlen, fmt, ap);
1441          va_end(ap);
1442 
1443          if (len < 0 || len >= (maxlen - 5)) {
1444             more.ReallocPm(maxlen + maxlen / 2);
1445             continue;
1446          }
1447 
1448          break;
1449       }
1450 
1451       if (trace_fd != NULL) {
1452 #ifdef FULL_LOCATION
1453          if (details) {
1454             fputs(buf.c_str(), trace_fd);
1455          }
1456 #endif
1457          fputs(more.c_str(), trace_fd);
1458          fflush(trace_fd);
1459       }
1460    }
1461 }
1462 
1463 /*
1464  * print an error message
1465  */
e_msg(const char * file,int line,int type,int level,const char * fmt,...)1466 void e_msg(const char *file, int line, int type, int level, const char *fmt,...)
1467 {
1468    va_list ap;
1469    int len, maxlen;
1470    PoolMem buf(PM_EMSG),
1471             more(PM_EMSG),
1472             typestr(PM_EMSG);
1473 
1474    switch (type) {
1475    case M_ABORT:
1476       Mmsg(typestr, "ABORT");
1477       Mmsg(buf, _("%s: ABORTING due to ERROR in %s:%d\n"), my_name, get_basename(file), line);
1478       break;
1479    case M_ERROR_TERM:
1480       Mmsg(typestr, "ERROR TERMINATION");
1481       Mmsg(buf, _("%s: ERROR TERMINATION at %s:%d\n"), my_name, get_basename(file), line);
1482       break;
1483    case M_FATAL:
1484       Mmsg(typestr, "FATAL ERROR");
1485       if (level == -1)            /* skip details */
1486          Mmsg(buf, _("%s: Fatal Error because: "), my_name);
1487       else
1488          Mmsg(buf, _("%s: Fatal Error at %s:%d because:\n"), my_name, get_basename(file), line);
1489       break;
1490    case M_ERROR:
1491       Mmsg(typestr, "ERROR");
1492       if (level == -1)            /* skip details */
1493          Mmsg(buf, _("%s: ERROR: "), my_name);
1494       else
1495          Mmsg(buf, _("%s: ERROR in %s:%d "), my_name, get_basename(file), line);
1496       break;
1497    case M_WARNING:
1498       Mmsg(typestr, "WARNING");
1499       Mmsg(buf, _("%s: Warning: "), my_name);
1500       break;
1501    case M_SECURITY:
1502       Mmsg(typestr, "Security violation");
1503       Mmsg(buf, _("%s: Security violation: "), my_name);
1504       break;
1505    default:
1506       Mmsg(buf, "%s: ", my_name);
1507       break;
1508    }
1509 
1510    while (1) {
1511       maxlen = more.MaxSize() - 1;
1512       va_start(ap, fmt);
1513       len = Bvsnprintf(more.c_str(), maxlen, fmt, ap);
1514       va_end(ap);
1515 
1516       if (len < 0 || len >= (maxlen - 5)) {
1517          more.ReallocPm(maxlen + maxlen / 2);
1518          continue;
1519       }
1520 
1521       break;
1522    }
1523 
1524    /*
1525     * show error message also as debug message (level 10)
1526     */
1527    d_msg(file, line, 10, "%s: %s", typestr.c_str(), more.c_str());
1528 
1529    /*
1530     * Check if we have a message destination defined.
1531     * We always report M_ABORT and M_ERROR_TERM
1532     */
1533    if (!daemon_msgs || ((type != M_ABORT && type != M_ERROR_TERM) &&
1534                         !BitIsSet(type, daemon_msgs->SendMsg))) {
1535       return;                        /* no destination */
1536    }
1537 
1538    PmStrcat(buf, more.c_str());
1539    DispatchMessage(NULL, type, 0, buf.c_str());
1540 
1541    if (type == M_ABORT) {
1542       char *p = 0;
1543       p[0] = 0;                      /* generate segmentation violation */
1544    }
1545 
1546    if (type == M_ERROR_TERM) {
1547       exit(1);
1548    }
1549 }
1550 
1551 /*
1552  * Generate a Job message
1553  */
Jmsg(JobControlRecord * jcr,int type,utime_t mtime,const char * fmt,...)1554 void Jmsg(JobControlRecord *jcr, int type, utime_t mtime, const char *fmt,...)
1555 {
1556    va_list ap;
1557    MessagesResource *msgs;
1558    int len, maxlen;
1559    uint32_t JobId = 0;
1560    PoolMem buf(PM_EMSG),
1561             more(PM_EMSG);
1562 
1563    Dmsg1(850, "Enter Jmsg type=%d\n", type);
1564 
1565    /*
1566     * Special case for the console, which has a dir_bsock and JobId == 0,
1567     * in that case, we send the message directly back to the
1568     * dir_bsock.
1569     */
1570    if (jcr && jcr->JobId == 0 && jcr->dir_bsock) {
1571       BareosSocket *dir = jcr->dir_bsock;
1572 
1573       va_start(ap, fmt);
1574       dir->message_length = Bvsnprintf(dir->msg, SizeofPoolMemory(dir->msg), fmt, ap);
1575       va_end(ap);
1576       jcr->dir_bsock->send();
1577 
1578       return;
1579    }
1580 
1581    /*
1582     * The watchdog thread can't use Jmsg directly, we always queued it
1583     */
1584    if (IsWatchdog()) {
1585       while (1) {
1586          maxlen = buf.MaxSize() - 1;
1587          va_start(ap, fmt);
1588          len = Bvsnprintf(buf.c_str(), maxlen, fmt, ap);
1589          va_end(ap);
1590 
1591          if (len < 0 || len >= (maxlen - 5)) {
1592             buf.ReallocPm(maxlen + maxlen / 2);
1593             continue;
1594          }
1595 
1596          break;
1597       }
1598       Qmsg(jcr, type, mtime, "%s", buf.c_str());
1599 
1600       return;
1601    }
1602 
1603    msgs = NULL;
1604    if (!jcr) {
1605       jcr = get_jcr_from_tsd();
1606    }
1607 
1608    if (jcr) {
1609       /*
1610        * Dequeue messages to keep the original order
1611        */
1612       if (!jcr->dequeuing_msgs) {    /* Avoid recursion */
1613          DequeueMessages(jcr);
1614       }
1615       msgs = jcr->jcr_msgs;
1616       JobId = jcr->JobId;
1617    }
1618 
1619    if (!msgs) {
1620       msgs = daemon_msgs;            /* if no jcr, we use daemon handler */
1621    }
1622 
1623    /*
1624     * Check if we have a message destination defined.
1625     * We always report M_ABORT and M_ERROR_TERM
1626     */
1627    if (msgs && (type != M_ABORT && type != M_ERROR_TERM) && !BitIsSet(type, msgs->SendMsg)) {
1628       return;                        /* no destination */
1629    }
1630 
1631    switch (type) {
1632    case M_ABORT:
1633       Mmsg(buf, _("%s ABORTING due to ERROR\n"), my_name);
1634       break;
1635    case M_ERROR_TERM:
1636       Mmsg(buf, _("%s ERROR TERMINATION\n"), my_name);
1637       break;
1638    case M_FATAL:
1639       Mmsg(buf, _("%s JobId %u: Fatal error: "), my_name, JobId);
1640       if (jcr) {
1641          jcr->setJobStatus(JS_FatalError);
1642       }
1643       if (jcr && jcr->JobErrors == 0) {
1644          jcr->JobErrors = 1;
1645       }
1646       break;
1647    case M_ERROR:
1648       Mmsg(buf, _("%s JobId %u: Error: "), my_name, JobId);
1649       if (jcr) {
1650          jcr->JobErrors++;
1651       }
1652       break;
1653    case M_WARNING:
1654       Mmsg(buf, _("%s JobId %u: Warning: "), my_name, JobId);
1655       if (jcr) {
1656          jcr->JobWarnings++;
1657       }
1658       break;
1659    case M_SECURITY:
1660       Mmsg(buf, _("%s JobId %u: Security violation: "), my_name, JobId);
1661       break;
1662    default:
1663       Mmsg(buf, "%s JobId %u: ", my_name, JobId);
1664       break;
1665    }
1666 
1667    while (1) {
1668       maxlen = more.MaxSize() - 1;
1669       va_start(ap, fmt);
1670       len = Bvsnprintf(more.c_str(), maxlen, fmt, ap);
1671       va_end(ap);
1672 
1673       if (len < 0 || len >= (maxlen - 5)) {
1674          more.ReallocPm(maxlen + maxlen / 2);
1675          continue;
1676       }
1677 
1678       break;
1679    }
1680 
1681    PmStrcat(buf, more.c_str());
1682    DispatchMessage(jcr, type, mtime, buf.c_str());
1683 
1684    if (type == M_ABORT){
1685       char *p = 0;
1686       printf("BAREOS forced SEG FAULT to obtain traceback.\n");
1687       syslog(LOG_DAEMON | LOG_ERR, "BAREOS forced SEG FAULT to obtain traceback.\n");
1688       p[0] = 0;                      /* generate segmentation violation */
1689    }
1690 
1691    if (type == M_ERROR_TERM) {
1692       exit(1);
1693    }
1694 }
1695 
1696 /*
1697  * If we come here, prefix the message with the file:line-number,
1698  * then pass it on to the normal Jmsg routine.
1699  */
j_msg(const char * file,int line,JobControlRecord * jcr,int type,utime_t mtime,const char * fmt,...)1700 void j_msg(const char *file, int line, JobControlRecord *jcr, int type, utime_t mtime, const char *fmt,...)
1701 {
1702    va_list ap;
1703    int len, maxlen;
1704    PoolMem buf(PM_EMSG),
1705             more(PM_EMSG);
1706 
1707    Mmsg(buf, "%s:%d ", get_basename(file), line);
1708    while (1) {
1709       maxlen = more.MaxSize() - 1;
1710       va_start(ap, fmt);
1711       len = Bvsnprintf(more.c_str(), maxlen, fmt, ap);
1712       va_end(ap);
1713 
1714       if (len < 0 || len >= (maxlen - 5)) {
1715          more.ReallocPm(maxlen + maxlen / 2);
1716          continue;
1717       }
1718 
1719       break;
1720    }
1721 
1722    PmStrcat(buf, more.c_str());
1723 
1724    Jmsg(jcr, type, mtime, "%s", buf.c_str());
1725 }
1726 
1727 /*
1728  * Edit a message into a Pool memory buffer, with file:lineno
1729  */
msg_(const char * file,int line,POOLMEM * & pool_buf,const char * fmt,...)1730 int msg_(const char *file, int line, POOLMEM *&pool_buf, const char *fmt, ...)
1731 {
1732    va_list ap;
1733    int len, maxlen;
1734    PoolMem buf(PM_EMSG),
1735             more(PM_EMSG);
1736 
1737    Mmsg(buf, "%s:%d ", get_basename(file), line);
1738    while (1) {
1739       maxlen = more.MaxSize() - 1;
1740       va_start(ap, fmt);
1741       len = Bvsnprintf(more.c_str(), maxlen, fmt, ap);
1742       va_end(ap);
1743 
1744       if (len < 0 || len >= (maxlen - 5)) {
1745          more.ReallocPm(maxlen + maxlen / 2);
1746          continue;
1747       }
1748 
1749       break;
1750    }
1751 
1752    PmStrcpy(pool_buf, buf.c_str());
1753    len = PmStrcat(pool_buf, more.c_str());
1754 
1755    return len;
1756 }
1757 
1758 /*
1759  * Edit a message into a Pool Memory buffer NO file:lineno
1760  *
1761  * Returns: string length of what was edited.
1762  *          -1 when the buffer isn't enough to hold the edited string.
1763  */
Mmsg(POOLMEM * & pool_buf,const char * fmt,...)1764 int Mmsg(POOLMEM *&pool_buf, const char *fmt, ...)
1765 {
1766    int len, maxlen;
1767    va_list ap;
1768 
1769    while (1) {
1770       maxlen = SizeofPoolMemory(pool_buf) - 1;
1771       va_start(ap, fmt);
1772       len = Bvsnprintf(pool_buf, maxlen, fmt, ap);
1773       va_end(ap);
1774 
1775       if (len < 0 || len >= (maxlen - 5)) {
1776          pool_buf = ReallocPoolMemory(pool_buf, maxlen + maxlen / 2);
1777          continue;
1778       }
1779 
1780       break;
1781    }
1782 
1783    return len;
1784 }
1785 
Mmsg(PoolMem & pool_buf,const char * fmt,...)1786 int Mmsg(PoolMem &pool_buf, const char *fmt, ...)
1787 {
1788    int len, maxlen;
1789    va_list ap;
1790 
1791    while (1) {
1792       maxlen = pool_buf.MaxSize() - 1;
1793       va_start(ap, fmt);
1794       len = Bvsnprintf(pool_buf.c_str(), maxlen, fmt, ap);
1795       va_end(ap);
1796 
1797       if (len < 0 || len >= (maxlen - 5)) {
1798          pool_buf.ReallocPm(maxlen + maxlen / 2);
1799          continue;
1800       }
1801 
1802       break;
1803    }
1804 
1805    return len;
1806 }
1807 
Mmsg(PoolMem * & pool_buf,const char * fmt,...)1808 int Mmsg(PoolMem *&pool_buf, const char *fmt, ...)
1809 {
1810    int len, maxlen;
1811    va_list ap;
1812 
1813    while (1) {
1814       maxlen = pool_buf->MaxSize() - 1;
1815       va_start(ap, fmt);
1816       len = Bvsnprintf(pool_buf->c_str(), maxlen, fmt, ap);
1817       va_end(ap);
1818 
1819       if (len < 0 || len >= (maxlen - 5)) {
1820          pool_buf->ReallocPm(maxlen + maxlen / 2);
1821          continue;
1822       }
1823 
1824       break;
1825    }
1826 
1827    return len;
1828 }
1829 
1830 /*
1831  * We queue messages rather than print them directly. This
1832  * is generally used in low level routines (msg handler, bnet)
1833  * to prevent recursion (i.e. if you are in the middle of
1834  * sending a message, it is a bit messy to recursively call
1835  * yourself when the bnet packet is not reentrant).
1836  */
Qmsg(JobControlRecord * jcr,int type,utime_t mtime,const char * fmt,...)1837 void Qmsg(JobControlRecord *jcr, int type, utime_t mtime, const char *fmt,...)
1838 {
1839    va_list ap;
1840    int len, maxlen;
1841    PoolMem buf(PM_EMSG);
1842    MessageQeueItem *item;
1843 
1844    while (1) {
1845       maxlen = buf.MaxSize() - 1;
1846       va_start(ap, fmt);
1847       len = Bvsnprintf(buf.c_str(), maxlen, fmt, ap);
1848       va_end(ap);
1849 
1850       if (len < 0 || len >= (maxlen - 5)) {
1851          buf.ReallocPm(maxlen + maxlen / 2);
1852          continue;
1853       }
1854 
1855       break;
1856    }
1857 
1858    item = (MessageQeueItem *)malloc(sizeof(MessageQeueItem) + len + 1);
1859    item->type = type;
1860    item->mtime = time(NULL);
1861    strcpy(item->msg, buf.c_str());
1862 
1863    if (!jcr) {
1864       jcr = get_jcr_from_tsd();
1865    }
1866 
1867    /*
1868     * If no jcr  or no JobId or no queue or dequeuing send to syslog
1869     */
1870    if (!jcr || !jcr->JobId || !jcr->msg_queue || jcr->dequeuing_msgs) {
1871       syslog(LOG_DAEMON|LOG_ERR, "%s", item->msg);
1872       free(item);
1873    } else {
1874       /*
1875        * Queue message for later sending
1876        */
1877       P(jcr->msg_queue_mutex);
1878       jcr->msg_queue->append(item);
1879       V(jcr->msg_queue_mutex);
1880    }
1881 }
1882 
1883 /*
1884  * Dequeue messages
1885  */
DequeueMessages(JobControlRecord * jcr)1886 void DequeueMessages(JobControlRecord *jcr)
1887 {
1888    MessageQeueItem *item;
1889 
1890    if (!jcr->msg_queue) {
1891       return;
1892    }
1893 
1894    P(jcr->msg_queue_mutex);
1895    jcr->dequeuing_msgs = true;
1896    foreach_dlist(item, jcr->msg_queue) {
1897       Jmsg(jcr, item->type, item->mtime, "%s", item->msg);
1898    }
1899 
1900    /*
1901     * Remove messages just sent
1902     */
1903    jcr->msg_queue->destroy();
1904    jcr->dequeuing_msgs = false;
1905    V(jcr->msg_queue_mutex);
1906 }
1907 
1908 /*
1909  * If we come here, prefix the message with the file:line-number,
1910  * then pass it on to the normal Qmsg routine.
1911  */
q_msg(const char * file,int line,JobControlRecord * jcr,int type,utime_t mtime,const char * fmt,...)1912 void q_msg(const char *file, int line, JobControlRecord *jcr, int type, utime_t mtime, const char *fmt,...)
1913 {
1914    va_list ap;
1915    int len, maxlen;
1916    PoolMem buf(PM_EMSG),
1917             more(PM_EMSG);
1918 
1919    Mmsg(buf, "%s:%d ", get_basename(file), line);
1920    while (1) {
1921       maxlen = more.MaxSize() - 1;
1922       va_start(ap, fmt);
1923       len = Bvsnprintf(more.c_str(), maxlen, fmt, ap);
1924       va_end(ap);
1925 
1926       if (len < 0 || len >= (maxlen - 5)) {
1927          more.ReallocPm(maxlen + maxlen / 2);
1928          continue;
1929       }
1930 
1931       break;
1932    }
1933 
1934    PmStrcat(buf, more.c_str());
1935 
1936    Qmsg(jcr, type, mtime, "%s", buf.c_str());
1937 }
1938 
1939 /*
1940  * Set gobal date format used for log messages.
1941  */
SetLogTimestampFormat(const char * format)1942 void SetLogTimestampFormat(const char *format)
1943 {
1944    log_timestamp_format = format;
1945 }
1946