]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/message.c
First cut AutoPrune
[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 /*
11    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30
31 #include "bacula.h"
32 #include "jcr.h"
33
34 #define FULL_LOCATION 1               /* set for file:line in Debug messages */
35
36 char *working_directory = NULL;       /* working directory path stored here */
37 int debug_level = 5;                  /* debug level */
38 time_t daemon_start_time = 0;         /* Daemon start time */
39
40 char my_name[20];                     /* daemon name is stored here */
41 char *exepath = (char *)NULL;
42 char *exename = (char *)NULL;
43 int console_msg_pending = 0;
44 char con_fname[1000];
45 FILE *con_fd = NULL;
46
47 /* Forward referenced functions */
48
49 /* Imported functions */
50
51
52 /* Static storage */
53
54 static MSGS *daemon_msgs;              /* global messages */
55
56 /* 
57  * Set daemon name. Also, find canonical execution
58  *  path.  Note, exepath has spare room for tacking on
59  *  the exename so that we can reconstruct the full name.
60  *
61  * Note, this routine can get called multiple times
62  *  The second time is to put the name as found in the
63  *  Resource record. On the second call, generally,
64  *  argv is NULL to avoid doing the path code twice.
65  */
66 #define BTRACE_EXTRA 20
67 void my_name_is(int argc, char *argv[], char *name)
68 {
69    char *l, *p, *q;
70    char cpath[400], npath[400];
71    int len;
72
73    strncpy(my_name, name, sizeof(my_name));
74    my_name[sizeof(my_name)-1] = 0;
75    if (argc>0 && argv && argv[0]) {
76       /* strip trailing filename and save exepath */
77       for (l=p=argv[0]; *p; p++) {
78          if (*p == '/') {
79             l = p;                       /* set pos of last slash */
80          }
81       }
82       if (*l == '/') {
83          l++;
84       } else {
85          l = argv[0];
86 #ifdef HAVE_CYGWIN
87          /* On Windows allow c: junk */
88          if (l[1] == ':') {
89             l += 2;
90          }
91 #endif
92       }
93       len = strlen(l) + 1;
94       if (exename) {
95          free(exename);
96       }
97       exename = (char *)malloc(len);
98       strcpy(exename, l);
99       if (exepath) {
100          free(exepath);
101       }
102       exepath = (char *)malloc(strlen(argv[0]) + 1 + len);
103       for (p=argv[0],q=exepath; p < l; ) {
104          *q++ = *p++;
105       }
106       *q = 0;
107       Dmsg1(200, "exepath=%s\n", exepath);
108       if (strchr(exepath, '.') || exepath[0] != '/') {
109          npath[0] = 0;
110          if (getcwd(cpath, sizeof(cpath))) {
111             if (chdir(exepath) == 0) {
112                if (!getcwd(npath, sizeof(npath))) {
113                   npath[0] = 0;
114                }
115                chdir(cpath);
116             }
117             if (npath[0]) {
118                free(exepath);
119                exepath = (char *)malloc(strlen(npath) + 1 + len);
120                strcpy(exepath, npath);
121             }
122          }
123          Dmsg1(200, "Normalized exepath=%s\n", exepath);
124       }
125    }
126 }
127
128 /* 
129  * Initialize message handler for a daemon or a Job
130  * 
131  *   NULL for jcr -> initialize global messages for daemon
132  *   non-NULL     -> initialize jcr using Message resource
133  */
134 void
135 init_msg(void *vjcr, MSGS *msg)
136 {
137    DEST *d, *dnew, *temp_chain = NULL;
138    JCR *jcr = (JCR *)vjcr;
139
140    /*
141     * If msg is NULL, initialize global chain for STDOUT and syslog
142     */
143    if (msg == NULL) {
144       int i;
145       daemon_msgs = (MSGS *)malloc(sizeof(MSGS));
146       memset(daemon_msgs, 0, sizeof(MSGS));
147       for (i=1; i<=M_MAX; i++) {
148          add_msg_dest(daemon_msgs, MD_STDOUT, i, NULL, NULL);
149          add_msg_dest(daemon_msgs, MD_SYSLOG, i, NULL, NULL);
150       }
151       Dmsg1(050, "Create daemon global message resource 0x%x\n", daemon_msgs);
152       return;
153    }
154
155    /*
156     * Walk down the message resource chain duplicating it
157     * for the current Job.
158     */
159    for (d=msg->dest_chain; d; d=d->next) {
160       dnew = (DEST *) malloc(sizeof(DEST));
161       memcpy(dnew, d, sizeof(DEST));
162       dnew->next = temp_chain;
163       dnew->fd = NULL;
164       dnew->mail_filename = NULL;
165       if (d->mail_cmd) {
166          dnew->mail_cmd = bstrdup(d->mail_cmd);
167       }
168       if (d->where) {
169          dnew->where = bstrdup(d->where);
170       }
171       temp_chain = dnew;
172    }
173
174    if (jcr) {
175       jcr->msgs = (MSGS *)malloc(sizeof(MSGS));
176       memset(jcr->msgs, 0, sizeof(MSGS));
177       jcr->msgs->dest_chain = temp_chain;
178       memcpy(jcr->msgs->send_msg, msg->send_msg, sizeof(msg->send_msg));
179    } else {
180       daemon_msgs = (MSGS *)malloc(sizeof(MSGS));
181       memset(daemon_msgs, 0, sizeof(MSGS));
182       daemon_msgs->dest_chain = temp_chain;
183       memcpy(daemon_msgs->send_msg, msg->send_msg, sizeof(msg->send_msg));
184    }
185    Dmsg2(050, "Copy message resource 0x%x to 0x%x\n", msg, temp_chain);
186 }
187
188 /* Initialize so that the console (User Agent) can
189  * receive messages -- stored in a file.
190  */
191 void init_console_msg(char *wd)
192 {
193    int fd;
194
195    sprintf(con_fname, "%s/%s.conmsg", wd, my_name);
196    fd = open(con_fname, O_CREAT|O_RDWR|O_BINARY, 0600);
197    if (fd == -1) {
198        Emsg2(M_ABORT, 0, "Could not open console message file %s: ERR=%s\n",
199           con_fname, strerror(errno));
200    }
201    if (lseek(fd, 0, SEEK_END) > 0) {
202       console_msg_pending = 1;
203    }
204    close(fd);
205    con_fd = fopen(con_fname, "a+");
206    if (!con_fd) {
207        Emsg2(M_ERROR, 0, "Could not open console message file %s: ERR=%s\n",
208           con_fname, strerror(errno));
209    }
210 }
211
212 /* 
213  * Called only during parsing of the config file.
214  *
215  * Add a message destination. I.e. associate a message type with
216  *  a destination (code).
217  * Note, where in the case of dest_code FILE is a filename,
218  *  but in the case of MAIL is a space separated list of
219  *  email addresses, ...
220  */
221 void add_msg_dest(MSGS *msg, int dest_code, int msg_type, char *where, char *mail_cmd)
222 {
223    DEST *d; 
224    /*
225     * First search the existing chain and see if we
226     * can simply add this msg_type to an existing entry.
227     */
228    for (d=msg->dest_chain; d; d=d->next) {
229       if (dest_code == d->dest_code && ((where == NULL && d->where == NULL) ||
230                      (strcmp(where, d->where) == 0))) {  
231          Dmsg4(200, "Add to existing d=%x msgtype=%d destcode=%d where=%s\n", 
232              d, msg_type, dest_code, where);
233          set_bit(msg_type, d->msg_types);
234          set_bit(msg_type, msg->send_msg);  /* set msg_type bit in our local */
235          return;
236       }
237    }
238    /* Not found, create a new entry */
239    d = (DEST *)malloc(sizeof(DEST));
240    memset(d, 0, sizeof(DEST));
241    d->next = msg->dest_chain;
242    d->dest_code = dest_code;
243    set_bit(msg_type, d->msg_types);      /* set type bit in structure */
244    set_bit(msg_type, msg->send_msg);     /* set type bit in our local */
245    if (where) {
246       d->where = bstrdup(where);
247    }
248    if (mail_cmd) {
249       d->mail_cmd = bstrdup(mail_cmd);
250    }
251    Dmsg5(200, "add new d=%x msgtype=%d destcode=%d where=%s mailcmd=%s\n", 
252           d, msg_type, dest_code, where?where:"(null)", 
253           d->mail_cmd?d->mail_cmd:"(null)");
254    msg->dest_chain = d;
255 }
256
257 /* 
258  * Called only during parsing of the config file.
259  *
260  * Remove a message destination   
261  */
262 void rem_msg_dest(MSGS *msg, int dest_code, int msg_type, char *where)
263 {
264    DEST *d;
265
266    for (d=msg->dest_chain; d; d=d->next) {
267       Dmsg2(200, "Remove_msg_dest d=%x where=%s\n", d, d->where);
268       if (bit_is_set(msg_type, d->msg_types) && (dest_code == d->dest_code) &&
269           ((where == NULL && d->where == NULL) ||
270                      (strcmp(where, d->where) == 0))) {  
271          Dmsg3(200, "Found for remove d=%x msgtype=%d destcode=%d\n", 
272                d, msg_type, dest_code);
273          clear_bit(msg_type, d->msg_types);
274          Dmsg0(200, "Return rem_msg_dest\n");
275          return;
276       }
277    }
278 }
279
280 /*
281  * Concatenate a string (str) onto a message (msg)
282  *  return new message pointer
283  */
284 static void add_str(char **base, char **msg, char *str)
285 {
286    int len = strlen(str) + 1;
287    char *b, *m;
288
289    b = *base;
290    *base = (char *) check_pool_memory_size(*base, len);
291    m = *base - b + *msg;
292    while (*str) {
293       *m++ = *str++;
294    }
295    *msg = m;
296 }
297
298 /*
299  * Convert Job Termination Status into a string
300  */
301 static char *job_status_to_str(int stat) 
302 {
303    char *str;
304
305    switch (stat) {
306    case JS_Terminated:
307       str = "OK";
308       break;
309    case JS_Errored:
310       str = "Error";
311       break;
312    case JS_Cancelled:
313       str = "Cancelled";
314       break;
315    case JS_Differences:
316       str = "Differences";
317       break;
318    default:
319       str = "Unknown term code";
320       break;
321    }
322    return str;
323 }
324
325
326 /*
327  * Convert Job Type into a string
328  */
329 static char *job_type_to_str(int type) 
330 {
331    char *str;
332
333    switch (type) {
334    case JT_BACKUP:
335       str = "Backup";
336       break;
337    case JT_VERIFY:
338       str = "Verify";
339       break;
340    case JT_RESTORE:
341       str = "Restore";
342       break;
343    default:
344       str = "Unknown Job Type";
345       break;
346    }
347    return str;
348 }
349
350 /*
351  * Convert Job Level into a string
352  */
353 static char *job_level_to_str(int level) 
354 {
355    char *str;
356
357    switch (level) {
358    case L_FULL:
359       str = "full";
360       break;
361    case L_INCREMENTAL:
362       str = "incremental";
363       break;
364    case L_DIFFERENTIAL:
365       str = "differential";
366       break;
367    case L_LEVEL:
368       str = "level";
369       break;
370    case L_SINCE:
371       str = "since";
372       break;
373    case L_VERIFY_CATALOG:
374       str = "verify catalog";
375       break;
376    case L_VERIFY_INIT:
377       str = "verify init";
378       break;
379    case L_VERIFY_VOLUME:
380       str = "verify volume";
381       break;
382    case L_VERIFY_DATA:
383       str = "verify data";
384       break;
385    default:
386       str = "Unknown Job level";
387       break;
388    }
389    return str;
390 }
391
392
393 /*
394  * Edit job codes into main command line
395  *  %% = %
396  *  %j = Job name
397  *  %t = Job type (Backup, ...)
398  *  %e = Job Exit code
399  *  %l = job level
400  *  %c = Client's name
401  *  %r = Recipients
402  *  %d = Director's name
403  */
404 static char *edit_job_codes(JCR *jcr, char *omsg, char *imsg, char *to)   
405 {
406    char *p, *o, *str;
407    char add[3];
408
409    Dmsg1(200, "edit_job_codes: %s\n", imsg);
410    add[2] = 0;
411    o = omsg;
412    for (p=imsg; *p; p++) {
413       if (*p == '%') {
414          switch (*++p) {
415          case '%':
416             add[0] = '%';
417             add[1] = 0;
418             str = add;
419             break;
420          case 'j':                    /* Job name */
421             str = jcr->Job;
422             break;
423          case 'e':
424             str = job_status_to_str(jcr->JobStatus); 
425             break;
426          case 't':
427             str = job_type_to_str(jcr->JobType);
428             break;
429          case 'r':
430             str = to;
431             break;
432          case 'l':
433             str = job_level_to_str(jcr->level);
434             break;
435          case 'c':
436             str = jcr->client_name;
437             if (!str) {
438                str = "";
439             }
440             break;
441          case 'd':
442             str = my_name;            /* Director's name */
443             break;
444          default:
445             add[0] = '%';
446             add[1] = *p;
447             str = add;
448             break;
449          }
450       } else {
451          add[0] = *p;
452          add[1] = 0;
453          str = add;
454       }
455       Dmsg1(200, "add_str %s\n", str);
456       add_str(&omsg, &o, str);
457       *o = 0;
458       Dmsg1(200, "omsg=%s\n", omsg);
459    }
460    *o = 0;
461    return omsg;
462 }
463
464 /*
465  * Create a unique filename for the mail command
466  */
467 static void make_unique_mail_filename(JCR *jcr, char **name, DEST *d)
468 {
469    if (jcr) {
470       Mmsg(name, "%s/%s.mail.%s.%d", working_directory, my_name,
471                  jcr->Job, (int)d);
472    } else {
473       Mmsg(name, "%s/%s.mail.%s.%d", working_directory, my_name,
474                  my_name, (int)d);
475    }
476    Dmsg1(200, "mailname=%s\n", *name);
477 }
478
479 /*
480  * Open a mail pipe
481  */
482 static FILE *open_mail_pipe(JCR *jcr, char **cmd, DEST *d)
483 {
484    FILE *pfd;
485
486    if (d->mail_cmd && jcr) {
487       *cmd = edit_job_codes(jcr, *cmd, d->mail_cmd, d->where);
488    } else {
489       Mmsg(cmd, "mail -s \"Bacula Message\" %s", d->where);
490    }
491    Dmsg1(200, "mailcmd=%s\n", cmd);
492    pfd = popen(*cmd, "w");
493    if (!pfd) {
494       Jmsg(jcr, M_ERROR, 0, "mail popen %s failed: ERR=%s\n", cmd, strerror(errno));
495    } 
496    return pfd;
497 }
498
499 /* 
500  * Close the messages for this Messages resource, which means to close
501  *  any open files, and dispatch any pending email messages.
502  */
503 void close_msg(void *vjcr)
504 {
505    MSGS *msgs;
506    JCR *jcr = (JCR *)vjcr;
507    DEST *d;
508    FILE *pfd;
509    POOLMEM *cmd, *line;
510    int len;
511    
512    Dmsg1(050, "Close_msg jcr=0x%x\n", jcr);
513
514    if (jcr == NULL) {                /* NULL -> global chain */
515       msgs = daemon_msgs;
516       daemon_msgs = NULL;
517    } else {
518       msgs = jcr->msgs;
519       jcr->msgs = NULL;
520    }
521    if (msgs == NULL) {
522       return;
523    }
524    Dmsg1(050, "close msg resource at 0x%x\n", msgs);
525    cmd = get_pool_memory(PM_MESSAGE);
526    for (d=msgs->dest_chain; d; ) {
527       if (d->fd) {
528          switch (d->dest_code) {
529          case MD_FILE:
530          case MD_APPEND:
531             if (d->fd) {
532                fclose(d->fd);            /* close open file descriptor */
533             }
534             break;
535          case MD_MAIL:
536          case MD_MAIL_ON_ERROR:
537             if (!d->fd) {
538                break;
539             }
540             if (d->dest_code == MD_MAIL_ON_ERROR && jcr &&
541                 jcr->JobStatus == JS_Terminated) {
542                goto rem_temp_file;
543             }
544             
545             pfd = open_mail_pipe(jcr, &cmd, d);
546             if (!pfd) {
547                goto rem_temp_file;
548             }
549             len = d->max_len+10;
550             line = get_memory(len);
551             rewind(d->fd);
552             while (fgets(line, len, d->fd)) {
553                fputs(line, pfd);
554             }
555             pclose(pfd);            /* close pipe, sending mail */
556             free_memory(line);
557 rem_temp_file:
558             /* Remove temp file */
559             fclose(d->fd);
560             unlink(d->mail_filename);
561             free_pool_memory(d->mail_filename);
562             d->mail_filename = NULL;
563             break;
564          default:
565             break;
566          }
567          d->fd = NULL;
568       }
569       d = d->next;                    /* point to next buffer */
570    }
571    free_pool_memory(cmd);
572
573    free_msgs_res(msgs);
574 }
575
576 /*
577  * Free memory associated with Messages resource  
578  */
579 void free_msgs_res(MSGS *msgs)
580 {
581    DEST *d, *old;
582
583    for (d=msgs->dest_chain; d; ) {
584       if (d->where) {
585          free(d->where);
586       }
587       if (d->mail_cmd) {
588          free(d->mail_cmd);
589       }
590       old = d;                        /* save pointer to release */
591       d = d->next;                    /* point to next buffer */
592       free(old);                      /* free the destination item */
593    }
594    msgs->dest_chain = NULL;
595    free(msgs);
596 }
597
598
599 /* 
600  * Terminate the message handler for good. 
601  * Release the global destination chain.
602  * 
603  * Also, clean up a few other items (cons, exepath). Note,
604  *   these really should be done elsewhere.
605  */
606 void term_msg()
607 {
608    Dmsg0(100, "Enter term_msg\n");
609    close_msg(NULL);                   /* close global chain */
610    daemon_msgs = NULL;
611    if (con_fd) {
612       fflush(con_fd);
613       fclose(con_fd);
614       con_fd = NULL;
615    }
616    if (exepath) {
617       free(exepath);
618       exepath = NULL;
619    }
620    if (exename) {
621       free(exename);
622       exename = NULL;
623    }
624 }
625
626
627
628 /*
629  * Handle sending the message to the appropriate place
630  */
631 void dispatch_message(void *vjcr, int type, int level, char *buf)
632 {
633     DEST *d;   
634     char cmd[MAXSTRING];
635     POOLMEM *mcmd;
636     JCR *jcr = (JCR *) vjcr;
637     int len;
638     MSGS *msgs;
639
640     Dmsg2(200, "Enter dispatch_msg type=%d msg=%s\n", type, buf);
641
642     if (type == M_ABORT) {
643        fprintf(stdout, buf);          /* print this here to INSURE that it is printed */
644     }
645
646     /* Now figure out where to send the message */
647     msgs = NULL;
648     if (jcr) {
649        msgs = jcr->msgs;
650     } 
651     if (msgs == NULL) {
652        msgs = daemon_msgs;
653     }
654     for (d=msgs->dest_chain; d; d=d->next) {
655        if (bit_is_set(type, d->msg_types)) {
656           switch (d->dest_code) {
657              case MD_CONSOLE:
658                 Dmsg1(200, "CONSOLE for following err: %s\n", buf);
659                 if (!con_fd) {
660                    con_fd = fopen(con_fname, "a+");
661                    Dmsg0(200, "Console file not open.\n");
662                 }
663                 if (con_fd) {
664                    fcntl(fileno(con_fd), F_SETLKW);
665                    errno = 0;
666                    bstrftime(cmd, sizeof(cmd), time(NULL));
667                    len = strlen(cmd);
668                    cmd[len++] = ' ';
669                    fwrite(cmd, len, 1, con_fd);
670                    len = strlen(buf);
671                    if (len > 0 && buf[len-1] != '\n') {
672                       buf[len++] = '\n';
673                       buf[len] = 0;
674                    }
675                    fwrite(buf, len, 1, con_fd);
676                    fflush(con_fd);
677                    fcntl(fileno(con_fd), F_UNLCK);
678                    console_msg_pending = TRUE;
679                 }
680                 break;
681              case MD_SYSLOG:
682                 Dmsg1(200, "SYSLOG for following err: %s\n", buf);
683                 /* We really should do an openlog() here */
684                 syslog(LOG_DAEMON|LOG_ERR, buf);
685                 break;
686              case MD_OPERATOR:
687                 Dmsg1(200, "OPERATOR for following err: %s\n", buf);
688                 mcmd = get_pool_memory(PM_MESSAGE);
689                 d->fd = open_mail_pipe(jcr, &mcmd, d);
690                 free_pool_memory(mcmd);
691                 if (d->fd) {
692                    fputs(buf, d->fd);
693                    /* Messages to the operator go one at a time */
694                    pclose(d->fd);
695                    d->fd = NULL;
696                 }
697                 break;
698              case MD_MAIL:
699              case MD_MAIL_ON_ERROR:
700                 Dmsg1(200, "MAIL for following err: %s\n", buf);
701                 if (!d->fd) {
702                    char *name  = (char *)get_pool_memory(PM_MESSAGE);
703                    make_unique_mail_filename(jcr, &name, d);
704                    d->fd = fopen(name, "w+");
705                    if (!d->fd) {
706                       Emsg2(M_ERROR, 0, "fopen %s failed: ERR=%s\n", name, strerror(errno));
707                       free_pool_memory(name);
708                       break;
709                    }
710                    d->mail_filename = name;
711                 }
712                 len = strlen(buf);
713                 if (len > d->max_len) {
714                    d->max_len = len;      /* keep max line length */
715                 }
716                 fputs(buf, d->fd);
717                 break;
718              case MD_FILE:
719                 Dmsg1(200, "FILE for following err: %s\n", buf);
720                 if (!d->fd) {
721                    d->fd = fopen(d->where, "w+");
722                    if (!d->fd) {
723                       Emsg2(M_ERROR, 0, "fopen %s failed: ERR=%s\n", d->where, strerror(errno));
724                       break;
725                    }
726                 }
727                 fputs(buf, d->fd);
728                 break;
729              case MD_APPEND:
730                 Dmsg1(200, "APPEND for following err: %s\n", buf);
731                 if (!d->fd) {
732                    d->fd = fopen(d->where, "a");
733                    if (!d->fd) {
734                       Emsg2(M_ERROR, 0, "fopen %s failed: ERR=%s\n", d->where, strerror(errno));
735                       break;
736                    }
737                 }
738                 fputs(buf, d->fd);
739                 break;
740              case MD_DIRECTOR:
741                 Dmsg1(200, "DIRECTOR for following err: %s\n", buf);
742                 if (jcr && jcr->dir_bsock && !jcr->dir_bsock->errors) {
743
744                    jcr->dir_bsock->msglen = Mmsg(&(jcr->dir_bsock->msg),
745                         "Jmsg Job=%s type=%d level=%d %s", jcr->Job,
746                          type, level, buf) + 1;
747                    bnet_send(jcr->dir_bsock);
748                 }
749                 break;
750              case MD_STDOUT:
751                 Dmsg1(200, "STDOUT for following err: %s\n", buf);
752                 if (type != M_ABORT && type != M_FATAL)  /* already printed */
753                    fprintf(stdout, buf);
754                 break;
755              case MD_STDERR:
756                 Dmsg1(200, "STDERR for following err: %s\n", buf);
757                 fprintf(stderr, buf);
758                 break;
759              default:
760                 break;
761           }
762        }
763     }
764 }
765
766
767 /*********************************************************************
768  *
769  *  subroutine prints a debug message if the level number
770  *  is less than or equal the debug_level. File and line numbers
771  *  are included for more detail if desired, but not currently
772  *  printed.
773  *  
774  *  If the level is negative, the details of file and line number
775  *  are not printed.
776  */
777 void 
778 d_msg(char *file, int line, int level, char *fmt,...)
779 {
780     char      buf[MAXSTRING];
781     int       i;
782     va_list   arg_ptr;
783     int       details = TRUE;
784
785     if (level < 0) {
786        details = FALSE;
787        level = -level;
788     }
789
790 /*  printf("level=%d debug=%d fmt=%s\n", level, debug_level, fmt); */
791
792     if (level <= debug_level) {
793 #ifdef FULL_LOCATION
794        if (details) {
795           sprintf(buf, "%s: %s:%d ", my_name, file, line);
796           i = strlen(buf);
797        } else {
798           i = 0;
799        }
800 #else
801        i = 0;
802 #endif
803        va_start(arg_ptr, fmt);
804        bvsnprintf(buf+i, sizeof(buf)-i, (char *)fmt, arg_ptr);
805        va_end(arg_ptr);
806
807        fprintf(stdout, buf);
808     }
809 }
810
811
812 /* *********************************************************
813  *
814  * print an error message
815  *
816  */
817 void 
818 e_msg(char *file, int line, int type, int level, char *fmt,...)
819 {
820     char     buf[2000];
821     va_list   arg_ptr;
822     int i;
823
824     /* 
825      * Check if we have a message destination defined.  
826      * We always report M_ABORT 
827      */
828     if (!daemon_msgs || (type != M_ABORT && 
829                          !bit_is_set(type, daemon_msgs->send_msg)))
830        return;                        /* no destination */
831     switch (type) {
832        case M_ABORT:
833           sprintf(buf, "%s ABORTING due to ERROR in %s:%d\n", 
834                   my_name, file, line);
835           break;
836        case M_FATAL:
837           if (level == -1)            /* skip details */
838              sprintf(buf, "%s: Fatal Error because: ", my_name);
839           else
840              sprintf(buf, "%s: Fatal Error at %s:%d because:\n", my_name, file, line);
841           break;
842        case M_ERROR:
843           if (level == -1)            /* skip details */
844              sprintf(buf, "%s: Error: ", my_name);
845           else
846              sprintf(buf, "%s: Error in %s:%d ", my_name, file, line);
847           break;
848        case M_WARNING:
849           sprintf(buf, "%s: Warning: ", my_name);
850           break;
851        default:
852           sprintf(buf, "%s: ", my_name);
853           break;
854     }
855
856     i = strlen(buf);
857     va_start(arg_ptr, fmt);
858     bvsnprintf(buf+i, sizeof(buf)-i, (char *)fmt, arg_ptr);
859     va_end(arg_ptr);
860
861     dispatch_message(NULL, type, level, buf);
862
863     if (type == M_ABORT) {
864        abort();
865     }
866 }
867
868 /* *********************************************************
869  *
870  * Generate a Job message
871  *
872  */
873 void 
874 Jmsg(void *vjcr, int type, int level, char *fmt,...)
875 {
876     char     rbuf[2000];
877     char     *buf;
878     va_list   arg_ptr;
879     int i, len;
880     JCR *jcr = (JCR *) vjcr;
881     int typesave = type;
882
883     
884     Dmsg1(200, "Enter Jmsg type=%d\n", type);
885
886     buf = rbuf;                    /* we are the Director */
887     /* 
888      * Check if we have a message destination defined.  
889      * We always report M_ABORT 
890      */
891     if (type != M_ABORT && jcr->msgs && !bit_is_set(type, jcr->msgs->send_msg)) {
892        Dmsg1(200, "No bit set for type %d\n", type);
893        return;                        /* no destination */
894     }
895     switch (type) {
896        case M_ABORT:
897           sprintf(buf, "%s ABORTING due to ERROR\n", my_name);
898           break;
899        case M_FATAL:
900           sprintf(buf, "%s: Job %s Cancelled because: ", my_name, jcr->Job);
901           break;
902        case M_ERROR:
903           sprintf(buf, "%s: Job %s Error: ", my_name, jcr->Job);
904           break;
905        case M_WARNING:
906           sprintf(buf, "%s: Job %s Warning: ", my_name, jcr->Job);
907           break;
908        default:
909           sprintf(buf, "%s: ", my_name);
910           break;
911     }
912
913     i = strlen(buf);
914     va_start(arg_ptr, fmt);
915     len = bvsnprintf(buf+i, sizeof(rbuf)-i, fmt, arg_ptr);
916     va_end(arg_ptr);
917
918     ASSERT(typesave==type);           /* type trashed, compiler bug???? */
919     dispatch_message(jcr, type, level, rbuf);
920
921     Dmsg3(500, "i=%d sizeof(rbuf)-i=%d len=%d\n", i, sizeof(rbuf)-i, len);
922
923     if (type == M_ABORT)
924        abort();
925 }
926
927 /*
928  * Edit a message into a Pool memory buffer, with file:lineno
929  */
930 int m_msg(char *file, int line, char **pool_buf, char *fmt, ...)
931 {
932    va_list   arg_ptr;
933    int i, len, maxlen;
934
935    sprintf(*pool_buf, "%s:%d ", file, line);
936    i = strlen(*pool_buf);
937
938 again:
939    maxlen = sizeof_pool_memory(*pool_buf) - i - 1; 
940    va_start(arg_ptr, fmt);
941    len = bvsnprintf(*pool_buf+i, maxlen, fmt, arg_ptr);
942    va_end(arg_ptr);
943    if (len < 0 || len >= maxlen) {
944       *pool_buf = (char *) realloc_pool_memory(*pool_buf, maxlen + i + 200);
945       goto again;
946    }
947    return len;
948 }
949
950 /*
951  * Edit a message into a Pool Memory buffer NO file:lineno
952  *  Returns: string length of what was edited.
953  */
954 int Mmsg(char **pool_buf, char *fmt, ...)
955 {
956    va_list   arg_ptr;
957    int len, maxlen;
958
959 again:
960    maxlen = sizeof_pool_memory(*pool_buf) - 1; 
961    va_start(arg_ptr, fmt);
962    len = bvsnprintf(*pool_buf, maxlen, fmt, arg_ptr);
963    va_end(arg_ptr);
964    if (len < 0 || len >= maxlen) {
965       *pool_buf = (char *) realloc_pool_memory(*pool_buf, maxlen + 200);
966       goto again;
967    }
968    return len;
969 }
970
971
972 void j_msg(char *file, int line, void *jcr, int type, int level, char *fmt,...)
973 {
974    va_list   arg_ptr;
975    int i, len, maxlen;
976    char *pool_buf;
977
978    pool_buf = (char *) get_pool_memory(PM_EMSG);
979    sprintf(pool_buf, "%s:%d ", file, line);
980    i = strlen(pool_buf);
981
982 again:
983    maxlen = sizeof_pool_memory(pool_buf) - i - 1; 
984    va_start(arg_ptr, fmt);
985    len = bvsnprintf(pool_buf+i, maxlen, fmt, arg_ptr);
986    va_end(arg_ptr);
987    if (len < 0 || len >= maxlen) {
988       pool_buf = (char *) realloc_pool_memory(pool_buf, maxlen + i + 200);
989       goto again;
990    }
991
992    Jmsg(jcr, type, level, pool_buf);
993    free_memory(pool_buf);
994 }