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