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