X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Flib%2Fmessage.c;h=cfb838860975c341b0084ab72e7a584517b88444;hb=5fdde77b03110df59baeda81488677e502433794;hp=5af7851074e1b4ad9fd9bae88125e44854c4f93b;hpb=481b1253dbff30c5b2659e82ebac61da09e6577b;p=bacula%2Fbacula diff --git a/bacula/src/lib/message.c b/bacula/src/lib/message.c index 5af7851074..cfb8388609 100644 --- a/bacula/src/lib/message.c +++ b/bacula/src/lib/message.c @@ -1,35 +1,26 @@ /* - Bacula® - The Network Backup Solution - - Copyright (C) 2000-2011 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 */ @@ -74,10 +74,12 @@ void create_jcr_key(); /* Static storage */ +/* Exclude spaces but require .mail at end */ +#define MAIL_REGEX "^[^ ]+\\.mail$" + /* 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) @@ -86,12 +88,31 @@ static bool trace = true; static bool trace = false; #endif static int hangup = 0; +static int blowup = 0; /* Constants */ const char *host_os = HOST_OS; const char *distname = DISTNAME; const char *distver = DISTVER; +/* + * Walk back in a string from end looking for a + * 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. + */ +static const char *bstrrpath(const char *start, const char *end) +{ + while ( end > start ) { + end--; + if (IsPathSeparator(*end)) { + break; + } + } + return end; +} + /* Some message class methods */ void MSGS::lock() { @@ -109,7 +130,7 @@ void MSGS::unlock() void MSGS::wait_not_in_use() /* leaves fides_mutex set */ { lock(); - while (m_in_use) { + while (m_in_use || m_closing) { unlock(); bmicrosleep(0, 200); /* wait */ lock(); @@ -152,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)) { @@ -224,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 @@ -242,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; @@ -255,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 @@ -323,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 @@ -341,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"), @@ -448,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); } @@ -496,12 +575,18 @@ void close_msg(JCR *jcr) return; } msgs->wait_not_in_use(); /* leaves fides_mutex set */ + /* Note get_closing() does not lock because we are already locked */ + if (msgs->get_closing()) { + msgs->unlock(); + return; + } msgs->set_closing(); msgs->unlock(); 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: @@ -518,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; @@ -566,13 +649,19 @@ void close_msg(JCR *jcr) "ERR=%s\n"), cmd, be.bstrerror()); } free_memory(line); + rem_temp_file: - /* Remove temp file */ - fclose(d->fd); - d->fd = NULL; - unlink(d->mail_filename); - free_pool_memory(d->mail_filename); - d->mail_filename = NULL; + /* Remove temp mail file */ + if (d->fd) { + fclose(d->fd); + d->fd = NULL; + } + /* Exclude spaces in mail_filename */ + if (d->mail_filename) { + safer_unlink(d->mail_filename, MAIL_REGEX); + free_pool_memory(d->mail_filename); + d->mail_filename = NULL; + } Dmsg0(850, "end mail or mail on error\n"); break; default: @@ -604,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 */ @@ -646,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()); @@ -666,7 +755,7 @@ static bool open_dest_file(JCR *jcr, DEST *d, const char *mode) } /* Split the output for syslog (it converts \n to ' ' and is - * limited to 1024c + * limited to 1024 characters per syslog message */ static void send_to_syslog(int mode, const char *msg) { @@ -675,9 +764,8 @@ static void send_to_syslog(int mode, const char *msg) const char *p2; const char *p = msg; - while (*p && ((p2 = strchr(p, '\n')) != NULL)) - { - len = MIN(sizeof(buf) - 1, p2 - p + 1); /* Add 1 to keep \n */ + while (*p && ((p2 = strchr(p, '\n')) != NULL)) { + len = MIN((int)sizeof(buf) - 1, p2 - p + 1); /* Add 1 to keep \n */ strncpy(buf, p, len); buf[len] = 0; syslog(mode, "%s", buf); @@ -700,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); @@ -745,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; } @@ -763,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); } @@ -791,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) { @@ -851,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, @@ -902,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); } @@ -927,33 +1033,34 @@ 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, *basename2; - - if ((basename = strrchr(pathname, PathSeparator)) == NULL) { - basename = pathname; - if ((basename2 = strrchr(pathname, PathSeparator)) != NULL) { - basename = basename2 + 1; - } + const char *basename; + + if ((basename = bstrrpath(pathname, pathname+strlen(pathname))) == pathname) { + /* empty */ + } else if ((basename = bstrrpath(pathname, basename-1)) == pathname) { + /* empty */ } else { basename++; } - return basename; } /* - * print or write output to trace file + * print or write output to trace file */ static void pt_out(char *buf) { @@ -965,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); @@ -990,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; @@ -1005,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 */ @@ -1055,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; } } @@ -1067,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; @@ -1083,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); } @@ -1114,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; @@ -1129,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 @@ -1207,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) { @@ -1218,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 @@ -1265,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; @@ -1311,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: @@ -1346,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); @@ -1508,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); @@ -1527,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; @@ -1570,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); +}