]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/message.c
Make Recycle work -- kes25May02
[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(POOLMEM **base, char **msg, char *str)
285 {
286    int len = strlen(str) + 1;
287    char *b, *m;
288
289    b = *base;
290    *base = 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_ErrorTerminated:
310    case JS_Error:
311       str = "Error";
312       break;
313    case JS_FatalError:
314       str = "Fatal Error";
315       break;
316    case JS_Cancelled:
317       str = "Cancelled";
318       break;
319    case JS_Differences:
320       str = "Differences";
321       break;
322    default:
323       str = "Unknown term code";
324       break;
325    }
326    return str;
327 }
328
329
330 /*
331  * Convert Job Type into a string
332  */
333 static char *job_type_to_str(int type) 
334 {
335    char *str;
336
337    switch (type) {
338    case JT_BACKUP:
339       str = "Backup";
340       break;
341    case JT_VERIFY:
342       str = "Verify";
343       break;
344    case JT_RESTORE:
345       str = "Restore";
346       break;
347    default:
348       str = "Unknown Job Type";
349       break;
350    }
351    return str;
352 }
353
354 /*
355  * Convert Job Level into a string
356  */
357 static char *job_level_to_str(int level) 
358 {
359    char *str;
360
361    switch (level) {
362    case L_FULL:
363       str = "full";
364       break;
365    case L_INCREMENTAL:
366       str = "incremental";
367       break;
368    case L_DIFFERENTIAL:
369       str = "differential";
370       break;
371    case L_LEVEL:
372       str = "level";
373       break;
374    case L_SINCE:
375       str = "since";
376       break;
377    case L_VERIFY_CATALOG:
378       str = "verify catalog";
379       break;
380    case L_VERIFY_INIT:
381       str = "verify init";
382       break;
383    case L_VERIFY_VOLUME:
384       str = "verify volume";
385       break;
386    case L_VERIFY_DATA:
387       str = "verify data";
388       break;
389    default:
390       str = "Unknown Job level";
391       break;
392    }
393    return str;
394 }
395
396
397 /*
398  * Edit job codes into main command line
399  *  %% = %
400  *  %j = Job name
401  *  %t = Job type (Backup, ...)
402  *  %e = Job Exit code
403  *  %l = job level
404  *  %c = Client's name
405  *  %r = Recipients
406  *  %d = Director's name
407  */
408 static char *edit_job_codes(JCR *jcr, char *omsg, char *imsg, char *to)   
409 {
410    char *p, *o, *str;
411    char add[3];
412
413    Dmsg1(200, "edit_job_codes: %s\n", imsg);
414    add[2] = 0;
415    o = omsg;
416    for (p=imsg; *p; p++) {
417       if (*p == '%') {
418          switch (*++p) {
419          case '%':
420             add[0] = '%';
421             add[1] = 0;
422             str = add;
423             break;
424          case 'j':                    /* Job name */
425             str = jcr->Job;
426             break;
427          case 'e':
428             str = job_status_to_str(jcr->JobStatus); 
429             break;
430          case 't':
431             str = job_type_to_str(jcr->JobType);
432             break;
433          case 'r':
434             str = to;
435             break;
436          case 'l':
437             str = job_level_to_str(jcr->level);
438             break;
439          case 'c':
440             str = jcr->client_name;
441             if (!str) {
442                str = "";
443             }
444             break;
445          case 'd':
446             str = my_name;            /* Director's name */
447             break;
448          default:
449             add[0] = '%';
450             add[1] = *p;
451             str = add;
452             break;
453          }
454       } else {
455          add[0] = *p;
456          add[1] = 0;
457          str = add;
458       }
459       Dmsg1(200, "add_str %s\n", str);
460       add_str(&omsg, &o, str);
461       *o = 0;
462       Dmsg1(200, "omsg=%s\n", omsg);
463    }
464    *o = 0;
465    return omsg;
466 }
467
468 /*
469  * Create a unique filename for the mail command
470  */
471 static void make_unique_mail_filename(JCR *jcr, char **name, DEST *d)
472 {
473    if (jcr) {
474       Mmsg(name, "%s/%s.mail.%s.%d", working_directory, my_name,
475                  jcr->Job, (int)d);
476    } else {
477       Mmsg(name, "%s/%s.mail.%s.%d", working_directory, my_name,
478                  my_name, (int)d);
479    }
480    Dmsg1(200, "mailname=%s\n", *name);
481 }
482
483 /*
484  * Open a mail pipe
485  */
486 static FILE *open_mail_pipe(JCR *jcr, char **cmd, DEST *d)
487 {
488    FILE *pfd;
489
490    if (d->mail_cmd && jcr) {
491       *cmd = edit_job_codes(jcr, *cmd, d->mail_cmd, d->where);
492    } else {
493       Mmsg(cmd, "mail -s \"Bacula Message\" %s", d->where);
494    }
495    Dmsg1(200, "mailcmd=%s\n", cmd);
496    pfd = popen(*cmd, "w");
497    if (!pfd) {
498       Jmsg(jcr, M_ERROR, 0, "mail popen %s failed: ERR=%s\n", cmd, strerror(errno));
499    } 
500    return pfd;
501 }
502
503 /* 
504  * Close the messages for this Messages resource, which means to close
505  *  any open files, and dispatch any pending email messages.
506  */
507 void close_msg(void *vjcr)
508 {
509    MSGS *msgs;
510    JCR *jcr = (JCR *)vjcr;
511    DEST *d;
512    FILE *pfd;
513    POOLMEM *cmd, *line;
514    int len;
515    
516    Dmsg1(050, "Close_msg jcr=0x%x\n", jcr);
517
518    if (jcr == NULL) {                /* NULL -> global chain */
519       msgs = daemon_msgs;
520       daemon_msgs = NULL;
521    } else {
522       msgs = jcr->msgs;
523       jcr->msgs = NULL;
524    }
525    if (msgs == NULL) {
526       return;
527    }
528    Dmsg1(050, "close msg resource at 0x%x\n", msgs);
529    cmd = get_pool_memory(PM_MESSAGE);
530    for (d=msgs->dest_chain; d; ) {
531       if (d->fd) {
532          switch (d->dest_code) {
533          case MD_FILE:
534          case MD_APPEND:
535             if (d->fd) {
536                fclose(d->fd);            /* close open file descriptor */
537             }
538             break;
539          case MD_MAIL:
540          case MD_MAIL_ON_ERROR:
541             if (!d->fd) {
542                break;
543             }
544             if (d->dest_code == MD_MAIL_ON_ERROR && jcr &&
545                 jcr->JobStatus == JS_Terminated) {
546                goto rem_temp_file;
547             }
548             
549             pfd = open_mail_pipe(jcr, &cmd, d);
550             if (!pfd) {
551                goto rem_temp_file;
552             }
553             len = d->max_len+10;
554             line = get_memory(len);
555             rewind(d->fd);
556             while (fgets(line, len, d->fd)) {
557                fputs(line, pfd);
558             }
559             pclose(pfd);            /* close pipe, sending mail */
560             free_memory(line);
561 rem_temp_file:
562             /* Remove temp file */
563             fclose(d->fd);
564             unlink(d->mail_filename);
565             free_pool_memory(d->mail_filename);
566             d->mail_filename = NULL;
567             break;
568          default:
569             break;
570          }
571          d->fd = NULL;
572       }
573       d = d->next;                    /* point to next buffer */
574    }
575    free_pool_memory(cmd);
576
577    free_msgs_res(msgs);
578 }
579
580 /*
581  * Free memory associated with Messages resource  
582  */
583 void free_msgs_res(MSGS *msgs)
584 {
585    DEST *d, *old;
586
587    for (d=msgs->dest_chain; d; ) {
588       if (d->where) {
589          free(d->where);
590       }
591       if (d->mail_cmd) {
592          free(d->mail_cmd);
593       }
594       old = d;                        /* save pointer to release */
595       d = d->next;                    /* point to next buffer */
596       free(old);                      /* free the destination item */
597    }
598    msgs->dest_chain = NULL;
599    free(msgs);
600 }
601
602
603 /* 
604  * Terminate the message handler for good. 
605  * Release the global destination chain.
606  * 
607  * Also, clean up a few other items (cons, exepath). Note,
608  *   these really should be done elsewhere.
609  */
610 void term_msg()
611 {
612    Dmsg0(100, "Enter term_msg\n");
613    close_msg(NULL);                   /* close global chain */
614    daemon_msgs = NULL;
615    if (con_fd) {
616       fflush(con_fd);
617       fclose(con_fd);
618       con_fd = NULL;
619    }
620    if (exepath) {
621       free(exepath);
622       exepath = NULL;
623    }
624    if (exename) {
625       free(exename);
626       exename = NULL;
627    }
628 }
629
630
631
632 /*
633  * Handle sending the message to the appropriate place
634  */
635 void dispatch_message(void *vjcr, int type, int level, char *msg)
636 {
637     DEST *d;   
638     char cmd[MAXSTRING];
639     POOLMEM *mcmd;
640     JCR *jcr = (JCR *) vjcr;
641     int len;
642     MSGS *msgs;
643
644     Dmsg2(200, "Enter dispatch_msg type=%d msg=%s\n", type, msg);
645
646     if (type == M_ABORT) {
647        fprintf(stdout, msg);          /* print this here to INSURE that it is printed */
648     }
649
650     /* Now figure out where to send the message */
651     msgs = NULL;
652     if (jcr) {
653        msgs = jcr->msgs;
654     } 
655     if (msgs == NULL) {
656        msgs = daemon_msgs;
657     }
658     for (d=msgs->dest_chain; d; d=d->next) {
659        if (bit_is_set(type, d->msg_types)) {
660           switch (d->dest_code) {
661              case MD_CONSOLE:
662                 Dmsg1(200, "CONSOLE for following err: %s\n", msg);
663                 if (!con_fd) {
664                    con_fd = fopen(con_fname, "a+");
665                    Dmsg0(200, "Console file not open.\n");
666                 }
667                 if (con_fd) {
668                    fcntl(fileno(con_fd), F_SETLKW);
669                    errno = 0;
670                    bstrftime(cmd, sizeof(cmd), time(NULL));
671                    len = strlen(cmd);
672                    cmd[len++] = ' ';
673                    fwrite(cmd, len, 1, con_fd);
674                    len = strlen(msg);
675                    if (len > 0 && msg[len-1] != '\n') {
676                       msg[len++] = '\n';
677                       msg[len] = 0;
678                    }
679                    fwrite(msg, len, 1, con_fd);
680                    fflush(con_fd);
681                    fcntl(fileno(con_fd), F_UNLCK);
682                    console_msg_pending = TRUE;
683                 }
684                 break;
685              case MD_SYSLOG:
686                 Dmsg1(200, "SYSLOG for following err: %s\n", msg);
687                 /* We really should do an openlog() here */
688                 syslog(LOG_DAEMON|LOG_ERR, msg);
689                 break;
690              case MD_OPERATOR:
691                 Dmsg1(200, "OPERATOR for following err: %s\n", msg);
692                 mcmd = get_pool_memory(PM_MESSAGE);
693                 d->fd = open_mail_pipe(jcr, &mcmd, d);
694                 free_pool_memory(mcmd);
695                 if (d->fd) {
696                    fputs(msg, d->fd);
697                    /* Messages to the operator go one at a time */
698                    pclose(d->fd);
699                    d->fd = NULL;
700                 }
701                 break;
702              case MD_MAIL:
703              case MD_MAIL_ON_ERROR:
704                 Dmsg1(200, "MAIL for following err: %s\n", msg);
705                 if (!d->fd) {
706                    POOLMEM *name  = get_pool_memory(PM_MESSAGE);
707                    make_unique_mail_filename(jcr, &name, d);
708                    d->fd = fopen(name, "w+");
709                    if (!d->fd) {
710                       Emsg2(M_ERROR, 0, "fopen %s failed: ERR=%s\n", name, strerror(errno));
711                       free_pool_memory(name);
712                       break;
713                    }
714                    d->mail_filename = name;
715                 }
716                 len = strlen(msg);
717                 if (len > d->max_len) {
718                    d->max_len = len;      /* keep max line length */
719                 }
720                 fputs(msg, d->fd);
721                 break;
722              case MD_FILE:
723                 Dmsg1(200, "FILE for following err: %s\n", msg);
724                 if (!d->fd) {
725                    d->fd = fopen(d->where, "w+");
726                    if (!d->fd) {
727                       Emsg2(M_ERROR, 0, "fopen %s failed: ERR=%s\n", d->where, strerror(errno));
728                       break;
729                    }
730                 }
731                 fputs(msg, d->fd);
732                 break;
733              case MD_APPEND:
734                 Dmsg1(200, "APPEND for following err: %s\n", msg);
735                 if (!d->fd) {
736                    d->fd = fopen(d->where, "a");
737                    if (!d->fd) {
738                       Emsg2(M_ERROR, 0, "fopen %s failed: ERR=%s\n", d->where, strerror(errno));
739                       break;
740                    }
741                 }
742                 fputs(msg, d->fd);
743                 break;
744              case MD_DIRECTOR:
745                 Dmsg1(200, "DIRECTOR for following err: %s\n", msg);
746                 if (jcr && jcr->dir_bsock && !jcr->dir_bsock->errors) {
747
748                    jcr->dir_bsock->msglen = Mmsg(&(jcr->dir_bsock->msg),
749                         "Jmsg Job=%s type=%d level=%d %s", jcr->Job,
750                          type, level, msg) + 1;
751                    bnet_send(jcr->dir_bsock);
752                 }
753                 break;
754              case MD_STDOUT:
755                 Dmsg1(200, "STDOUT for following err: %s\n", msg);
756                 if (type != M_ABORT && type != M_FATAL)  /* already printed */
757                    fprintf(stdout, msg);
758                 break;
759              case MD_STDERR:
760                 Dmsg1(200, "STDERR for following err: %s\n", msg);
761                 fprintf(stderr, msg);
762                 break;
763              default:
764                 break;
765           }
766        }
767     }
768 }
769
770
771 /*********************************************************************
772  *
773  *  subroutine prints a debug message if the level number
774  *  is less than or equal the debug_level. File and line numbers
775  *  are included for more detail if desired, but not currently
776  *  printed.
777  *  
778  *  If the level is negative, the details of file and line number
779  *  are not printed.
780  */
781 void 
782 d_msg(char *file, int line, int level, char *fmt,...)
783 {
784     char      buf[MAXSTRING];
785     int       i;
786     va_list   arg_ptr;
787     int       details = TRUE;
788
789     if (level < 0) {
790        details = FALSE;
791        level = -level;
792     }
793
794 /*  printf("level=%d debug=%d fmt=%s\n", level, debug_level, fmt); */
795
796     if (level <= debug_level) {
797 #ifdef FULL_LOCATION
798        if (details) {
799           sprintf(buf, "%s: %s:%d ", my_name, file, line);
800           i = strlen(buf);
801        } else {
802           i = 0;
803        }
804 #else
805        i = 0;
806 #endif
807        va_start(arg_ptr, fmt);
808        bvsnprintf(buf+i, sizeof(buf)-i, (char *)fmt, arg_ptr);
809        va_end(arg_ptr);
810
811        fprintf(stdout, buf);
812     }
813 }
814
815
816 /* *********************************************************
817  *
818  * print an error message
819  *
820  */
821 void 
822 e_msg(char *file, int line, int type, int level, char *fmt,...)
823 {
824     char     buf[2000];
825     va_list   arg_ptr;
826     int i;
827
828     /* 
829      * Check if we have a message destination defined.  
830      * We always report M_ABORT 
831      */
832     if (!daemon_msgs || (type != M_ABORT && 
833                          !bit_is_set(type, daemon_msgs->send_msg)))
834        return;                        /* no destination */
835     switch (type) {
836        case M_ABORT:
837           sprintf(buf, "%s ABORTING due to ERROR in %s:%d\n", 
838                   my_name, file, line);
839           break;
840        case M_FATAL:
841           if (level == -1)            /* skip details */
842              sprintf(buf, "%s: Fatal Error because: ", my_name);
843           else
844              sprintf(buf, "%s: Fatal Error at %s:%d because:\n", my_name, file, line);
845           break;
846        case M_ERROR:
847           if (level == -1)            /* skip details */
848              sprintf(buf, "%s: Error: ", my_name);
849           else
850              sprintf(buf, "%s: Error in %s:%d ", my_name, file, line);
851           break;
852        case M_WARNING:
853           sprintf(buf, "%s: Warning: ", my_name);
854           break;
855        default:
856           sprintf(buf, "%s: ", my_name);
857           break;
858     }
859
860     i = strlen(buf);
861     va_start(arg_ptr, fmt);
862     bvsnprintf(buf+i, sizeof(buf)-i, (char *)fmt, arg_ptr);
863     va_end(arg_ptr);
864
865     dispatch_message(NULL, type, level, buf);
866
867     if (type == M_ABORT) {
868        abort();
869     }
870 }
871
872 /* *********************************************************
873  *
874  * Generate a Job message
875  *
876  */
877 void 
878 Jmsg(void *vjcr, int type, int level, char *fmt,...)
879 {
880     char     rbuf[2000];
881     char     *buf;
882     va_list   arg_ptr;
883     int i, len;
884     JCR *jcr = (JCR *) vjcr;
885     int typesave = type;
886     MSGS *msgs;
887     char *job;
888
889     
890     Dmsg1(200, "Enter Jmsg type=%d\n", type);
891
892     msgs = NULL;
893     job = NULL;
894     if (jcr) {
895        msgs = jcr->msgs;
896        job = jcr->Job;
897     } 
898     if (!msgs) {
899        msgs = daemon_msgs;
900     }
901     if (!job) {
902        job = "*None*";
903     }
904
905     buf = rbuf;                    /* we are the Director */
906     /* 
907      * Check if we have a message destination defined.  
908      * We always report M_ABORT 
909      */
910     if (type != M_ABORT && msgs && !bit_is_set(type, msgs->send_msg)) {
911        Dmsg1(200, "No bit set for type %d\n", type);
912        return;                        /* no destination */
913     }
914     switch (type) {
915        case M_ABORT:
916           sprintf(buf, "%s ABORTING due to ERROR\n", my_name);
917           break;
918        case M_FATAL:
919           sprintf(buf, "%s: Job %s Fatal error: ", my_name, job);
920           if (jcr) {
921              jcr->JobStatus = JS_FatalError;
922           }
923           break;
924        case M_ERROR:
925           sprintf(buf, "%s: Job %s Error: ", my_name, job);
926           if (jcr) {
927              jcr->Errors++;
928           }
929           break;
930        case M_WARNING:
931           sprintf(buf, "%s: Job %s Warning: ", my_name, job);
932           break;
933        default:
934           sprintf(buf, "%s: ", my_name);
935           break;
936     }
937
938     i = strlen(buf);
939     va_start(arg_ptr, fmt);
940     len = bvsnprintf(buf+i, sizeof(rbuf)-i, fmt, arg_ptr);
941     va_end(arg_ptr);
942
943     ASSERT(typesave==type);           /* type trashed, compiler bug???? */
944     dispatch_message(jcr, type, level, rbuf);
945
946     Dmsg3(500, "i=%d sizeof(rbuf)-i=%d len=%d\n", i, sizeof(rbuf)-i, len);
947
948     if (type == M_ABORT)
949        abort();
950 }
951
952 /*
953  * Edit a message into a Pool memory buffer, with file:lineno
954  */
955 int m_msg(char *file, int line, POOLMEM **pool_buf, char *fmt, ...)
956 {
957    va_list   arg_ptr;
958    int i, len, maxlen;
959
960    sprintf(*pool_buf, "%s:%d ", file, line);
961    i = strlen(*pool_buf);
962
963 again:
964    maxlen = sizeof_pool_memory(*pool_buf) - i - 1; 
965    va_start(arg_ptr, fmt);
966    len = bvsnprintf(*pool_buf+i, maxlen, fmt, arg_ptr);
967    va_end(arg_ptr);
968    if (len < 0 || len >= maxlen) {
969       *pool_buf = realloc_pool_memory(*pool_buf, maxlen + i + 200);
970       goto again;
971    }
972    return len;
973 }
974
975 /*
976  * Edit a message into a Pool Memory buffer NO file:lineno
977  *  Returns: string length of what was edited.
978  */
979 int Mmsg(POOLMEM **pool_buf, char *fmt, ...)
980 {
981    va_list   arg_ptr;
982    int len, maxlen;
983
984 again:
985    maxlen = sizeof_pool_memory(*pool_buf) - 1; 
986    va_start(arg_ptr, fmt);
987    len = bvsnprintf(*pool_buf, maxlen, fmt, arg_ptr);
988    va_end(arg_ptr);
989    if (len < 0 || len >= maxlen) {
990       *pool_buf = realloc_pool_memory(*pool_buf, maxlen + 200);
991       goto again;
992    }
993    return len;
994 }
995
996
997 /*
998  * If we come here, prefix the message with the file:line-number,
999  *  then pass it on to the normal Jmsg routine.
1000  */
1001 void j_msg(char *file, int line, void *jcr, int type, int level, char *fmt,...)
1002 {
1003    va_list   arg_ptr;
1004    int i, len, maxlen;
1005    POOLMEM *pool_buf;
1006
1007    pool_buf = get_pool_memory(PM_EMSG);
1008    sprintf(pool_buf, "%s:%d ", file, line);
1009    i = strlen(pool_buf);
1010
1011 again:
1012    maxlen = sizeof_pool_memory(pool_buf) - i - 1; 
1013    va_start(arg_ptr, fmt);
1014    len = bvsnprintf(pool_buf+i, maxlen, fmt, arg_ptr);
1015    va_end(arg_ptr);
1016    if (len < 0 || len >= maxlen) {
1017       pool_buf = realloc_pool_memory(pool_buf, maxlen + i + 200);
1018       goto again;
1019    }
1020
1021    Jmsg(jcr, type, level, pool_buf);
1022    free_memory(pool_buf);
1023 }