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