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