]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/message.c
03074b5487a31987a93d845781f2d11c63dbd346
[bacula/bacula] / bacula / src / lib / message.c
1 /*
2  * Bacula message handling routines
3  *
4  *   Kern Sibbald, April 2000
5  *
6  *   Version $Id$
7  *
8  */
9 /*
10    Copyright (C) 2000-2006 Kern Sibbald
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License
14    version 2 as amended with additional clauses defined in the
15    file LICENSE in the main source directory.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
20    the file LICENSE for additional details.
21
22  */
23
24
25
26 #include "bacula.h"
27 #include "jcr.h"
28
29 sql_query p_sql_query = NULL;
30
31 #define FULL_LOCATION 1               /* set for file:line in Debug messages */
32
33 /*
34  *  This is where we define "Globals" because all the
35  *    daemons include this file.
36  */
37 const char *working_directory = NULL;       /* working directory path stored here */
38 int verbose = 0;                      /* increase User messages */
39 int debug_level = 0;                  /* debug level */
40 time_t daemon_start_time = 0;         /* Daemon start time */
41 const char *version = VERSION " (" BDATE ")";
42 char my_name[30];                     /* daemon name is stored here */
43 char *exepath = (char *)NULL;
44 char *exename = (char *)NULL;
45 int console_msg_pending = 0;
46 char con_fname[500];                  /* Console filename */
47 FILE *con_fd = NULL;                  /* Console file descriptor */
48 brwlock_t con_lock;                   /* Console lock structure */
49
50 static char *catalog_db = NULL;       /* database type */
51
52 const char *host_os = HOST_OS;
53 const char *distname = DISTNAME;
54 const char *distver = DISTVER;
55 static FILE *trace_fd = NULL;
56 #if defined(HAVE_WIN32)
57 static bool trace = true;
58 #else
59 static bool trace = false;
60 #endif
61
62 /* Forward referenced functions */
63
64 /* Imported functions */
65
66
67 /* Static storage */
68
69 /* Used to allow only one thread close the daemon messages at a time */
70 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
71 static MSGS *daemon_msgs;              /* global messages */
72
73
74 /*
75  * Set daemon name. Also, find canonical execution
76  *  path.  Note, exepath has spare room for tacking on
77  *  the exename so that we can reconstruct the full name.
78  *
79  * Note, this routine can get called multiple times
80  *  The second time is to put the name as found in the
81  *  Resource record. On the second call, generally,
82  *  argv is NULL to avoid doing the path code twice.
83  */
84 #define BTRACE_EXTRA 20
85 void my_name_is(int argc, char *argv[], const char *name)
86 {
87    char *l, *p, *q;
88    char cpath[1024];
89    int len;
90
91    bstrncpy(my_name, name, sizeof(my_name));
92    if (argc>0 && argv && argv[0]) {
93       /* strip trailing filename and save exepath */
94       for (l=p=argv[0]; *p; p++) {
95          if (*p == '/') {
96             l = p;                       /* set pos of last slash */
97          }
98       }
99       if (*l == '/') {
100          l++;
101       } else {
102          l = argv[0];
103 #if defined(HAVE_WIN32)
104          /* On Windows allow c: junk */
105          if (l[1] == ':') {
106             l += 2;
107          }
108 #endif
109       }
110       len = strlen(l) + 1;
111       if (exename) {
112          free(exename);
113       }
114       exename = (char *)malloc(len);
115       strcpy(exename, l);
116
117       if (exepath) {
118          free(exepath);
119       }
120       exepath = (char *)malloc(strlen(argv[0]) + 1 + len);
121       for (p=argv[0],q=exepath; p < l; ) {
122          *q++ = *p++;
123       }
124       *q = 0;
125       if (strchr(exepath, '.') || exepath[0] != '/') {
126          if (getcwd(cpath, sizeof(cpath))) {
127             free(exepath);
128             exepath = (char *)malloc(strlen(cpath) + 1 + len);
129             strcpy(exepath, cpath);
130          }
131       }
132       Dmsg2(500, "exepath=%s\nexename=%s\n", exepath, exename);
133    }
134 }
135
136 const char *
137 get_db_type(void)
138 {
139    return catalog_db != NULL ? catalog_db : "unknown";
140 }
141
142 void
143 set_db_type(const char *name)
144 {
145    if (catalog_db != NULL)
146    {
147       free(catalog_db);
148    }
149
150    catalog_db = bstrdup(name);
151 }
152
153 /*
154  * Initialize message handler for a daemon or a Job
155  *   We make a copy of the MSGS resource passed, so it belows
156  *   to the job or daemon and thus can be modified.
157  *
158  *   NULL for jcr -> initialize global messages for daemon
159  *   non-NULL     -> initialize jcr using Message resource
160  */
161 void
162 init_msg(JCR *jcr, MSGS *msg)
163 {
164    DEST *d, *dnew, *temp_chain = NULL;
165    int i;
166
167    if (jcr == NULL && msg == NULL) {
168       init_last_jobs_list();
169    }
170
171 #if !defined(HAVE_WIN32)
172    /*
173     * Make sure we have fd's 0, 1, 2 open
174     *  If we don't do this one of our sockets may open
175     *  there and if we then use stdout, it could
176     *  send total garbage to our socket.
177     *
178     */
179    int fd;
180    fd = open("/dev/null", O_RDONLY, 0644);
181    if (fd > 2) {
182       close(fd);
183    } else {
184       for(i=1; fd + i <= 2; i++) {
185          dup2(fd, fd+i);
186       }
187    }
188
189 #endif
190    /*
191     * If msg is NULL, initialize global chain for STDOUT and syslog
192     */
193    if (msg == NULL) {
194       daemon_msgs = (MSGS *)malloc(sizeof(MSGS));
195       memset(daemon_msgs, 0, sizeof(MSGS));
196       for (i=1; i<=M_MAX; i++) {
197          add_msg_dest(daemon_msgs, MD_STDOUT, i, NULL, NULL);
198       }
199       Dmsg1(050, "Create daemon global message resource %p\n", daemon_msgs);
200       return;
201    }
202
203    /*
204     * Walk down the message resource chain duplicating it
205     * for the current Job.
206     */
207    for (d=msg->dest_chain; d; d=d->next) {
208       dnew = (DEST *)malloc(sizeof(DEST));
209       memcpy(dnew, d, sizeof(DEST));
210       dnew->next = temp_chain;
211       dnew->fd = NULL;
212       dnew->mail_filename = NULL;
213       if (d->mail_cmd) {
214          dnew->mail_cmd = bstrdup(d->mail_cmd);
215       }
216       if (d->where) {
217          dnew->where = bstrdup(d->where);
218       }
219       temp_chain = dnew;
220    }
221
222    if (jcr) {
223       jcr->jcr_msgs = (MSGS *)malloc(sizeof(MSGS));
224       memset(jcr->jcr_msgs, 0, sizeof(MSGS));
225       jcr->jcr_msgs->dest_chain = temp_chain;
226       memcpy(jcr->jcr_msgs->send_msg, msg->send_msg, sizeof(msg->send_msg));
227    } else {
228       /* If we have default values, release them now */
229       if (daemon_msgs) {
230          free_msgs_res(daemon_msgs);
231       }
232       daemon_msgs = (MSGS *)malloc(sizeof(MSGS));
233       memset(daemon_msgs, 0, sizeof(MSGS));
234       daemon_msgs->dest_chain = temp_chain;
235       memcpy(daemon_msgs->send_msg, msg->send_msg, sizeof(msg->send_msg));
236    }
237    Dmsg2(250, "Copy message resource %p to %p\n", msg, temp_chain);
238
239 }
240
241 /* Initialize so that the console (User Agent) can
242  * receive messages -- stored in a file.
243  */
244 void init_console_msg(const char *wd)
245 {
246    int fd;
247
248    bsnprintf(con_fname, sizeof(con_fname), "%s/%s.conmsg", wd, my_name);
249    fd = open(con_fname, O_CREAT|O_RDWR|O_BINARY, 0600);
250    if (fd == -1) {
251       berrno be;
252       Emsg2(M_ERROR_TERM, 0, _("Could not open console message file %s: ERR=%s\n"),
253           con_fname, be.strerror());
254    }
255    if (lseek(fd, 0, SEEK_END) > 0) {
256       console_msg_pending = 1;
257    }
258    close(fd);
259    con_fd = fopen(con_fname, "a+b");
260    if (!con_fd) {
261       berrno be;
262       Emsg2(M_ERROR, 0, _("Could not open console message file %s: ERR=%s\n"),
263           con_fname, be.strerror());
264    }
265    if (rwl_init(&con_lock) != 0) {
266       berrno be;
267       Emsg1(M_ERROR_TERM, 0, _("Could not get con mutex: ERR=%s\n"),
268          be.strerror());
269    }
270 }
271
272 /*
273  * Called only during parsing of the config file.
274  *
275  * Add a message destination. I.e. associate a message type with
276  *  a destination (code).
277  * Note, where in the case of dest_code FILE is a filename,
278  *  but in the case of MAIL is a space separated list of
279  *  email addresses, ...
280  */
281 void add_msg_dest(MSGS *msg, int dest_code, int msg_type, char *where, char *mail_cmd)
282 {
283    DEST *d;
284    /*
285     * First search the existing chain and see if we
286     * can simply add this msg_type to an existing entry.
287     */
288    for (d=msg->dest_chain; d; d=d->next) {
289       if (dest_code == d->dest_code && ((where == NULL && d->where == NULL) ||
290                      (strcmp(where, d->where) == 0))) {
291          Dmsg4(850, "Add to existing d=%p msgtype=%d destcode=%d where=%s\n",
292              d, msg_type, dest_code, NPRT(where));
293          set_bit(msg_type, d->msg_types);
294          set_bit(msg_type, msg->send_msg);  /* set msg_type bit in our local */
295          return;
296       }
297    }
298    /* Not found, create a new entry */
299    d = (DEST *)malloc(sizeof(DEST));
300    memset(d, 0, sizeof(DEST));
301    d->next = msg->dest_chain;
302    d->dest_code = dest_code;
303    set_bit(msg_type, d->msg_types);      /* set type bit in structure */
304    set_bit(msg_type, msg->send_msg);     /* set type bit in our local */
305    if (where) {
306       d->where = bstrdup(where);
307    }
308    if (mail_cmd) {
309       d->mail_cmd = bstrdup(mail_cmd);
310    }
311    Dmsg5(850, "add new d=%p msgtype=%d destcode=%d where=%s mailcmd=%s\n",
312           d, msg_type, dest_code, NPRT(where), NPRT(d->mail_cmd));
313    msg->dest_chain = d;
314 }
315
316 /*
317  * Called only during parsing of the config file.
318  *
319  * Remove a message destination
320  */
321 void rem_msg_dest(MSGS *msg, int dest_code, int msg_type, char *where)
322 {
323    DEST *d;
324
325    for (d=msg->dest_chain; d; d=d->next) {
326       Dmsg2(850, "Remove_msg_dest d=%p where=%s\n", d, NPRT(d->where));
327       if (bit_is_set(msg_type, d->msg_types) && (dest_code == d->dest_code) &&
328           ((where == NULL && d->where == NULL) ||
329                      (strcmp(where, d->where) == 0))) {
330          Dmsg3(850, "Found for remove d=%p msgtype=%d destcode=%d\n",
331                d, msg_type, dest_code);
332          clear_bit(msg_type, d->msg_types);
333          Dmsg0(850, "Return rem_msg_dest\n");
334          return;
335       }
336    }
337 }
338
339
340 /*
341  * Create a unique filename for the mail command
342  */
343 static void make_unique_mail_filename(JCR *jcr, POOLMEM *&name, DEST *d)
344 {
345    if (jcr) {
346       Mmsg(name, "%s/%s.%s.%d.mail", working_directory, my_name,
347                  jcr->Job, (int)(long)d);
348    } else {
349       Mmsg(name, "%s/%s.%s.%d.mail", working_directory, my_name,
350                  my_name, (int)(long)d);
351    }
352    Dmsg1(850, "mailname=%s\n", name);
353 }
354
355 /*
356  * Open a mail pipe
357  */
358 static BPIPE *open_mail_pipe(JCR *jcr, POOLMEM *&cmd, DEST *d)
359 {
360    BPIPE *bpipe;
361
362    if (d->mail_cmd) {
363       cmd = edit_job_codes(jcr, cmd, d->mail_cmd, d->where);
364    } else {
365       Mmsg(cmd, "/usr/lib/sendmail -F Bacula %s", d->where);
366    }
367    fflush(stdout);
368
369    if (!(bpipe = open_bpipe(cmd, 120, "rw"))) {
370       berrno be;
371       Jmsg(jcr, M_ERROR, 0, _("open mail pipe %s failed: ERR=%s\n"),
372          cmd, be.strerror());
373    }
374
375    /* If we had to use sendmail, add subject */
376    if (!d->mail_cmd) {
377        fprintf(bpipe->wfd, "Subject: %s\r\n\r\n", _("Bacula Message"));
378    }
379
380    return bpipe;
381 }
382
383 /*
384  * Close the messages for this Messages resource, which means to close
385  *  any open files, and dispatch any pending email messages.
386  */
387 void close_msg(JCR *jcr)
388 {
389    MSGS *msgs;
390    DEST *d;
391    BPIPE *bpipe;
392    POOLMEM *cmd, *line;
393    int len, stat;
394
395    Dmsg1(580, "Close_msg jcr=%p\n", jcr);
396
397    if (jcr == NULL) {                /* NULL -> global chain */
398       msgs = daemon_msgs;
399       P(mutex);                       /* only one thread walking the chain */
400    } else {
401       msgs = jcr->jcr_msgs;
402       jcr->jcr_msgs = NULL;
403    }
404    if (msgs == NULL) {
405       return;
406    }
407    Dmsg1(850, "===Begin close msg resource at %p\n", msgs);
408    cmd = get_pool_memory(PM_MESSAGE);
409    for (d=msgs->dest_chain; d; ) {
410       if (d->fd) {
411          switch (d->dest_code) {
412          case MD_FILE:
413          case MD_APPEND:
414             if (d->fd) {
415                fclose(d->fd);            /* close open file descriptor */
416             }
417             break;
418          case MD_MAIL:
419          case MD_MAIL_ON_ERROR:
420             Dmsg0(850, "Got MD_MAIL or MD_MAIL_ON_ERROR\n");
421             if (!d->fd) {
422                break;
423             }
424             if (d->dest_code == MD_MAIL_ON_ERROR && jcr &&
425                 jcr->JobStatus == JS_Terminated) {
426                goto rem_temp_file;
427             }
428
429             if (!(bpipe=open_mail_pipe(jcr, cmd, d))) {
430                Pmsg0(000, _("open mail pipe failed.\n"));
431                goto rem_temp_file;
432             }
433             Dmsg0(850, "Opened mail pipe\n");
434             len = d->max_len+10;
435             line = get_memory(len);
436             rewind(d->fd);
437             while (fgets(line, len, d->fd)) {
438                fputs(line, bpipe->wfd);
439             }
440             if (!close_wpipe(bpipe)) {       /* close write pipe sending mail */
441                berrno be;
442                Pmsg1(000, _("close error: ERR=%s\n"), be.strerror());
443             }
444
445             /*
446              * Since we are closing all messages, before "recursing"
447              * make sure we are not closing the daemon messages, otherwise
448              * kaboom.
449              */
450             if (msgs != daemon_msgs) {
451                /* read what mail prog returned -- should be nothing */
452                while (fgets(line, len, bpipe->rfd)) {
453                   Jmsg1(jcr, M_INFO, 0, _("Mail prog: %s"), line);
454                }
455             }
456
457             stat = close_bpipe(bpipe);
458             if (stat != 0 && msgs != daemon_msgs) {
459                berrno be;
460                be.set_errno(stat);
461                Dmsg1(850, "Calling emsg. CMD=%s\n", cmd);
462                Jmsg2(jcr, M_ERROR, 0, _("Mail program terminated in error.\n"
463                                         "CMD=%s\n"
464                                         "ERR=%s\n"), cmd, be.strerror());
465             }
466             free_memory(line);
467 rem_temp_file:
468             /* Remove temp file */
469             fclose(d->fd);
470             unlink(d->mail_filename);
471             free_pool_memory(d->mail_filename);
472             d->mail_filename = NULL;
473             Dmsg0(850, "end mail or mail on error\n");
474             break;
475          default:
476             break;
477          }
478          d->fd = NULL;
479       }
480       d = d->next;                    /* point to next buffer */
481    }
482    free_pool_memory(cmd);
483    Dmsg0(850, "Done walking message chain.\n");
484    if (jcr) {
485       free_msgs_res(msgs);
486       msgs = NULL;
487    } else {
488       V(mutex);
489    }
490    Dmsg0(850, "===End close msg resource\n");
491 }
492
493 /*
494  * Free memory associated with Messages resource
495  */
496 void free_msgs_res(MSGS *msgs)
497 {
498    DEST *d, *old;
499
500    /* Walk down the message chain releasing allocated buffers */
501    for (d=msgs->dest_chain; d; ) {
502       if (d->where) {
503          free(d->where);
504       }
505       if (d->mail_cmd) {
506          free(d->mail_cmd);
507       }
508       old = d;                        /* save pointer to release */
509       d = d->next;                    /* point to next buffer */
510       free(old);                      /* free the destination item */
511    }
512    msgs->dest_chain = NULL;
513    free(msgs);                        /* free the head */
514 }
515
516
517 /*
518  * Terminate the message handler for good.
519  * Release the global destination chain.
520  *
521  * Also, clean up a few other items (cons, exepath). Note,
522  *   these really should be done elsewhere.
523  */
524 void term_msg()
525 {
526    Dmsg0(850, "Enter term_msg\n");
527    close_msg(NULL);                   /* close global chain */
528    free_msgs_res(daemon_msgs);        /* free the resources */
529    daemon_msgs = NULL;
530    if (con_fd) {
531       fflush(con_fd);
532       fclose(con_fd);
533       con_fd = NULL;
534    }
535    if (exepath) {
536       free(exepath);
537       exepath = NULL;
538    }
539    if (exename) {
540       free(exename);
541       exename = NULL;
542    }
543    if (trace_fd) {
544       fclose(trace_fd);
545       trace_fd = NULL;
546    }
547    term_last_jobs_list();
548 }
549
550 static bool open_dest_file(JCR *jcr, DEST *d, const char *mode) 
551 {
552    d->fd = fopen(d->where, mode);
553    if (!d->fd) {
554       berrno be;
555       d->fd = stdout;
556       Qmsg2(jcr, M_ERROR, 0, _("fopen %s failed: ERR=%s\n"), d->where,
557             be.strerror());
558       d->fd = NULL;
559       return false;
560    }
561    return true;
562 }
563
564 /*
565  * Handle sending the message to the appropriate place
566  */
567 void dispatch_message(JCR *jcr, int type, time_t mtime, char *msg)
568 {
569     DEST *d;
570     char dt[MAX_TIME_LENGTH];
571     POOLMEM *mcmd;
572     int len, dtlen;
573     MSGS *msgs;
574     BPIPE *bpipe;
575     char *mode;
576
577     Dmsg2(850, "Enter dispatch_msg type=%d msg=%s", type, msg);
578
579     /*
580      * Most messages are prefixed by a date and time. If mtime is
581      *  zero, then we use the current time.  If mtime is 1 (special
582      *  kludge), we do not prefix the date and time. Otherwise,
583      *  we assume mtime is a time_t and use it.
584      */
585     if (mtime == 0) {
586        mtime = time(NULL);
587     }
588     if (mtime == 1) {
589        *dt = 0;
590        dtlen = 0;
591     } else {
592        bstrftime_ny(dt, sizeof(dt), mtime);
593        dtlen = strlen(dt);
594        dt[dtlen++] = ' ';
595        dt[dtlen] = 0;
596     }
597
598     if (type == M_ABORT || type == M_ERROR_TERM) {
599 #if !defined(HAVE_WIN32)
600        fputs(dt, stdout);
601        fputs(msg, stdout);         /* print this here to INSURE that it is printed */
602        fflush(stdout);
603 #endif
604     }
605
606     /* Now figure out where to send the message */
607     msgs = NULL;
608     if (jcr) {
609        msgs = jcr->jcr_msgs;
610     }
611     if (msgs == NULL) {
612        msgs = daemon_msgs;
613     }
614     for (d=msgs->dest_chain; d; d=d->next) {
615        if (bit_is_set(type, d->msg_types)) {
616           switch (d->dest_code) {
617              case MD_CATALOG:
618                 char ed1[50];
619                 if (!jcr || !jcr->db) {
620                    break;
621                 }
622                 if (p_sql_query) {
623                    POOL_MEM cmd(PM_MESSAGE);
624                    bstrftimes(dt, sizeof(dt), mtime);
625                    Mmsg(cmd, "INSERT INTO Log (JobId, Time, LogText) VALUES (%s,'%s','%s')",
626                          edit_int64(jcr->JobId, ed1), dt, msg);
627                    p_sql_query(jcr, cmd.c_str());
628                 }
629                 break;
630              case MD_CONSOLE:
631                 Dmsg1(850, "CONSOLE for following msg: %s", msg);
632                 if (!con_fd) {
633                    con_fd = fopen(con_fname, "a+b");
634                    Dmsg0(850, "Console file not open.\n");
635                 }
636                 if (con_fd) {
637                    Pw(con_lock);      /* get write lock on console message file */
638                    errno = 0;
639                    if (dtlen) {
640                       (void)fwrite(dt, dtlen, 1, con_fd);
641                    }
642                    len = strlen(msg);
643                    if (len > 0) {
644                       (void)fwrite(msg, len, 1, con_fd);
645                       if (msg[len-1] != '\n') {
646                          (void)fwrite("\n", 2, 1, con_fd);
647                       }
648                    } else {
649                       (void)fwrite("\n", 2, 1, con_fd);
650                    }
651                    fflush(con_fd);
652                    console_msg_pending = TRUE;
653                    Vw(con_lock);
654                 }
655                 break;
656              case MD_SYSLOG:
657                 Dmsg1(850, "SYSLOG for following msg: %s\n", msg);
658                 /*
659                  * We really should do an openlog() here.
660                  */
661                 syslog(LOG_DAEMON|LOG_ERR, "%s", msg);
662                 break;
663              case MD_OPERATOR:
664                 Dmsg1(850, "OPERATOR for following msg: %s\n", msg);
665                 mcmd = get_pool_memory(PM_MESSAGE);
666                 if ((bpipe=open_mail_pipe(jcr, mcmd, d))) {
667                    int stat;
668                    fputs(dt, bpipe->wfd);
669                    fputs(msg, bpipe->wfd);
670                    /* Messages to the operator go one at a time */
671                    stat = close_bpipe(bpipe);
672                    if (stat != 0) {
673                       berrno be;
674                       be.set_errno(stat);
675                       Qmsg2(jcr, M_ERROR, 0, _("Operator mail program terminated in error.\n"
676                             "CMD=%s\n"
677                             "ERR=%s\n"), mcmd, be.strerror());
678                    }
679                 }
680                 free_pool_memory(mcmd);
681                 break;
682              case MD_MAIL:
683              case MD_MAIL_ON_ERROR:
684                 Dmsg1(850, "MAIL for following msg: %s", msg);
685                 if (!d->fd) {
686                    POOLMEM *name = get_pool_memory(PM_MESSAGE);
687                    make_unique_mail_filename(jcr, name, d);
688                    d->fd = fopen(name, "w+b");
689                    if (!d->fd) {
690                       berrno be;
691                       d->fd = stdout;
692                       Qmsg2(jcr, M_ERROR, 0, _("fopen %s failed: ERR=%s\n"), name,
693                             be.strerror());
694                       d->fd = NULL;
695                       free_pool_memory(name);
696                       break;
697                    }
698                    d->mail_filename = name;
699                 }
700                 fputs(dt, d->fd);
701                 len = strlen(msg) + dtlen;;
702                 if (len > d->max_len) {
703                    d->max_len = len;      /* keep max line length */
704                 }
705                 fputs(msg, d->fd);
706                 break;
707              case MD_APPEND:
708                 Dmsg1(850, "APPEND for following msg: %s", msg);
709                 mode = "ab";
710                 goto send_to_file;
711              case MD_FILE:
712                 Dmsg1(850, "FILE for following msg: %s", msg);
713                 mode = "w+b";
714 send_to_file:
715                 if (!d->fd && !open_dest_file(jcr, d, mode)) {
716                    break;
717                 }
718                 fputs(dt, d->fd);
719                 fputs(msg, d->fd);
720                 /* On error, we close and reopen to handle log rotation */
721                 if (ferror(d->fd)) {
722                    fclose(d->fd);
723                    if (open_dest_file(jcr, d, mode)) {
724                       fputs(dt, d->fd);
725                       fputs(msg, d->fd);
726                    }
727                 }
728                 break;
729              case MD_DIRECTOR:
730                 Dmsg1(850, "DIRECTOR for following msg: %s", msg);
731                 if (jcr && jcr->dir_bsock && !jcr->dir_bsock->errors) {
732                    bnet_fsend(jcr->dir_bsock, "Jmsg Job=%s type=%d level=%d %s",
733                       jcr->Job, type, mtime, msg);
734                 }
735                 break;
736              case MD_STDOUT:
737                 Dmsg1(850, "STDOUT for following msg: %s", msg);
738                 if (type != M_ABORT && type != M_ERROR_TERM) { /* already printed */
739                    fputs(dt, stdout);
740                    fputs(msg, stdout);
741                 }
742                 break;
743              case MD_STDERR:
744                 Dmsg1(850, "STDERR for following msg: %s", msg);
745                 fputs(dt, stderr);
746                 fputs(msg, stderr);
747                 break;
748              default:
749                 break;
750           }
751        }
752     }
753 }
754
755 /*********************************************************************
756  *
757  *  This subroutine returns the filename portion of a Windows 
758  *  path.  It is used because Microsoft Visual Studio sets __FILE__ 
759  *  to the full path.
760  */
761
762 inline const char *
763 get_basename(const char *pathname)
764 {
765 #if defined(_MSC_VER)
766    const char *basename;
767    
768    if ((basename = strrchr(pathname, '\\')) == NULL) {
769       basename = pathname;
770    } else {
771       basename++;
772    }
773
774    return basename;
775 #else
776    return pathname;
777 #endif
778 }
779
780 /*********************************************************************
781  *
782  *  This subroutine prints a debug message if the level number
783  *  is less than or equal the debug_level. File and line numbers
784  *  are included for more detail if desired, but not currently
785  *  printed.
786  *
787  *  If the level is negative, the details of file and line number
788  *  are not printed.
789  */
790 void
791 d_msg(const char *file, int line, int level, const char *fmt,...)
792 {
793     char      buf[5000];
794     int       len;
795     va_list   arg_ptr;
796     bool      details = true;
797
798     if (level < 0) {
799        details = false;
800        level = -level;
801     }
802
803     if (level <= debug_level) {
804 #ifdef FULL_LOCATION
805        if (details) {
806           len = bsnprintf(buf, sizeof(buf), "%s: %s:%d ", my_name, get_basename(file), line);
807        } else {
808           len = 0;
809        }
810 #else
811        len = 0;
812 #endif
813        va_start(arg_ptr, fmt);
814        bvsnprintf(buf+len, sizeof(buf)-len, (char *)fmt, arg_ptr);
815        va_end(arg_ptr);
816
817        /*
818         * Used the "trace on" command in the console to turn on
819         *  output to the trace file.  "trace off" will close the file.
820         */
821        if (trace) {
822           if (!trace_fd) {
823              char fn[200];
824              bsnprintf(fn, sizeof(fn), "%s/%s.trace", working_directory ? working_directory : ".", my_name);
825              trace_fd = fopen(fn, "a+b");
826           }
827           if (trace_fd) {
828              fputs(buf, trace_fd);
829              fflush(trace_fd);
830           } else {
831              /* Some problem, turn off tracing */
832              trace = false;
833           }
834        } else {   /* not tracing */
835           fputs(buf, stdout);
836        }
837     }
838 }
839
840 /*
841  * Set trace flag on/off. If argument is negative, there is no change
842  */
843 void set_trace(int trace_flag)
844 {
845    if (trace_flag < 0) {
846       return;
847    } else if (trace_flag > 0) {
848       trace = true;
849    } else {
850       trace = false;
851    }
852    if (!trace && trace_fd) {
853       FILE *ltrace_fd = trace_fd;
854       trace_fd = NULL;
855       bmicrosleep(0, 100000);         /* yield to prevent seg faults */
856       fclose(ltrace_fd);
857    }
858 }
859
860 bool get_trace(void)
861 {
862    return trace;
863 }
864
865 /*********************************************************************
866  *
867  *  This subroutine prints a message regardless of the debug level
868  *
869  *  If the level is negative, the details of file and line number
870  *  are not printed.
871  */
872 void
873 p_msg(const char *file, int line, int level, const char *fmt,...)
874 {
875     char      buf[5000];
876     int       len;
877     va_list   arg_ptr;
878
879 #ifdef FULL_LOCATION
880     if (level >= 0) {
881        len = bsnprintf(buf, sizeof(buf), "%s: %s:%d ", my_name, get_basename(file), line);
882     } else {
883        len = 0;
884     }
885 #else
886     len = 0;
887 #endif
888     va_start(arg_ptr, fmt);
889     bvsnprintf(buf+len, sizeof(buf)-len, (char *)fmt, arg_ptr);
890     va_end(arg_ptr);
891     fputs(buf, stdout);
892 }
893
894
895 /*********************************************************************
896  *
897  *  subroutine writes a debug message to the trace file if the level number
898  *  is less than or equal the debug_level. File and line numbers
899  *  are included for more detail if desired, but not currently
900  *  printed.
901  *
902  *  If the level is negative, the details of file and line number
903  *  are not printed.
904  */
905 void
906 t_msg(const char *file, int line, int level, const char *fmt,...)
907 {
908     char      buf[5000];
909     int       len;
910     va_list   arg_ptr;
911     int       details = TRUE;
912
913     if (level < 0) {
914        details = FALSE;
915        level = -level;
916     }
917
918     if (level <= debug_level) {
919        if (!trace_fd) {
920           bsnprintf(buf, sizeof(buf), "%s/%s.trace", working_directory ? working_directory : ".", my_name);
921           trace_fd = fopen(buf, "a+b");
922        }
923
924 #ifdef FULL_LOCATION
925        if (details) {
926           len = bsnprintf(buf, sizeof(buf), "%s: %s:%d ", my_name, get_basename(file), line);
927        } else {
928           len = 0;
929        }
930 #else
931        len = 0;
932 #endif
933        va_start(arg_ptr, fmt);
934        bvsnprintf(buf+len, sizeof(buf)-len, (char *)fmt, arg_ptr);
935        va_end(arg_ptr);
936        if (trace_fd != NULL) {
937            fputs(buf, trace_fd);
938            fflush(trace_fd);
939        }
940    }
941 }
942
943
944
945 /* *********************************************************
946  *
947  * print an error message
948  *
949  */
950 void
951 e_msg(const char *file, int line, int type, int level, const char *fmt,...)
952 {
953     char     buf[5000];
954     va_list   arg_ptr;
955     int len;
956
957     /*
958      * Check if we have a message destination defined.
959      * We always report M_ABORT and M_ERROR_TERM
960      */
961     if (!daemon_msgs || ((type != M_ABORT && type != M_ERROR_TERM) &&
962                          !bit_is_set(type, daemon_msgs->send_msg))) {
963        return;                        /* no destination */
964     }
965     switch (type) {
966     case M_ABORT:
967        len = bsnprintf(buf, sizeof(buf), _("%s: ABORTING due to ERROR in %s:%d\n"),
968                my_name, get_basename(file), line);
969        break;
970     case M_ERROR_TERM:
971        len = bsnprintf(buf, sizeof(buf), _("%s: ERROR TERMINATION at %s:%d\n"),
972                my_name, get_basename(file), line);
973        break;
974     case M_FATAL:
975        if (level == -1)            /* skip details */
976           len = bsnprintf(buf, sizeof(buf), _("%s: Fatal Error because: "), my_name);
977        else
978           len = bsnprintf(buf, sizeof(buf), _("%s: Fatal Error at %s:%d because:\n"), my_name, get_basename(file), line);
979        break;
980     case M_ERROR:
981        if (level == -1)            /* skip details */
982           len = bsnprintf(buf, sizeof(buf), _("%s: ERROR: "), my_name);
983        else
984           len = bsnprintf(buf, sizeof(buf), _("%s: ERROR in %s:%d "), my_name, get_basename(file), line);
985        break;
986     case M_WARNING:
987        len = bsnprintf(buf, sizeof(buf), _("%s: Warning: "), my_name);
988        break;
989     case M_SECURITY:
990        len = bsnprintf(buf, sizeof(buf), _("%s: Security violation: "), my_name);
991        break;
992     default:
993        len = bsnprintf(buf, sizeof(buf), "%s: ", my_name);
994        break;
995     }
996
997     va_start(arg_ptr, fmt);
998     bvsnprintf(buf+len, sizeof(buf)-len, (char *)fmt, arg_ptr);
999     va_end(arg_ptr);
1000
1001     dispatch_message(NULL, type, 0, buf);
1002
1003     if (type == M_ABORT) {
1004        char *p = 0;
1005        p[0] = 0;                      /* generate segmentation violation */
1006     }
1007     if (type == M_ERROR_TERM) {
1008        exit(1);
1009     }
1010 }
1011
1012 /* *********************************************************
1013  *
1014  * Generate a Job message
1015  *
1016  */
1017 void
1018 Jmsg(JCR *jcr, int type, time_t mtime, const char *fmt,...)
1019 {
1020     char     rbuf[5000];
1021     va_list   arg_ptr;
1022     int len;
1023     MSGS *msgs;
1024     const char *job;
1025
1026
1027     Dmsg1(850, "Enter Jmsg type=%d\n", type);
1028
1029     /* Special case for the console, which has a dir_bsock and JobId==0,
1030      *  in that case, we send the message directly back to the
1031      *  dir_bsock.
1032      */
1033     if (jcr && jcr->JobId == 0 && jcr->dir_bsock) {
1034        BSOCK *dir = jcr->dir_bsock;
1035        va_start(arg_ptr, fmt);
1036        dir->msglen = bvsnprintf(dir->msg, sizeof_pool_memory(dir->msg),
1037                                 fmt, arg_ptr);
1038        va_end(arg_ptr);
1039        bnet_send(jcr->dir_bsock);
1040        return;
1041     }
1042
1043     msgs = NULL;
1044     job = NULL;
1045     if (jcr) {
1046        msgs = jcr->jcr_msgs;
1047        job = jcr->Job;
1048     }
1049     if (!msgs) {
1050        msgs = daemon_msgs;            /* if no jcr, we use daemon handler */
1051     }
1052     if (!job) {
1053        job = "";                      /* Set null job name if none */
1054     }
1055
1056     /*
1057      * Check if we have a message destination defined.
1058      * We always report M_ABORT and M_ERROR_TERM
1059      */
1060     if (msgs && (type != M_ABORT && type != M_ERROR_TERM) &&
1061          !bit_is_set(type, msgs->send_msg)) {
1062        return;                        /* no destination */
1063     }
1064     switch (type) {
1065     case M_ABORT:
1066        len = bsnprintf(rbuf, sizeof(rbuf), _("%s ABORTING due to ERROR\n"), my_name);
1067        break;
1068     case M_ERROR_TERM:
1069        len = bsnprintf(rbuf, sizeof(rbuf), _("%s ERROR TERMINATION\n"), my_name);
1070        break;
1071     case M_FATAL:
1072        len = bsnprintf(rbuf, sizeof(rbuf), _("%s: %s Fatal error: "), my_name, job);
1073        if (jcr) {
1074           set_jcr_job_status(jcr, JS_FatalError);
1075        }
1076        break;
1077     case M_ERROR:
1078        len = bsnprintf(rbuf, sizeof(rbuf), _("%s: %s Error: "), my_name, job);
1079        if (jcr) {
1080           jcr->Errors++;
1081        }
1082        break;
1083     case M_WARNING:
1084        len = bsnprintf(rbuf, sizeof(rbuf), _("%s: %s Warning: "), my_name, job);
1085        break;
1086     case M_SECURITY:
1087        len = bsnprintf(rbuf, sizeof(rbuf), _("%s: %s Security violation: "), my_name, job);
1088        break;
1089     default:
1090        len = bsnprintf(rbuf, sizeof(rbuf), "%s: ", my_name);
1091        break;
1092     }
1093
1094     va_start(arg_ptr, fmt);
1095     bvsnprintf(rbuf+len,  sizeof(rbuf)-len, fmt, arg_ptr);
1096     va_end(arg_ptr);
1097
1098     dispatch_message(jcr, type, mtime, rbuf);
1099
1100     if (type == M_ABORT){
1101        char *p = 0;
1102        p[0] = 0;                      /* generate segmentation violation */
1103     }
1104     if (type == M_ERROR_TERM) {
1105        exit(1);
1106     }
1107 }
1108
1109 /*
1110  * If we come here, prefix the message with the file:line-number,
1111  *  then pass it on to the normal Jmsg routine.
1112  */
1113 void j_msg(const char *file, int line, JCR *jcr, int type, time_t mtime, const char *fmt,...)
1114 {
1115    va_list   arg_ptr;
1116    int i, len, maxlen;
1117    POOLMEM *pool_buf;
1118
1119    pool_buf = get_pool_memory(PM_EMSG);
1120    i = Mmsg(pool_buf, "%s:%d ", get_basename(file), line);
1121
1122    for (;;) {
1123       maxlen = sizeof_pool_memory(pool_buf) - i - 1;
1124       va_start(arg_ptr, fmt);
1125       len = bvsnprintf(pool_buf+i, maxlen, fmt, arg_ptr);
1126       va_end(arg_ptr);
1127       if (len < 0 || len >= (maxlen-5)) {
1128          pool_buf = realloc_pool_memory(pool_buf, maxlen + i + maxlen/2);
1129          continue;
1130       }
1131       break;
1132    }
1133
1134    Jmsg(jcr, type, mtime, "%s", pool_buf);
1135    free_memory(pool_buf);
1136 }
1137
1138
1139 /*
1140  * Edit a message into a Pool memory buffer, with file:lineno
1141  */
1142 int m_msg(const char *file, int line, POOLMEM **pool_buf, const char *fmt, ...)
1143 {
1144    va_list   arg_ptr;
1145    int i, len, maxlen;
1146
1147    i = sprintf(*pool_buf, "%s:%d ", get_basename(file), line);
1148
1149    for (;;) {
1150       maxlen = sizeof_pool_memory(*pool_buf) - i - 1;
1151       va_start(arg_ptr, fmt);
1152       len = bvsnprintf(*pool_buf+i, maxlen, fmt, arg_ptr);
1153       va_end(arg_ptr);
1154       if (len < 0 || len >= (maxlen-5)) {
1155          *pool_buf = realloc_pool_memory(*pool_buf, maxlen + i + maxlen/2);
1156          continue;
1157       }
1158       break;
1159    }
1160    return len;
1161 }
1162
1163 int m_msg(const char *file, int line, POOLMEM *&pool_buf, const char *fmt, ...)
1164 {
1165    va_list   arg_ptr;
1166    int i, len, maxlen;
1167
1168    i = sprintf(pool_buf, "%s:%d ", get_basename(file), line);
1169
1170    for (;;) {
1171       maxlen = sizeof_pool_memory(pool_buf) - i - 1;
1172       va_start(arg_ptr, fmt);
1173       len = bvsnprintf(pool_buf+i, maxlen, fmt, arg_ptr);
1174       va_end(arg_ptr);
1175       if (len < 0 || len >= (maxlen-5)) {
1176          pool_buf = realloc_pool_memory(pool_buf, maxlen + i + maxlen/2);
1177          continue;
1178       }
1179       break;
1180    }
1181    return len;
1182 }
1183
1184
1185 /*
1186  * Edit a message into a Pool Memory buffer NO file:lineno
1187  *  Returns: string length of what was edited.
1188  */
1189 int Mmsg(POOLMEM **pool_buf, const char *fmt, ...)
1190 {
1191    va_list   arg_ptr;
1192    int len, maxlen;
1193
1194    for (;;) {
1195       maxlen = sizeof_pool_memory(*pool_buf) - 1;
1196       va_start(arg_ptr, fmt);
1197       len = bvsnprintf(*pool_buf, maxlen, fmt, arg_ptr);
1198       va_end(arg_ptr);
1199       if (len < 0 || len >= (maxlen-5)) {
1200          *pool_buf = realloc_pool_memory(*pool_buf, maxlen + maxlen/2);
1201          continue;
1202       }
1203       break;
1204    }
1205    return len;
1206 }
1207
1208 int Mmsg(POOLMEM *&pool_buf, const char *fmt, ...)
1209 {
1210    va_list   arg_ptr;
1211    int len, maxlen;
1212
1213    for (;;) {
1214       maxlen = sizeof_pool_memory(pool_buf) - 1;
1215       va_start(arg_ptr, fmt);
1216       len = bvsnprintf(pool_buf, maxlen, fmt, arg_ptr);
1217       va_end(arg_ptr);
1218       if (len < 0 || len >= (maxlen-5)) {
1219          pool_buf = realloc_pool_memory(pool_buf, maxlen + maxlen/2);
1220          continue;
1221       }
1222       break;
1223    }
1224    return len;
1225 }
1226
1227 int Mmsg(POOL_MEM &pool_buf, const char *fmt, ...)
1228 {
1229    va_list   arg_ptr;
1230    int len, maxlen;
1231
1232    for (;;) {
1233       maxlen = pool_buf.max_size() - 1;
1234       va_start(arg_ptr, fmt);
1235       len = bvsnprintf(pool_buf.c_str(), maxlen, fmt, arg_ptr);
1236       va_end(arg_ptr);
1237       if (len < 0 || len >= (maxlen-5)) {
1238          pool_buf.realloc_pm(maxlen + maxlen/2);
1239          continue;
1240       }
1241       break;
1242    }
1243    return len;
1244 }
1245
1246
1247 static pthread_mutex_t msg_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
1248
1249 /*
1250  * We queue messages rather than print them directly. This
1251  *  is generally used in low level routines (msg handler, bnet)
1252  *  to prevent recursion (i.e. if you are in the middle of
1253  *  sending a message, it is a bit messy to recursively call
1254  *  yourself when the bnet packet is not reentrant).
1255  */
1256 void Qmsg(JCR *jcr, int type, time_t mtime, const char *fmt,...)
1257 {
1258    va_list   arg_ptr;
1259    int len, maxlen;
1260    POOLMEM *pool_buf;
1261    MQUEUE_ITEM *item;
1262
1263    pool_buf = get_pool_memory(PM_EMSG);
1264
1265    for (;;) {
1266       maxlen = sizeof_pool_memory(pool_buf) - 1;
1267       va_start(arg_ptr, fmt);
1268       len = bvsnprintf(pool_buf, maxlen, fmt, arg_ptr);
1269       va_end(arg_ptr);
1270       if (len < 0 || len >= (maxlen-5)) {
1271          pool_buf = realloc_pool_memory(pool_buf, maxlen + maxlen/2);
1272          continue;
1273       }
1274       break;
1275    }
1276    item = (MQUEUE_ITEM *)malloc(sizeof(MQUEUE_ITEM) + strlen(pool_buf) + 1);
1277    item->type = type;
1278    item->mtime = time(NULL);
1279    strcpy(item->msg, pool_buf);
1280    /* If no jcr or dequeuing send to daemon to avoid recursion */
1281    if (!jcr || jcr->dequeuing) {
1282       /* jcr==NULL => daemon message, safe to send now */
1283       Jmsg(NULL, item->type, item->mtime, "%s", item->msg);
1284       free(item);
1285    } else {
1286       /* Queue message for later sending */
1287       P(msg_queue_mutex);
1288       jcr->msg_queue->append(item);
1289       V(msg_queue_mutex);
1290    }
1291    free_memory(pool_buf);
1292 }
1293
1294 /*
1295  * Dequeue messages
1296  */
1297 void dequeue_messages(JCR *jcr)
1298 {
1299    MQUEUE_ITEM *item;
1300    P(msg_queue_mutex);
1301    if (!jcr->msg_queue) {
1302       goto bail_out;
1303    }
1304    jcr->dequeuing = true;
1305    foreach_dlist(item, jcr->msg_queue) {
1306       Jmsg(jcr, item->type, item->mtime, "%s", item->msg);
1307    }
1308    jcr->msg_queue->destroy();
1309    jcr->dequeuing = false;
1310
1311 bail_out:
1312    V(msg_queue_mutex);
1313 }
1314
1315
1316 /*
1317  * If we come here, prefix the message with the file:line-number,
1318  *  then pass it on to the normal Qmsg routine.
1319  */
1320 void q_msg(const char *file, int line, JCR *jcr, int type, time_t mtime, const char *fmt,...)
1321 {
1322    va_list   arg_ptr;
1323    int i, len, maxlen;
1324    POOLMEM *pool_buf;
1325
1326    pool_buf = get_pool_memory(PM_EMSG);
1327    i = Mmsg(pool_buf, "%s:%d ", file, line);
1328
1329    for (;;) {
1330       maxlen = sizeof_pool_memory(pool_buf) - i - 1;
1331       va_start(arg_ptr, fmt);
1332       len = bvsnprintf(pool_buf+i, maxlen, fmt, arg_ptr);
1333       va_end(arg_ptr);
1334       if (len < 0 || len >= (maxlen-5)) {
1335          pool_buf = realloc_pool_memory(pool_buf, maxlen + i + maxlen/2);
1336          continue;
1337       }
1338       break;
1339    }
1340
1341    Qmsg(jcr, type, mtime, "%s", pool_buf);
1342    free_memory(pool_buf);
1343 }