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