]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/message.c
Add temporary fix to avoid a deadlock after a reload command on an incorrect configur...
[bacula/bacula] / bacula / src / lib / message.c
index e2fd3e416bba31e295995a93a362c91e33e33ebd..cfb838860975c341b0084ab72e7a584517b88444 100644 (file)
@@ -1,35 +1,26 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
-
-   The main author of Bacula is Kern Sibbald, with contributions from
-   many others, a complete list can be found in the file AUTHORS.
-   This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version three of the GNU Affero General Public
-   License as published by the Free Software Foundation and included
-   in the file LICENSE.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU Affero General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA.
-
-   Bacula® is a registered trademark of Kern Sibbald.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is 
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
 /*
  * Bacula message handling routines
  *
  * NOTE: don't use any Jmsg or Qmsg calls within this file,
- *   except in q_msg or j_msg (setup routines), 
+ *   except in q_msg or j_msg (setup routines),
  *   otherwise you may get into recursive calls if there are
  *   errors, and that can lead to looping or deadlocks.
  *
@@ -40,8 +31,8 @@
 #include "bacula.h"
 #include "jcr.h"
 
-sql_query_func p_sql_query = NULL;
-sql_escape_func p_sql_escape = NULL;
+sql_query_call  p_sql_query = NULL;
+sql_escape_call p_sql_escape = NULL;
 
 #define FULL_LOCATION 1               /* set for file:line in Debug messages */
 
@@ -49,23 +40,32 @@ sql_escape_func p_sql_escape = NULL;
  *  This is where we define "Globals" because all the
  *    daemons include this file.
  */
-const char *working_directory = NULL;       /* working directory path stored here */
-int verbose = 0;                      /* increase User messages */
-int debug_level = 0;                  /* debug level */
-bool dbg_timestamp = false;           /* print timestamp in debug output */
-bool prt_kaboom = false;              /* Print kaboom output */
-utime_t daemon_start_time = 0;        /* Daemon start time */
+dlist *daemon_msg_queue = NULL;
+pthread_mutex_t daemon_msg_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+static bool dequeuing_daemon_msgs = false;
+const char *working_directory = NULL; /* working directory path stored here */
+const char *assert_msg = NULL;        /* ASSERT2 error message */
 const char *version = VERSION " (" BDATE ")";
 const char *dist_name = DISTNAME " " DISTVER;
-int beef = BEEF;
-char my_name[30] = {0};               /* daemon name is stored here */
-char host_name[50] = {0};             /* host machine name */
 char *exepath = (char *)NULL;
 char *exename = (char *)NULL;
-int console_msg_pending = false;
+char db_engine_name[50] = {0};        /* Database engine name or type */
 char con_fname[500];                  /* Console filename */
+char my_name[MAX_NAME_LENGTH] = {0};  /* daemon name is stored here */
+char host_name[50] = {0};             /* host machine name */
+char fail_time[30] = {0};             /* Time of failure */
+int verbose = 0;                      /* increase User messages */
+int64_t debug_level = 0;              /* debug level */
+int64_t debug_level_tags = 0;         /* debug tags */
+int32_t debug_flags = 0;              /* debug flags */
+bool console_msg_pending = false;
+utime_t daemon_start_time = 0;        /* Daemon start time */
 FILE *con_fd = NULL;                  /* Console file descriptor */
 brwlock_t con_lock;                   /* Console lock structure */
+bool dbg_timestamp = false;           /* print timestamp in debug output */
+bool dbg_thread = false;              /* add thread_id to details */
+bool prt_kaboom = false;              /* Print kaboom output */
+job_code_callback_t message_job_code_callback = NULL;   /* Job code callback. Only used by director. */
 
 /* Forward referenced functions */
 
@@ -80,7 +80,6 @@ void create_jcr_key();
 /* Allow only one thread to tweak d->fd at a time */
 static pthread_mutex_t fides_mutex = PTHREAD_MUTEX_INITIALIZER;
 static MSGS *daemon_msgs;              /* global messages */
-static char *catalog_db = NULL;       /* database type */
 static void (*message_callback)(int type, char *msg) = NULL;
 static FILE *trace_fd = NULL;
 #if defined(HAVE_WIN32)
@@ -89,6 +88,7 @@ static bool trace = true;
 static bool trace = false;
 #endif
 static int hangup = 0;
+static int blowup = 0;
 
 /* Constants */
 const char *host_os = HOST_OS;
@@ -97,7 +97,7 @@ const char *distver = DISTVER;
 
 /*
  * Walk back in a string from end looking for a
- *  path separator.  
+ *  path separator.
  *  This routine is passed the start of the string and
  *  the end of the string, it returns either the beginning
  *  of the string or where it found a path separator.
@@ -105,7 +105,7 @@ const char *distver = DISTVER;
 static const char *bstrrpath(const char *start, const char *end)
 {
    while ( end > start ) {
-      end--;   
+      end--;
       if (IsPathSeparator(*end)) {
          break;
       }
@@ -173,7 +173,58 @@ static void delivery_error(const char *fmt,...)
    fflush(stdout);
    syslog(LOG_DAEMON|LOG_ERR, "%s", pool_buf);
    free_memory(pool_buf);
-}                 
+}
+
+void set_debug_flags(char *options)
+{
+   for (char *p = options; *p ; p++) {
+      switch(*p) {
+      case '0':                 /* clear flags */
+         debug_flags = 0;
+         break;
+
+      case 'i':                 /* used by FD */
+      case 'd':                 /* used by FD */
+         break;
+
+      case 't':
+         dbg_timestamp = true;
+         break;
+
+      case 'T':
+         dbg_timestamp = false;
+         break;
+
+      case 'h':
+         dbg_thread = true;
+         break;
+
+      case 'H':
+         dbg_thread = false;
+         break;
+
+      case 'c':
+         /* truncate the trace file */
+         if (trace && trace_fd) {
+            ftruncate(fileno(trace_fd), 0);
+         }
+         break;
+
+      case 'l':
+         /* Turn on/off add_events for P()/V() */
+         debug_flags |= DEBUG_MUTEX_EVENT;
+         break;
+
+      case 'p':
+         /* Display event stack during lockdump */
+         debug_flags |= DEBUG_PRINT_EVENT;
+         break;
+
+      default:
+         Dmsg1(000, "Unknown debug flag %c\n", *p);
+      }
+   }
+}
 
 void register_message_callback(void msg_callback(int type, char *msg))
 {
@@ -245,15 +296,21 @@ void my_name_is(int argc, char *argv[], const char *name)
    }
 }
 
+/* Set special ASSERT2 message where debugger can find it */
 void
-set_db_type(const char *name)
+set_assert_msg(const char *file, int line, const char *msg)
 {
-   if (catalog_db != NULL) {
-      free(catalog_db);
-   }
-   catalog_db = bstrdup(name);
+   char buf[2000];
+   bsnprintf(buf, sizeof(buf), "ASSERT at %s:%d-%u ERR=%s",
+      get_basename(file), line, get_jobid_from_tsd(), msg);
+   assert_msg = bstrdup(buf);
 }
 
+void set_db_engine_name(const char *name)
+{ 
+   bstrncpy(db_engine_name, name, sizeof(db_engine_name)-1);
+} 
 /*
  * Initialize message handler for a daemon or a Job
  *   We make a copy of the MSGS resource passed, so it belows
@@ -263,7 +320,7 @@ set_db_type(const char *name)
  *   non-NULL     -> initialize jcr using Message resource
  */
 void
-init_msg(JCR *jcr, MSGS *msg)
+init_msg(JCR *jcr, MSGS *msg, job_code_callback_t job_code_callback)
 {
    DEST *d, *dnew, *temp_chain = NULL;
    int i;
@@ -276,6 +333,8 @@ init_msg(JCR *jcr, MSGS *msg)
       set_jcr_in_tsd(INVALID_JCR);
    }
 
+   message_job_code_callback = job_code_callback;
+
 #if !defined(HAVE_WIN32)
    /*
     * Make sure we have fd's 0, 1, 2 open
@@ -344,7 +403,6 @@ init_msg(JCR *jcr, MSGS *msg)
    }
 
    Dmsg2(250, "Copy message resource %p to %p\n", msg, temp_chain);
-
 }
 
 /* Initialize so that the console (User Agent) can
@@ -362,10 +420,10 @@ void init_console_msg(const char *wd)
           con_fname, be.bstrerror());
    }
    if (lseek(fd, 0, SEEK_END) > 0) {
-      console_msg_pending = 1;
+      console_msg_pending = true;
    }
    close(fd);
-   con_fd = fopen(con_fname, "a+b");
+   con_fd = bfopen(con_fname, "a+b");
    if (!con_fd) {
       berrno be;
       Emsg2(M_ERROR, 0, _("Could not open console message file %s: ERR=%s\n"),
@@ -469,7 +527,7 @@ static BPIPE *open_mail_pipe(JCR *jcr, POOLMEM *&cmd, DEST *d)
    BPIPE *bpipe;
 
    if (d->mail_cmd) {
-      cmd = edit_job_codes(jcr, cmd, d->mail_cmd, d->where);
+      cmd = edit_job_codes(jcr, cmd, d->mail_cmd, d->where, message_job_code_callback);
    } else {
       Mmsg(cmd, "/usr/lib/sendmail -F Bacula %s", d->where);
    }
@@ -528,6 +586,7 @@ void close_msg(JCR *jcr)
    Dmsg1(850, "===Begin close msg resource at %p\n", msgs);
    cmd = get_pool_memory(PM_MESSAGE);
    for (d=msgs->dest_chain; d; ) {
+      bool success;
       if (d->fd) {
          switch (d->dest_code) {
          case MD_FILE:
@@ -544,19 +603,17 @@ void close_msg(JCR *jcr)
             if (!d->fd) {
                break;
             }
-            if (
-                (d->dest_code == MD_MAIL_ON_ERROR && jcr &&
-                  (jcr->JobStatus == JS_Terminated || jcr->JobStatus == JS_Warnings)) 
-                ||
-                (d->dest_code == MD_MAIL_ON_SUCCESS && jcr &&
-                 jcr->JobStatus == JS_ErrorTerminated)
-                ) {
-               goto rem_temp_file;
+            success = jcr && (jcr->JobStatus == JS_Terminated || jcr->JobStatus == JS_Warnings);
+
+            if (d->dest_code == MD_MAIL_ON_ERROR && success) {
+               goto rem_temp_file;       /* no mail */
+            } else if (d->dest_code == MD_MAIL_ON_SUCCESS && !success) {
+               goto rem_temp_file;       /* no mail */
             }
 
             if (!(bpipe=open_mail_pipe(jcr, cmd, d))) {
                Pmsg0(000, _("open mail pipe failed.\n"));
-               goto rem_temp_file;
+               goto rem_temp_file;       /* error get out */
             }
             Dmsg0(850, "Opened mail pipe\n");
             len = d->max_len+10;
@@ -592,14 +649,15 @@ void close_msg(JCR *jcr)
                                  "ERR=%s\n"), cmd, be.bstrerror());
             }
             free_memory(line);
+
 rem_temp_file:
-            /* Remove temp file */
+            /* Remove temp mail file */
             if (d->fd) {
-               fclose(d->fd);
-               d->fd = NULL;
+              fclose(d->fd);
+              d->fd = NULL;
             }
+            /* Exclude spaces in mail_filename */
             if (d->mail_filename) {
-               /* Exclude spaces in mail_filename */
                safer_unlink(d->mail_filename, MAIL_REGEX);
                free_pool_memory(d->mail_filename);
                d->mail_filename = NULL;
@@ -635,9 +693,11 @@ void free_msgs_res(MSGS *msgs)
    for (d=msgs->dest_chain; d; ) {
       if (d->where) {
          free(d->where);
+         d->where = NULL;
       }
       if (d->mail_cmd) {
          free(d->mail_cmd);
+         d->mail_cmd = NULL;
       }
       old = d;                        /* save pointer to release */
       d = d->next;                    /* point to next buffer */
@@ -677,17 +737,15 @@ void term_msg()
    if (trace_fd) {
       fclose(trace_fd);
       trace_fd = NULL;
+      trace = false;
    }
-   if (catalog_db) {
-      free(catalog_db);
-      catalog_db = NULL;
-   }
+   working_directory = NULL;
    term_last_jobs_list();
 }
 
-static bool open_dest_file(JCR *jcr, DEST *d, const char *mode) 
+static bool open_dest_file(JCR *jcr, DEST *d, const char *mode)
 {
-   d->fd = fopen(d->where, mode);
+   d->fd = bfopen(d->where, mode);
    if (!d->fd) {
       berrno be;
       delivery_error(_("fopen %s failed: ERR=%s\n"), d->where, be.bstrerror());
@@ -730,6 +788,7 @@ void dispatch_message(JCR *jcr, int type, utime_t mtime, char *msg)
     MSGS *msgs;
     BPIPE *bpipe;
     const char *mode;
+    bool created_jcr = false;
 
     Dmsg2(850, "Enter dispatch_msg type=%d msg=%s", type, msg);
 
@@ -775,6 +834,18 @@ void dispatch_message(JCR *jcr, int type, utime_t mtime, char *msg)
     if (!jcr) {
        jcr = get_jcr_from_tsd();
     }
+
+/* Temporary fix for a deadlock in the reload command when
+ * the configuration has a problem. The JCR chain is locked
+ * during the reload, we cannot create a new JCR.
+ */
+#if 0
+    if (!jcr) {
+       jcr = new_jcr(sizeof(JCR), NULL);
+       created_jcr = true;
+    }
+#endif
+
     if (jcr) {
        msgs = jcr->jcr_msgs;
     }
@@ -793,27 +864,32 @@ void dispatch_message(JCR *jcr, int type, utime_t mtime, char *msg)
        return;
     }
 
+
     for (d=msgs->dest_chain; d; d=d->next) {
        if (bit_is_set(type, d->msg_types)) {
+          bool ok;
           switch (d->dest_code) {
              case MD_CATALOG:
                 char ed1[50];
-                if (!jcr || !jcr->db) {
+                if (!jcr || !jcr->db) { 
                    break;
                 }
                 if (p_sql_query && p_sql_escape) {
                    POOLMEM *cmd = get_pool_memory(PM_MESSAGE);
                    POOLMEM *esc_msg = get_pool_memory(PM_MESSAGE);
-                   
+
                    int len = strlen(msg) + 1;
                    esc_msg = check_pool_memory_size(esc_msg, len*2+1);
-                   p_sql_escape(jcr, jcr->db, esc_msg, msg, len);
-
-                   bstrutime(dt, sizeof(dt), mtime);
-                   Mmsg(cmd, "INSERT INTO Log (JobId, Time, LogText) VALUES (%s,'%s','%s')",
-                         edit_int64(jcr->JobId, ed1), dt, esc_msg);
-                   p_sql_query(jcr, cmd);
-                   
+                   ok = p_sql_escape(jcr, jcr->db, esc_msg, msg, len);
+                   if (ok) {
+                      bstrutime(dt, sizeof(dt), mtime); 
+                      Mmsg(cmd, "INSERT INTO Log (JobId, Time, LogText) VALUES (%s,'%s','%s')", 
+                           edit_int64(jcr->JobId, ed1), dt, esc_msg);
+                      ok = p_sql_query(jcr, cmd);
+                   } 
+                   if (!ok) {
+                      delivery_error(_("Message delivery error: Unable to store data in database.\n"));
+                   } 
                    free_pool_memory(cmd);
                    free_pool_memory(esc_msg);
                 }
@@ -821,7 +897,7 @@ void dispatch_message(JCR *jcr, int type, utime_t mtime, char *msg)
              case MD_CONSOLE:
                 Dmsg1(850, "CONSOLE for following msg: %s", msg);
                 if (!con_fd) {
-                   con_fd = fopen(con_fname, "a+b");
+                   con_fd = bfopen(con_fname, "a+b");
                    Dmsg0(850, "Console file not open.\n");
                 }
                 if (con_fd) {
@@ -881,7 +957,7 @@ void dispatch_message(JCR *jcr, int type, utime_t mtime, char *msg)
                 if (!d->fd) {
                    POOLMEM *name = get_pool_memory(PM_MESSAGE);
                    make_unique_mail_filename(jcr, name, d);
-                   d->fd = fopen(name, "w+b");
+                   d->fd = bfopen(name, "w+b");
                    if (!d->fd) {
                       berrno be;
                       delivery_error(_("Msg delivery error: fopen %s failed: ERR=%s\n"), name,
@@ -932,8 +1008,8 @@ send_to_file:
              case MD_DIRECTOR:
                 Dmsg1(850, "DIRECTOR for following msg: %s", msg);
                 if (jcr && jcr->dir_bsock && !jcr->dir_bsock->errors) {
-                   jcr->dir_bsock->fsend("Jmsg Job=%s type=%d level=%lld %s",
-                      jcr->Job, type, mtime, msg);
+                   jcr->dir_bsock->fsend("Jmsg JobId=%ld type=%d level=%lld %s",
+                      jcr->JobId, type, mtime, msg);
                 } else {
                    Dmsg1(800, "no jcr for following msg: %s", msg);
                 }
@@ -957,19 +1033,22 @@ send_to_file:
           }
        }
     }
+    if (created_jcr) {
+       free_jcr(jcr);
+    }
 }
 
 /*********************************************************************
  *
- *  This subroutine returns the filename portion of a path.  
- *  It is used because some compilers set __FILE__ 
+ *  This subroutine returns the filename portion of a path.
+ *  It is used because some compilers set __FILE__
  *  to the full path.  Try to return base + next higher path.
  */
 
 const char *get_basename(const char *pathname)
 {
    const char *basename;
-   
+
    if ((basename = bstrrpath(pathname, pathname+strlen(pathname))) == pathname) {
       /* empty */
    } else if ((basename = bstrrpath(pathname, basename-1)) == pathname) {
@@ -981,7 +1060,7 @@ const char *get_basename(const char *pathname)
 }
 
 /*
- * print or write output to trace file 
+ * print or write output to trace file
  */
 static void pt_out(char *buf)
 {
@@ -993,7 +1072,7 @@ static void pt_out(char *buf)
        if (!trace_fd) {
           char fn[200];
           bsnprintf(fn, sizeof(fn), "%s/%s.trace", working_directory ? working_directory : "./", my_name);
-          trace_fd = fopen(fn, "a+b");
+          trace_fd = bfopen(fn, "a+b");
        }
        if (trace_fd) {
           fputs(buf, trace_fd);
@@ -1018,13 +1097,13 @@ static void pt_out(char *buf)
  *
  *  If the level is negative, the details of file and line number
  *  are not printed.
+ *
  */
 void
-d_msg(const char *file, int line, int level, const char *fmt,...)
+vd_msg(const char *file, int line, int64_t level, const char *fmt, va_list arg_ptr)
 {
     char      buf[5000];
-    int       len;
-    va_list   arg_ptr;
+    int       len = 0; /* space used in buf */
     bool      details = true;
     utime_t   mtime;
 
@@ -1033,34 +1112,42 @@ d_msg(const char *file, int line, int level, const char *fmt,...)
        level = -level;
     }
 
-    if (level <= debug_level) {
+    if (chk_dbglvl(level)) {
        if (dbg_timestamp) {
           mtime = time(NULL);
-          bstrftimes(buf, sizeof(buf), mtime);
+          bstrftimes(buf+len, sizeof(buf)-len, mtime);
           len = strlen(buf);
           buf[len++] = ' ';
-          buf[len] = 0;
-          pt_out(buf);
        }
-    
+
 #ifdef FULL_LOCATION
        if (details) {
-          len = bsnprintf(buf, sizeof(buf), "%s: %s:%d-%u ", 
-                my_name, get_basename(file), line, get_jobid_from_tsd());
-       } else {
-          len = 0;
+          if (dbg_thread) {
+             len += bsnprintf(buf+len, sizeof(buf)-len, "%s[%lld]: %s:%d-%u ",
+                             my_name, bthread_get_thread_id(),
+                             get_basename(file), line, get_jobid_from_tsd());
+          } else {
+             len += bsnprintf(buf+len, sizeof(buf)-len, "%s: %s:%d-%u ",
+                   my_name, get_basename(file), line, get_jobid_from_tsd());
+          }
        }
-#else
-       len = 0;
 #endif
-       va_start(arg_ptr, fmt);
        bvsnprintf(buf+len, sizeof(buf)-len, (char *)fmt, arg_ptr);
-       va_end(arg_ptr);
 
        pt_out(buf);
     }
 }
 
+void
+d_msg(const char *file, int line, int64_t level, const char *fmt,...)
+{
+   va_list arg_ptr;
+   va_start(arg_ptr, fmt);
+   vd_msg(file, line, level, fmt, arg_ptr); /* without tags */
+   va_end(arg_ptr);
+}
+
+
 /*
  * Set trace flag on/off. If argument is negative, there is no change
  */
@@ -1083,9 +1170,7 @@ void set_trace(int trace_flag)
 
 void set_hangup(int hangup_value)
 {
-   if (hangup_value < 0) {
-      return;
-   } else {
+   if (hangup_value != -1) {
       hangup = hangup_value;
    }
 }
@@ -1095,6 +1180,49 @@ int get_hangup(void)
    return hangup;
 }
 
+void set_blowup(int blowup_value)
+{
+   if (blowup_value != -1) {
+      blowup = blowup_value;
+   }
+}
+
+int get_blowup(void)
+{
+   return blowup;
+}
+
+bool handle_hangup_blowup(JCR *jcr, uint32_t file_count, uint64_t byte_count)
+{
+   if (hangup == 0 && blowup == 0) {
+      /* quick check */
+      return false;
+   }
+   /* Debug code: check if we must hangup  or blowup */
+   if ((hangup > 0 && (file_count > (uint32_t)hangup)) ||
+       (hangup < 0 && (byte_count/1024 > (uint32_t)-hangup)))  {
+      jcr->setJobStatus(JS_Incomplete);
+      if (hangup > 0) {
+         Jmsg1(jcr, M_FATAL, 0, "Debug hangup requested after %d files.\n", hangup);
+      } else {
+         Jmsg1(jcr, M_FATAL, 0, "Debug hangup requested after %d Kbytes.\n", -hangup);
+      }
+      set_hangup(0);
+      return true;
+   }
+   if ((blowup > 0 && (file_count > (uint32_t)blowup)) ||
+       (blowup < 0 && (byte_count/1024 > (uint32_t)-blowup)))  {
+      if (blowup > 0) {
+         Jmsg1(jcr, M_ABORT, 0, "Debug blowup requested after %d files.\n", blowup);
+      } else {
+         Jmsg1(jcr, M_ABORT, 0, "Debug blowup requested after %d Kbytes.\n", -blowup);
+      }
+      /* will never reach this line */
+      return true;
+   }
+   return false;
+}
+
 bool get_trace(void)
 {
    return trace;
@@ -1111,23 +1239,28 @@ void
 p_msg(const char *file, int line, int level, const char *fmt,...)
 {
     char      buf[5000];
-    int       len;
+    int       len = 0; /* space used in buf */
     va_list   arg_ptr;
 
+    if (dbg_timestamp) {
+       utime_t mtime = time(NULL);
+       bstrftimes(buf+len, sizeof(buf)-len, mtime);
+       len = strlen(buf);
+       buf[len++] = ' ';
+    }
+
 #ifdef FULL_LOCATION
     if (level >= 0) {
-       len = bsnprintf(buf, sizeof(buf), "%s: %s:%d ", my_name, get_basename(file), line);
-    } else {
-       len = 0;
+       len += bsnprintf(buf+len, sizeof(buf)-len, "%s: %s:%d-%u ",
+             my_name, get_basename(file), line, get_jobid_from_tsd());
     }
-#else
-    len = 0;
 #endif
+
     va_start(arg_ptr, fmt);
     bvsnprintf(buf+len, sizeof(buf)-len, (char *)fmt, arg_ptr);
     va_end(arg_ptr);
 
-    pt_out(buf);     
+    pt_out(buf);
 }
 
 
@@ -1142,13 +1275,15 @@ p_msg(const char *file, int line, int level, const char *fmt,...)
  *  are not printed.
  */
 void
-t_msg(const char *file, int line, int level, const char *fmt,...)
+t_msg(const char *file, int line, int64_t level, const char *fmt,...)
 {
     char      buf[5000];
     int       len;
     va_list   arg_ptr;
     int       details = TRUE;
 
+    level = level & ~DT_ALL;    /* level should be tag free */
+
     if (level < 0) {
        details = FALSE;
        level = -level;
@@ -1157,7 +1292,7 @@ t_msg(const char *file, int line, int level, const char *fmt,...)
     if (level <= debug_level) {
        if (!trace_fd) {
           bsnprintf(buf, sizeof(buf), "%s/%s.trace", working_directory ? working_directory : ".", my_name);
-          trace_fd = fopen(buf, "a+b");
+          trace_fd = bfopen(buf, "a+b");
        }
 
 #ifdef FULL_LOCATION
@@ -1235,6 +1370,7 @@ e_msg(const char *file, int line, int type, int level, const char *fmt,...)
     bvsnprintf(buf+len, sizeof(buf)-len, (char *)fmt, arg_ptr);
     va_end(arg_ptr);
 
+    pt_out(buf);
     dispatch_message(NULL, type, 0, buf);
 
     if (type == M_ABORT) {
@@ -1246,6 +1382,23 @@ e_msg(const char *file, int line, int type, int level, const char *fmt,...)
     }
 }
 
+/* Check in the msgs resource if a given type is defined */
+bool is_message_type_set(JCR *jcr, int type)
+{
+   MSGS *msgs = NULL;
+   if (jcr) {
+       msgs = jcr->jcr_msgs;
+   }
+   if (!msgs) {
+      msgs = daemon_msgs;            /* if no jcr, we use daemon handler */
+   }
+   if (msgs && (type != M_ABORT && type != M_ERROR_TERM) &&
+       !bit_is_set(type, msgs->send_msg)) {
+      return false;                 /* no destination */
+   }
+   return true;
+}
+
 /* *********************************************************
  *
  * Generate a Job message
@@ -1293,7 +1446,7 @@ Jmsg(JCR *jcr, int type, utime_t mtime, const char *fmt,...)
     if (jcr) {
        if (!jcr->dequeuing_msgs) { /* Avoid recursion */
           /* Dequeue messages to keep the original order  */
-          dequeue_messages(jcr); 
+          dequeue_messages(jcr);
        }
        msgs = jcr->jcr_msgs;
        JobId = jcr->JobId;
@@ -1339,7 +1492,7 @@ Jmsg(JCR *jcr, int type, utime_t mtime, const char *fmt,...)
        }
        break;
     case M_SECURITY:
-       len = bsnprintf(rbuf, sizeof(rbuf), _("%s JobId %u: Security violation: "), 
+       len = bsnprintf(rbuf, sizeof(rbuf), _("%s JobId %u: Security violation: "),
                my_name, JobId);
        break;
     default:
@@ -1374,6 +1527,10 @@ void j_msg(const char *file, int line, JCR *jcr, int type, utime_t mtime, const
    int i, len, maxlen;
    POOLMEM *pool_buf;
 
+   va_start(arg_ptr, fmt);
+   vd_msg(file, line, 0, fmt, arg_ptr);
+   va_end(arg_ptr);
+
    pool_buf = get_pool_memory(PM_EMSG);
    i = Mmsg(pool_buf, "%s:%d ", get_basename(file), line);
 
@@ -1536,10 +1693,19 @@ void Qmsg(JCR *jcr, int type, utime_t mtime, const char *fmt,...)
    if (!jcr) {
       jcr = get_jcr_from_tsd();
    }
+
+   if (jcr && type==M_FATAL) {
+      jcr->setJobStatus(JS_FatalError);
+    }
+
    /* If no jcr or no queue or dequeuing send to syslog */
    if (!jcr || !jcr->msg_queue || jcr->dequeuing_msgs) {
       syslog(LOG_DAEMON|LOG_ERR, "%s", item->msg);
-      free(item);
+      P(daemon_msg_queue_mutex);
+      if (daemon_msg_queue) {
+         daemon_msg_queue->append(item);
+      }
+      V(daemon_msg_queue_mutex);
    } else {
       /* Queue message for later sending */
       P(jcr->msg_queue_mutex);
@@ -1555,14 +1721,44 @@ void Qmsg(JCR *jcr, int type, utime_t mtime, const char *fmt,...)
 void dequeue_messages(JCR *jcr)
 {
    MQUEUE_ITEM *item;
-   if (!jcr->msg_queue) {
+   JobId_t JobId;
+
+   /* Avoid bad calls and recursion */
+   if (!jcr || jcr->dequeuing_msgs) {
+      return;
+   }
+
+   /* Dequeue daemon messages */
+   if (daemon_msg_queue && !dequeuing_daemon_msgs) {
+      P(daemon_msg_queue_mutex);
+      dequeuing_daemon_msgs = true;
+      jcr->dequeuing_msgs = true;
+      JobId = jcr->JobId;
+      jcr->JobId = 0;       /* set daemon JobId == 0 */
+      if (jcr->dir_bsock) jcr->dir_bsock->suppress_error_messages(true);
+      foreach_dlist(item, daemon_msg_queue) {
+         Jmsg(jcr, item->type, item->mtime, "%s", item->msg);
+      }
+      if (jcr->dir_bsock) jcr->dir_bsock->suppress_error_messages(false);
+      /* Remove messages just sent */
+      daemon_msg_queue->destroy();
+      jcr->JobId = JobId;   /* restore JobId */
+      jcr->dequeuing_msgs = false;
+      dequeuing_daemon_msgs = false;
+      V(daemon_msg_queue_mutex);
+   }
+
+   /* Dequeue Job specific messages */
+   if (!jcr->msg_queue || jcr->dequeuing_msgs) {
       return;
    }
    P(jcr->msg_queue_mutex);
    jcr->dequeuing_msgs = true;
+   if (jcr->dir_bsock) jcr->dir_bsock->suppress_error_messages(true);
    foreach_dlist(item, jcr->msg_queue) {
       Jmsg(jcr, item->type, item->mtime, "%s", item->msg);
    }
+   if (jcr->dir_bsock) jcr->dir_bsock->suppress_error_messages(false);
    /* Remove messages just sent */
    jcr->msg_queue->destroy();
    jcr->dequeuing_msgs = false;
@@ -1598,3 +1794,136 @@ void q_msg(const char *file, int line, JCR *jcr, int type, utime_t mtime, const
    Qmsg(jcr, type, mtime, "%s", pool_buf);
    free_memory(pool_buf);
 }
+
+
+/* not all in alphabetical order.  New commands are added after existing commands with similar letters
+   to prevent breakage of existing user scripts.  */
+struct debugtags {
+   const char *tag;             /* command */
+   int64_t     bit;             /* bit to set */
+   const char *help;            /* main purpose */
+};
+
+/* setdebug tag=all,-plugin */
+static struct debugtags debug_tags[] = {
+ { NT_("lock"),        DT_LOCK,     _("Debug lock information")},
+ { NT_("network"),     DT_NETWORK,  _("Debug network information")},
+ { NT_("plugin"),      DT_PLUGIN,   _("Debug plugin information")},
+ { NT_("volume"),      DT_VOLUME,   _("Debug volume information")},
+ { NT_("sql"),         DT_SQL,      _("Debug SQL queries")},
+ { NT_("bvfs"),        DT_BVFS,     _("Debug BVFS queries")},
+ { NT_("memory"),      DT_MEMORY,   _("Debug memory allocation")},
+ { NT_("scheduler"),   DT_SCHEDULER,_("Debug scheduler information")},
+ { NT_("protocol"),    DT_PROTOCOL, _("Debug protocol information")},
+ { NT_("snapshot"),    DT_SNAPSHOT, _("Debug snapshots")},
+ { NT_("asx"),         DT_ASX,      _("ASX personal's debugging")},
+ { NT_("all"),         DT_ALL,      _("Debug all information")},
+ { NULL,               0,   NULL}
+};
+
+#define MAX_TAG (sizeof(debug_tags) / sizeof(struct debugtags))
+
+const char *debug_get_tag(uint32_t pos, const char **desc)
+{
+   if (pos < MAX_TAG) {
+      if (desc) {
+         *desc = debug_tags[pos].help;
+      }
+      return debug_tags[pos].tag;
+   }
+   return NULL;
+}
+
+/* Allow +-, */
+bool debug_find_tag(const char *tagname, bool add, int64_t *current_level)
+{
+   Dmsg3(010, "add=%d tag=%s level=%lld\n", add, tagname, *current_level);
+   if (!*tagname) {
+      /* Nothing in the buffer */
+      return true;
+   }
+   for (int i=0; debug_tags[i].tag ; i++) {
+      if (strcasecmp(debug_tags[i].tag, tagname) == 0) {
+         if (add) {
+            *current_level |= debug_tags[i].bit;
+         } else {
+            *current_level &= ~(debug_tags[i].bit);
+         }
+         return true;
+      }
+   }
+   return false;
+}
+
+bool debug_parse_tags(const char *options, int64_t *current_level)
+{
+   bool operation;              /* + => true, - false */
+   char *p, *t, tag[256];
+   int max = sizeof(tag) - 1;
+   bool ret=true;
+   int64_t level= *current_level;
+
+   t = tag;
+   *tag = 0;
+   operation = true;            /* add by default */
+
+   if (!options) {
+      Dmsg0(100, "No options for tags\n");
+      return false;
+   }
+
+   for (p = (char *)options; *p ; p++) {
+      if (*p == ',' || *p == '+' || *p == '-' || *p == '!') {
+         /* finish tag keyword */
+         *t = 0;
+         /* handle tag */
+         ret &= debug_find_tag(tag, operation, &level);
+
+         if (*p == ',') {
+            /* reset tag */
+            t = tag;
+            *tag = 0;
+            operation = true;
+
+         } else {
+            /* reset tag */
+            t = tag;
+            *tag = 0;
+            operation = (*p == '+');
+         }
+
+      } else if (isalpha(*p) && (t - tag) < max) {
+         *t++ = *p;
+
+      } else {                  /* not isalpha or too long */
+         Dmsg1(010, "invalid %c\n", *p);
+         return false;
+      }
+   }
+
+   /* At the end, finish the string and look it */
+   *t = 0;
+   if (t > tag) {               /* something found */
+      /* handle tag */
+      ret &= debug_find_tag(tag, operation, &level);
+   }
+
+   *current_level = level;
+   return ret;
+}
+
+int generate_daemon_event(JCR *jcr, const char *event) { return 0; }
+
+void setup_daemon_message_queue()
+{
+   static MQUEUE_ITEM *item = NULL;
+   daemon_msg_queue = New(dlist(item, &item->link));
+}
+
+void free_daemon_message_queue()
+{
+   P(daemon_msg_queue_mutex);
+   daemon_msg_queue->destroy();
+   free(daemon_msg_queue);
+   V(daemon_msg_queue_mutex);
+}