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