pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
logging.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2020 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <sys/param.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <sys/stat.h>
16 #include <sys/utsname.h>
17 
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <limits.h>
23 #include <ctype.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #include <time.h>
27 #include <libgen.h>
28 #include <signal.h>
29 #include <bzlib.h>
30 
31 #include <qb/qbdefs.h>
32 
33 #include <crm/crm.h>
34 #include <crm/common/mainloop.h>
35 
36 unsigned int crm_log_priority = LOG_NOTICE;
37 unsigned int crm_log_level = LOG_INFO;
38 static gboolean crm_tracing_enabled(void);
39 unsigned int crm_trace_nonlog = 0;
40 bool crm_is_daemon = 0;
41 
43 
44 static void
45 crm_glib_handler(const gchar * log_domain, GLogLevelFlags flags, const gchar * message,
46  gpointer user_data)
47 {
48  int log_level = LOG_WARNING;
49  GLogLevelFlags msg_level = (flags & G_LOG_LEVEL_MASK);
50  static struct qb_log_callsite *glib_cs = NULL;
51 
52  if (glib_cs == NULL) {
53  glib_cs = qb_log_callsite_get(__FUNCTION__, __FILE__, "glib-handler", LOG_DEBUG, __LINE__, crm_trace_nonlog);
54  }
55 
56 
57  switch (msg_level) {
58  case G_LOG_LEVEL_CRITICAL:
59  log_level = LOG_CRIT;
60 
61  if (crm_is_callsite_active(glib_cs, LOG_DEBUG, 0) == FALSE) {
62  /* log and record how we got here */
63  crm_abort(__FILE__, __FUNCTION__, __LINE__, message, TRUE, TRUE);
64  }
65  break;
66 
67  case G_LOG_LEVEL_ERROR:
68  log_level = LOG_ERR;
69  break;
70  case G_LOG_LEVEL_MESSAGE:
71  log_level = LOG_NOTICE;
72  break;
73  case G_LOG_LEVEL_INFO:
74  log_level = LOG_INFO;
75  break;
76  case G_LOG_LEVEL_DEBUG:
77  log_level = LOG_DEBUG;
78  break;
79 
80  case G_LOG_LEVEL_WARNING:
81  case G_LOG_FLAG_RECURSION:
82  case G_LOG_FLAG_FATAL:
83  case G_LOG_LEVEL_MASK:
84  log_level = LOG_WARNING;
85  break;
86  }
87 
88  do_crm_log(log_level, "%s: %s", log_domain, message);
89 }
90 
91 #ifndef NAME_MAX
92 # define NAME_MAX 256
93 #endif
94 
103 static void
104 crm_trigger_blackbox(int nsig)
105 {
106  if(nsig == SIGTRAP) {
107  /* Turn it on if it wasn't already */
108  crm_enable_blackbox(nsig);
109  }
110  crm_write_blackbox(nsig, NULL);
111 }
112 
113 void
115 {
116  g_log_set_default_handler(glib_log_default, NULL);
117 }
118 
119 #define FMT_MAX 256
120 
121 static void
122 set_format_string(int method, const char *daemon)
123 {
124  if (method == QB_LOG_SYSLOG) {
125  // The system log gets a simplified, user-friendly format
126  crm_extended_logging(method, QB_FALSE);
127  qb_log_format_set(method, "%g %p: %b");
128 
129  } else {
130  // Everything else gets more detail, for advanced troubleshooting
131 
132  int offset = 0;
133  char fmt[FMT_MAX];
134 
135  if (method > QB_LOG_STDERR) {
136  struct utsname res;
137  const char *nodename = "localhost";
138 
139  if (uname(&res) == 0) {
140  nodename = res.nodename;
141  }
142 
143  // If logging to file, prefix with timestamp, node name, daemon ID
144  offset += snprintf(fmt + offset, FMT_MAX - offset,
145  "%%t %s %-20s[%lu] ",
146  nodename, daemon, (unsigned long) getpid());
147  }
148 
149  // Add function name (in parentheses)
150  offset += snprintf(fmt + offset, FMT_MAX - offset, "(%%n");
151  if (crm_tracing_enabled()) {
152  // When tracing, add file and line number
153  offset += snprintf(fmt + offset, FMT_MAX - offset, "@%%f:%%l");
154  }
155  offset += snprintf(fmt + offset, FMT_MAX - offset, ")");
156 
157  // Add tag (if any), severity, and actual message
158  offset += snprintf(fmt + offset, FMT_MAX - offset, " %%g\t%%p: %%b");
159 
160  CRM_LOG_ASSERT(offset > 0);
161  qb_log_format_set(method, fmt);
162  }
163 }
164 
165 gboolean
166 crm_add_logfile(const char *filename)
167 {
168  bool is_default = false;
169  static int default_fd = -1;
170  static gboolean have_logfile = FALSE;
171  const char *default_logfile = CRM_LOG_DIR "/pacemaker.log";
172 
173  struct stat parent;
174  int fd = 0, rc = 0;
175  FILE *logfile = NULL;
176  char *parent_dir = NULL;
177  char *filename_cp;
178 
179  if (filename == NULL && have_logfile == FALSE) {
180  filename = default_logfile;
181  }
182 
183  if (filename == NULL) {
184  return FALSE; /* Nothing to do */
185  } else if(safe_str_eq(filename, "none")) {
186  return FALSE; /* Nothing to do */
187  } else if(safe_str_eq(filename, "/dev/null")) {
188  return FALSE; /* Nothing to do */
189  } else if(safe_str_eq(filename, default_logfile)) {
190  is_default = TRUE;
191  }
192 
193  if(is_default && default_fd >= 0) {
194  return TRUE; /* Nothing to do */
195  }
196 
197  /* Check the parent directory */
198  filename_cp = strdup(filename);
199  parent_dir = dirname(filename_cp);
200  rc = stat(parent_dir, &parent);
201 
202  if (rc != 0) {
203  crm_err("Directory '%s' does not exist: logging to '%s' is disabled", parent_dir, filename);
204  free(filename_cp);
205  return FALSE;
206  }
207  free(filename_cp);
208 
209  errno = 0;
210  logfile = fopen(filename, "a");
211  if(logfile == NULL) {
212  crm_err("%s (%d): Logging to '%s' as uid=%u, gid=%u is disabled",
213  pcmk_strerror(errno), errno, filename, geteuid(), getegid());
214  return FALSE;
215  }
216 
217  /* Check/Set permissions if we're root */
218  if (geteuid() == 0) {
219  struct stat st;
220  uid_t pcmk_uid = 0;
221  gid_t pcmk_gid = 0;
222  gboolean fix = FALSE;
223  int logfd = fileno(logfile);
224 
225  rc = fstat(logfd, &st);
226  if (rc < 0) {
227  crm_perror(LOG_WARNING, "Cannot stat %s", filename);
228  fclose(logfile);
229  return FALSE;
230  }
231 
232  if (pcmk_daemon_user(&pcmk_uid, &pcmk_gid) == 0) {
233  if (st.st_gid != pcmk_gid) {
234  /* Wrong group */
235  fix = TRUE;
236  } else if ((st.st_mode & S_IRWXG) != (S_IRGRP | S_IWGRP)) {
237  /* Not read/writable by the correct group */
238  fix = TRUE;
239  }
240  }
241 
242  if (fix) {
243  rc = fchown(logfd, pcmk_uid, pcmk_gid);
244  if (rc < 0) {
245  crm_warn("Cannot change the ownership of %s to user %s and gid %d",
246  filename, CRM_DAEMON_USER, pcmk_gid);
247  }
248 
249  rc = fchmod(logfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
250  if (rc < 0) {
251  crm_warn("Cannot change the mode of %s to rw-rw----", filename);
252  }
253 
254  fprintf(logfile, "Set r/w permissions for uid=%d, gid=%d on %s\n",
255  pcmk_uid, pcmk_gid, filename);
256  if (fflush(logfile) < 0 || fsync(logfd) < 0) {
257  crm_err("Couldn't write out logfile: %s", filename);
258  }
259  }
260  }
261 
262  /* Close and reopen with libqb */
263  fclose(logfile);
264  fd = qb_log_file_open(filename);
265 
266  if (fd < 0) {
267  crm_perror(LOG_WARNING, "Couldn't send additional logging to %s", filename);
268  return FALSE;
269  }
270 
271  if(is_default) {
272  default_fd = fd;
273 
274  // Some resource agents will log only if environment variable is set
275  if (pcmk__env_option("logfile") == NULL) {
276  pcmk__set_env_option("logfile", filename);
277  }
278 
279  } else if(default_fd >= 0) {
280  crm_notice("Switching to %s", filename);
281  qb_log_ctl(default_fd, QB_LOG_CONF_ENABLED, QB_FALSE);
282  }
283 
284  crm_notice("Additional logging available in %s", filename);
285  qb_log_ctl(fd, QB_LOG_CONF_ENABLED, QB_TRUE);
286  /* qb_log_ctl(fd, QB_LOG_CONF_FILE_SYNC, 1); Turn on synchronous writes */
287 
288 #ifdef HAVE_qb_log_conf_QB_LOG_CONF_MAX_LINE_LEN
289  // Longer than default, for logging long XML lines
290  qb_log_ctl(fd, QB_LOG_CONF_MAX_LINE_LEN, 800);
291 #endif
292 
293  /* Enable callsites */
295  have_logfile = TRUE;
296 
297  return TRUE;
298 }
299 
300 static int blackbox_trigger = 0;
301 static volatile char *blackbox_file_prefix = NULL;
302 
303 #ifdef QB_FEATURE_LOG_HIRES_TIMESTAMPS
304 typedef struct timespec *log_time_t;
305 #else
306 typedef time_t log_time_t;
307 #endif
308 
309 static void
310 blackbox_logger(int32_t t, struct qb_log_callsite *cs, log_time_t timestamp,
311  const char *msg)
312 {
313  if(cs && cs->priority < LOG_ERR) {
314  crm_write_blackbox(SIGTRAP, cs); /* Bypass the over-dumping logic */
315  } else {
316  crm_write_blackbox(0, cs);
317  }
318 }
319 
320 static void
321 crm_control_blackbox(int nsig, bool enable)
322 {
323  int lpc = 0;
324 
325  if (blackbox_file_prefix == NULL) {
326  pid_t pid = getpid();
327 
328  blackbox_file_prefix = crm_strdup_printf("%s/%s-%lu",
331  (unsigned long) pid);
332  }
333 
334  if (enable && qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
335  qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 5 * 1024 * 1024); /* Any size change drops existing entries */
336  qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); /* Setting the size seems to disable it */
337 
338  /* Enable synchronous logging */
339  for (lpc = QB_LOG_BLACKBOX; lpc < QB_LOG_TARGET_MAX; lpc++) {
340  qb_log_ctl(lpc, QB_LOG_CONF_FILE_SYNC, QB_TRUE);
341  }
342 
343  crm_notice("Initiated blackbox recorder: %s", blackbox_file_prefix);
344 
345  /* Save to disk on abnormal termination */
346  crm_signal_handler(SIGSEGV, crm_trigger_blackbox);
347  crm_signal_handler(SIGABRT, crm_trigger_blackbox);
348  crm_signal_handler(SIGILL, crm_trigger_blackbox);
349  crm_signal_handler(SIGBUS, crm_trigger_blackbox);
350  crm_signal_handler(SIGFPE, crm_trigger_blackbox);
351 
353 
354  blackbox_trigger = qb_log_custom_open(blackbox_logger, NULL, NULL, NULL);
355  qb_log_ctl(blackbox_trigger, QB_LOG_CONF_ENABLED, QB_TRUE);
356  crm_trace("Trigger: %d is %d %d", blackbox_trigger,
357  qb_log_ctl(blackbox_trigger, QB_LOG_CONF_STATE_GET, 0), QB_LOG_STATE_ENABLED);
358 
360 
361  } else if (!enable && qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0) == QB_LOG_STATE_ENABLED) {
362  qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
363 
364  /* Disable synchronous logging again when the blackbox is disabled */
365  for (lpc = QB_LOG_BLACKBOX; lpc < QB_LOG_TARGET_MAX; lpc++) {
366  qb_log_ctl(lpc, QB_LOG_CONF_FILE_SYNC, QB_FALSE);
367  }
368  }
369 }
370 
371 void
373 {
374  crm_control_blackbox(nsig, TRUE);
375 }
376 
377 void
379 {
380  crm_control_blackbox(nsig, FALSE);
381 }
382 
393 void
394 crm_write_blackbox(int nsig, struct qb_log_callsite *cs)
395 {
396  static volatile int counter = 1;
397  static volatile time_t last = 0;
398 
399  char buffer[NAME_MAX];
400  time_t now = time(NULL);
401 
402  if (blackbox_file_prefix == NULL) {
403  return;
404  }
405 
406  switch (nsig) {
407  case 0:
408  case SIGTRAP:
409  /* The graceful case - such as assertion failure or user request */
410 
411  if (nsig == 0 && now == last) {
412  /* Prevent over-dumping */
413  return;
414  }
415 
416  snprintf(buffer, NAME_MAX, "%s.%d", blackbox_file_prefix, counter++);
417  if (nsig == SIGTRAP) {
418  crm_notice("Blackbox dump requested, please see %s for contents", buffer);
419 
420  } else if (cs) {
421  syslog(LOG_NOTICE,
422  "Problem detected at %s:%d (%s), please see %s for additional details",
423  cs->function, cs->lineno, cs->filename, buffer);
424  } else {
425  crm_notice("Problem detected, please see %s for additional details", buffer);
426  }
427 
428  last = now;
429  qb_log_blackbox_write_to_file(buffer);
430 
431  /* Flush the existing contents
432  * A size change would also work
433  */
434  qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
435  qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
436  break;
437 
438  default:
439  /* Do as little as possible, just try to get what we have out
440  * We logged the filename when the blackbox was enabled
441  */
442  crm_signal_handler(nsig, SIG_DFL);
443  qb_log_blackbox_write_to_file((const char *)blackbox_file_prefix);
444  qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
445  raise(nsig);
446  break;
447  }
448 }
449 
450 gboolean
451 crm_log_cli_init(const char *entity)
452 {
453  return crm_log_init(entity, LOG_ERR, FALSE, FALSE, 0, NULL, TRUE);
454 }
455 
456 static const char *
457 crm_quark_to_string(uint32_t tag)
458 {
459  const char *text = g_quark_to_string(tag);
460 
461  if (text) {
462  return text;
463  }
464  return "";
465 }
466 
467 static void
468 crm_log_filter_source(int source, const char *trace_files, const char *trace_fns,
469  const char *trace_fmts, const char *trace_tags, const char *trace_blackbox,
470  struct qb_log_callsite *cs)
471 {
472  if (qb_log_ctl(source, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
473  return;
474  } else if (cs->tags != crm_trace_nonlog && source == QB_LOG_BLACKBOX) {
475  /* Blackbox gets everything if enabled */
476  qb_bit_set(cs->targets, source);
477 
478  } else if (source == blackbox_trigger && blackbox_trigger > 0) {
479  /* Should this log message result in the blackbox being dumped */
480  if (cs->priority <= LOG_ERR) {
481  qb_bit_set(cs->targets, source);
482 
483  } else if (trace_blackbox) {
484  char *key = crm_strdup_printf("%s:%d", cs->function, cs->lineno);
485 
486  if (strstr(trace_blackbox, key) != NULL) {
487  qb_bit_set(cs->targets, source);
488  }
489  free(key);
490  }
491 
492  } else if (source == QB_LOG_SYSLOG) { /* No tracing to syslog */
493  if (cs->priority <= crm_log_priority && cs->priority <= crm_log_level) {
494  qb_bit_set(cs->targets, source);
495  }
496  /* Log file tracing options... */
497  } else if (cs->priority <= crm_log_level) {
498  qb_bit_set(cs->targets, source);
499  } else if (trace_files && strstr(trace_files, cs->filename) != NULL) {
500  qb_bit_set(cs->targets, source);
501  } else if (trace_fns && strstr(trace_fns, cs->function) != NULL) {
502  qb_bit_set(cs->targets, source);
503  } else if (trace_fmts && strstr(trace_fmts, cs->format) != NULL) {
504  qb_bit_set(cs->targets, source);
505  } else if (trace_tags
506  && cs->tags != 0
507  && cs->tags != crm_trace_nonlog && g_quark_to_string(cs->tags) != NULL) {
508  qb_bit_set(cs->targets, source);
509  }
510 }
511 
512 static void
513 crm_log_filter(struct qb_log_callsite *cs)
514 {
515  int lpc = 0;
516  static int need_init = 1;
517  static const char *trace_fns = NULL;
518  static const char *trace_tags = NULL;
519  static const char *trace_fmts = NULL;
520  static const char *trace_files = NULL;
521  static const char *trace_blackbox = NULL;
522 
523  if (need_init) {
524  need_init = 0;
525  trace_fns = getenv("PCMK_trace_functions");
526  trace_fmts = getenv("PCMK_trace_formats");
527  trace_tags = getenv("PCMK_trace_tags");
528  trace_files = getenv("PCMK_trace_files");
529  trace_blackbox = getenv("PCMK_trace_blackbox");
530 
531  if (trace_tags != NULL) {
532  uint32_t tag;
533  char token[500];
534  const char *offset = NULL;
535  const char *next = trace_tags;
536 
537  do {
538  offset = next;
539  next = strchrnul(offset, ',');
540  snprintf(token, sizeof(token), "%.*s", (int)(next - offset), offset);
541 
542  tag = g_quark_from_string(token);
543  crm_info("Created GQuark %u from token '%s' in '%s'", tag, token, trace_tags);
544 
545  if (next[0] != 0) {
546  next++;
547  }
548 
549  } while (next != NULL && next[0] != 0);
550  }
551  }
552 
553  cs->targets = 0; /* Reset then find targets to enable */
554  for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
555  crm_log_filter_source(lpc, trace_files, trace_fns, trace_fmts, trace_tags, trace_blackbox,
556  cs);
557  }
558 }
559 
560 gboolean
561 crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
562 {
563  gboolean refilter = FALSE;
564 
565  if (cs == NULL) {
566  return FALSE;
567  }
568 
569  if (cs->priority != level) {
570  cs->priority = level;
571  refilter = TRUE;
572  }
573 
574  if (cs->tags != tags) {
575  cs->tags = tags;
576  refilter = TRUE;
577  }
578 
579  if (refilter) {
580  crm_log_filter(cs);
581  }
582 
583  if (cs->targets == 0) {
584  return FALSE;
585  }
586  return TRUE;
587 }
588 
589 void
591 {
592  static gboolean log = TRUE;
593 
594  if (log) {
595  log = FALSE;
596  crm_debug
597  ("Enabling callsites based on priority=%d, files=%s, functions=%s, formats=%s, tags=%s",
598  crm_log_level, getenv("PCMK_trace_files"), getenv("PCMK_trace_functions"),
599  getenv("PCMK_trace_formats"), getenv("PCMK_trace_tags"));
600  }
601  qb_log_filter_fn_set(crm_log_filter);
602 }
603 
604 static gboolean
605 crm_tracing_enabled(void)
606 {
607  if (crm_log_level == LOG_TRACE) {
608  return TRUE;
609  } else if (getenv("PCMK_trace_files") || getenv("PCMK_trace_functions")
610  || getenv("PCMK_trace_formats") || getenv("PCMK_trace_tags")) {
611  return TRUE;
612  }
613  return FALSE;
614 }
615 
616 static int
617 crm_priority2int(const char *name)
618 {
619  struct syslog_names {
620  const char *name;
621  int priority;
622  };
623  static struct syslog_names p_names[] = {
624  {"emerg", LOG_EMERG},
625  {"alert", LOG_ALERT},
626  {"crit", LOG_CRIT},
627  {"error", LOG_ERR},
628  {"warning", LOG_WARNING},
629  {"notice", LOG_NOTICE},
630  {"info", LOG_INFO},
631  {"debug", LOG_DEBUG},
632  {NULL, -1}
633  };
634  int lpc;
635 
636  for (lpc = 0; name != NULL && p_names[lpc].name != NULL; lpc++) {
637  if (crm_str_eq(p_names[lpc].name, name, TRUE)) {
638  return p_names[lpc].priority;
639  }
640  }
641  return crm_log_priority;
642 }
643 
644 
645 static void
646 crm_identity(const char *entity, int argc, char **argv)
647 {
648  if(crm_system_name != NULL) {
649  /* Nothing to do */
650 
651  } else if (entity) {
652  free(crm_system_name);
653  crm_system_name = strdup(entity);
654 
655  } else if (argc > 0 && argv != NULL) {
656  char *mutable = strdup(argv[0]);
657  char *modified = basename(mutable);
658 
659  if (strstr(modified, "lt-") == modified) {
660  modified += 3;
661  }
662 
663  free(crm_system_name);
664  crm_system_name = strdup(modified);
665  free(mutable);
666 
667  } else if (crm_system_name == NULL) {
668  crm_system_name = strdup("Unknown");
669  }
670 
671  setenv("PCMK_service", crm_system_name, 1);
672 }
673 
674 
675 void
676 crm_log_preinit(const char *entity, int argc, char **argv)
677 {
678  /* Configure libqb logging with nothing turned on */
679 
680  int lpc = 0;
681  int32_t qb_facility = 0;
682 
683  static bool have_logging = FALSE;
684 
685  if(have_logging == FALSE) {
686  have_logging = TRUE;
687 
688  crm_xml_init(); /* Sets buffer allocation strategy */
689 
690  if (crm_trace_nonlog == 0) {
691  crm_trace_nonlog = g_quark_from_static_string("Pacemaker non-logging tracepoint");
692  }
693 
694  umask(S_IWGRP | S_IWOTH | S_IROTH);
695 
696  /* Redirect messages from glib functions to our handler */
697  glib_log_default = g_log_set_default_handler(crm_glib_handler, NULL);
698 
699  /* and for good measure... - this enum is a bit field (!) */
700  g_log_set_always_fatal((GLogLevelFlags) 0); /*value out of range */
701 
702  /* Who do we log as */
703  crm_identity(entity, argc, argv);
704 
705  qb_facility = qb_log_facility2int("local0");
706  qb_log_init(crm_system_name, qb_facility, LOG_ERR);
707  crm_log_level = LOG_CRIT;
708 
709  /* Nuke any syslog activity until it's asked for */
710  qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
711 #ifdef HAVE_qb_log_conf_QB_LOG_CONF_MAX_LINE_LEN
712  // Shorter than default, generous for what we *should* send to syslog
713  qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_MAX_LINE_LEN, 256);
714 #endif
715 
716  /* Set format strings and disable threading
717  * Pacemaker and threads do not mix well (due to the amount of forking)
718  */
719  qb_log_tags_stringify_fn_set(crm_quark_to_string);
720  for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
721  qb_log_ctl(lpc, QB_LOG_CONF_THREADED, QB_FALSE);
722 #ifdef HAVE_qb_log_conf_QB_LOG_CONF_ELLIPSIS
723  // End truncated lines with '...'
724  qb_log_ctl(lpc, QB_LOG_CONF_ELLIPSIS, QB_TRUE);
725 #endif
726  set_format_string(lpc, crm_system_name);
727  }
728  }
729 }
730 
731 gboolean
732 crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_stderr,
733  int argc, char **argv, gboolean quiet)
734 {
735  const char *syslog_priority = NULL;
736  const char *logfile = pcmk__env_option("logfile");
737  const char *facility = pcmk__env_option("logfacility");
738  const char *f_copy = facility;
739 
741  crm_log_preinit(entity, argc, argv);
742 
743  if (level > LOG_TRACE) {
744  level = LOG_TRACE;
745  }
746  if(level > crm_log_level) {
747  crm_log_level = level;
748  }
749 
750  /* Should we log to syslog */
751  if (facility == NULL) {
752  if(crm_is_daemon) {
753  facility = "daemon";
754  } else {
755  facility = "none";
756  }
757  pcmk__set_env_option("logfacility", facility);
758  }
759 
760  if (safe_str_eq(facility, "none")) {
761  quiet = TRUE;
762 
763 
764  } else {
765  qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_FACILITY, qb_log_facility2int(facility));
766  }
767 
769  /* Override the default setting */
770  crm_log_level = LOG_DEBUG;
771  }
772 
773  /* What lower threshold do we have for sending to syslog */
774  syslog_priority = pcmk__env_option("logpriority");
775  if(syslog_priority) {
776  int priority = crm_priority2int(syslog_priority);
777  crm_log_priority = priority;
778  qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", priority);
779  } else {
780  qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_NOTICE);
781  }
782 
783  // Log to syslog unless requested to be quiet
784  if (!quiet) {
785  qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE);
786  }
787 
788  /* Should we log to stderr */
789  if (pcmk__env_option_enabled(crm_system_name, "stderr")) {
790  /* Override the default setting */
791  to_stderr = TRUE;
792  }
793  crm_enable_stderr(to_stderr);
794 
795  /* Should we log to a file */
796  if (safe_str_eq("none", logfile)) {
797  /* No soup^Hlogs for you! */
798  } else if(crm_is_daemon) {
799  // Daemons always get a log file, unless explicitly set to "none"
800  crm_add_logfile(logfile);
801  } else if(logfile) {
802  crm_add_logfile(logfile);
803  }
804 
807  }
808 
809  /* Summary */
810  crm_trace("Quiet: %d, facility %s", quiet, f_copy);
811  pcmk__env_option("logfile");
812  pcmk__env_option("logfacility");
813 
815 
816  /* Ok, now we can start logging... */
817  if (quiet == FALSE && crm_is_daemon == FALSE) {
818  crm_log_args(argc, argv);
819  }
820 
821  if (crm_is_daemon) {
822  const char *user = getenv("USER");
823 
824  if (user != NULL && safe_str_neq(user, "root") && safe_str_neq(user, CRM_DAEMON_USER)) {
825  crm_trace("Not switching to corefile directory for %s", user);
826  crm_is_daemon = FALSE;
827  }
828  }
829 
830  if (crm_is_daemon) {
831  int user = getuid();
832  const char *base = CRM_CORE_DIR;
833  struct passwd *pwent = getpwuid(user);
834 
835  if (pwent == NULL) {
836  crm_perror(LOG_ERR, "Cannot get name for uid: %d", user);
837 
838  } else if (safe_str_neq(pwent->pw_name, "root")
839  && safe_str_neq(pwent->pw_name, CRM_DAEMON_USER)) {
840  crm_trace("Don't change active directory for regular user: %s", pwent->pw_name);
841 
842  } else if (chdir(base) < 0) {
843  crm_perror(LOG_INFO, "Cannot change active directory to %s", base);
844 
845  } else {
846  crm_info("Changed active directory to %s", base);
847 #if 0
848  {
849  char path[512];
850 
851  snprintf(path, 512, "%s-%lu", crm_system_name, (unsigned long) getpid());
852  mkdir(path, 0750);
853  chdir(path);
854  crm_info("Changed active directory to %s/%s/%s", base, pwent->pw_name, path);
855  }
856 #endif
857  }
858 
859  /* Original meanings from signal(7)
860  *
861  * Signal Value Action Comment
862  * SIGTRAP 5 Core Trace/breakpoint trap
863  * SIGUSR1 30,10,16 Term User-defined signal 1
864  * SIGUSR2 31,12,17 Term User-defined signal 2
865  *
866  * Our usage is as similar as possible
867  */
870  mainloop_add_signal(SIGTRAP, crm_trigger_blackbox);
871  }
872 
873  return TRUE;
874 }
875 
876 /* returns the old value */
877 unsigned int
878 set_crm_log_level(unsigned int level)
879 {
880  unsigned int old = crm_log_level;
881 
882  if (level > LOG_TRACE) {
883  level = LOG_TRACE;
884  }
885  crm_log_level = level;
887  crm_trace("New log level: %d", level);
888  return old;
889 }
890 
891 void
892 crm_enable_stderr(int enable)
893 {
894  if (enable && qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
895  qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
897 
898  } else if (enable == FALSE) {
899  qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_FALSE);
900  }
901 }
902 
903 void
904 crm_bump_log_level(int argc, char **argv)
905 {
906  static int args = TRUE;
907  int level = crm_log_level;
908 
909  if (args && argc > 1) {
910  crm_log_args(argc, argv);
911  }
912 
913  if (qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0) == QB_LOG_STATE_ENABLED) {
914  set_crm_log_level(level + 1);
915  }
916 
917  /* Enable after potentially logging the argstring, not before */
918  crm_enable_stderr(TRUE);
919 }
920 
921 unsigned int
923 {
924  return crm_log_level;
925 }
926 
927 #define ARGS_FMT "Invoked: %s"
928 void
929 crm_log_args(int argc, char **argv)
930 {
931  int lpc = 0;
932  int len = 0;
933  int existing_len = 0;
934  int line = __LINE__;
935  static int logged = 0;
936 
937  char *arg_string = NULL;
938 
939  if (argc == 0 || argv == NULL || logged) {
940  return;
941  }
942 
943  logged = 1;
944 
945  // cppcheck seems not to understand the abort logic in realloc_safe
946  // cppcheck-suppress memleak
947  for (; lpc < argc; lpc++) {
948  if (argv[lpc] == NULL) {
949  break;
950  }
951 
952  len = 2 + strlen(argv[lpc]); /* +1 space, +1 EOS */
953  arg_string = realloc_safe(arg_string, len + existing_len);
954  existing_len += sprintf(arg_string + existing_len, "%s ", argv[lpc]);
955  }
956 
957  qb_log_from_external_source(__func__, __FILE__, ARGS_FMT, LOG_NOTICE, line, 0, arg_string);
958 
959  free(arg_string);
960 }
961 
962 void
963 crm_log_output_fn(const char *file, const char *function, int line, int level, const char *prefix,
964  const char *output)
965 {
966  const char *next = NULL;
967  const char *offset = NULL;
968 
969  if (level == LOG_NEVER) {
970  return;
971  }
972 
973  if (output == NULL) {
974  if (level != LOG_STDOUT) {
975  level = LOG_TRACE;
976  }
977  output = "-- empty --";
978  }
979 
980  next = output;
981  do {
982  offset = next;
983  next = strchrnul(offset, '\n');
984  do_crm_log_alias(level, file, function, line, "%s [ %.*s ]", prefix,
985  (int)(next - offset), offset);
986  if (next[0] != 0) {
987  next++;
988  }
989 
990  } while (next != NULL && next[0] != 0);
991 }
#define CRM_CORE_DIR
Definition: config.h:23
#define LOG_TRACE
Definition: logging.h:36
char uname[MAX_NAME]
Definition: internal.h:85
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition: logging.c:394
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:365
const char * pcmk_strerror(int rc)
Definition: results.c:55
void crm_enable_stderr(int enable)
Definition: logging.c:892
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:263
gboolean mainloop_add_signal(int sig, void(*dispatch)(int sig))
Definition: mainloop.c:328
#define ARGS_FMT
Definition: logging.c:927
gboolean crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_stderr, int argc, char **argv, gboolean quiet)
Definition: logging.c:732
GLogFunc glib_log_default
Definition: logging.c:42
void crm_xml_init(void)
Definition: xml.c:4440
char * crm_system_name
Definition: utils.c:52
void pcmk__set_env_option(const char *option, const char *value)
Set or unset a Pacemaker environment variable option.
Definition: options.c:317
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:219
#define do_crm_log_alias(level, file, function, line, fmt, args...)
Log a message as if it came from a different code location.
Definition: logging.h:286
uint32_t pid
Definition: internal.h:81
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition: utils.c:171
unsigned int crm_trace_nonlog
Definition: logging.c:39
#define LOG_NEVER
Definition: logging.h:46
const char * pcmk__env_option(const char *option)
Definition: options.c:284
Wrappers for and extensions to glib mainloop.
gboolean crm_log_cli_init(const char *entity)
Definition: logging.c:451
#define FMT_MAX
Definition: logging.c:119
#define CRM_LOG_DIR
Definition: config.h:38
void crm_bump_log_level(int argc, char **argv)
Definition: logging.c:904
void crm_log_deinit(void)
Definition: logging.c:114
#define crm_warn(fmt, args...)
Definition: logging.h:364
int daemon(int nochdir, int noclose)
int rc
Definition: pcmk_fence.c:34
#define crm_debug(fmt, args...)
Definition: logging.h:368
void crm_log_output_fn(const char *file, const char *function, int line, int level, const char *prefix, const char *output)
Definition: logging.c:963
time_t log_time_t
Definition: logging.c:306
#define crm_trace(fmt, args...)
Definition: logging.h:369
#define do_crm_log(level, fmt, args...)
Log a message.
Definition: logging.h:150
int setenv(const char *name, const char *value, int why)
#define CRM_BLACKBOX_DIR
Definition: config.h:11
void crm_enable_blackbox(int nsig)
Definition: logging.c:372
sighandler_t crm_signal_handler(int sig, sighandler_t dispatch)
Definition: mainloop.c:280
#define CRM_DAEMON_USER
Definition: config.h:32
void crm_log_args(int argc, char **argv)
Definition: logging.c:929
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:326
unsigned int set_crm_log_level(unsigned int level)
Definition: logging.c:878
void crm_log_preinit(const char *entity, int argc, char **argv)
Definition: logging.c:676
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
Definition: logging.c:561
unsigned int get_crm_log_level(void)
Definition: logging.c:922
unsigned int crm_log_level
Definition: logging.c:37
void crm_disable_blackbox(int nsig)
Definition: logging.c:378
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:314
#define crm_err(fmt, args...)
Definition: logging.h:363
#define NAME_MAX
Definition: logging.c:92
char * strchrnul(const char *s, int c_in)
bool crm_is_daemon
Definition: logging.c:40
unsigned int crm_log_priority
Definition: logging.c:36
#define safe_str_eq(a, b)
Definition: util.h:65
gboolean crm_add_logfile(const char *filename)
Definition: logging.c:166
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition: utils.c:341
char * name
Definition: pcmk_fence.c:30
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
void crm_update_callsites(void)
Definition: logging.c:590
#define LOG_STDOUT
Definition: logging.h:41
#define crm_info(fmt, args...)
Definition: logging.h:366
uint64_t flags
Definition: remote.c:149
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition: options.c:354