pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
logging.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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// Use high-resolution (millisecond) timestamps if libqb supports them
37#ifdef QB_FEATURE_LOG_HIRES_TIMESTAMPS
38#define TIMESTAMP_FORMAT_SPEC "%%T"
39typedef struct timespec *log_time_t;
40#else
41#define TIMESTAMP_FORMAT_SPEC "%%t"
42typedef time_t log_time_t;
43#endif
44
45unsigned int crm_log_level = LOG_INFO;
46unsigned int crm_trace_nonlog = 0;
47bool pcmk__is_daemon = false;
48
49static unsigned int crm_log_priority = LOG_NOTICE;
50static GLogFunc glib_log_default = NULL;
51
52static gboolean crm_tracing_enabled(void);
53
54static void
55crm_glib_handler(const gchar * log_domain, GLogLevelFlags flags, const gchar * message,
56 gpointer user_data)
57{
58 int log_level = LOG_WARNING;
59 GLogLevelFlags msg_level = (flags & G_LOG_LEVEL_MASK);
60 static struct qb_log_callsite *glib_cs = NULL;
61
62 if (glib_cs == NULL) {
63 glib_cs = qb_log_callsite_get(__func__, __FILE__, "glib-handler",
64 LOG_DEBUG, __LINE__, crm_trace_nonlog);
65 }
66
67
68 switch (msg_level) {
69 case G_LOG_LEVEL_CRITICAL:
70 log_level = LOG_CRIT;
71
72 if (crm_is_callsite_active(glib_cs, LOG_DEBUG, 0) == FALSE) {
73 /* log and record how we got here */
74 crm_abort(__FILE__, __func__, __LINE__, message, TRUE, TRUE);
75 }
76 break;
77
78 case G_LOG_LEVEL_ERROR:
79 log_level = LOG_ERR;
80 break;
81 case G_LOG_LEVEL_MESSAGE:
82 log_level = LOG_NOTICE;
83 break;
84 case G_LOG_LEVEL_INFO:
85 log_level = LOG_INFO;
86 break;
87 case G_LOG_LEVEL_DEBUG:
88 log_level = LOG_DEBUG;
89 break;
90
91 case G_LOG_LEVEL_WARNING:
92 case G_LOG_FLAG_RECURSION:
93 case G_LOG_FLAG_FATAL:
94 case G_LOG_LEVEL_MASK:
95 log_level = LOG_WARNING;
96 break;
97 }
98
99 do_crm_log(log_level, "%s: %s", log_domain, message);
100}
101
102#ifndef NAME_MAX
103# define NAME_MAX 256
104#endif
105
114static void
115crm_trigger_blackbox(int nsig)
116{
117 if(nsig == SIGTRAP) {
118 /* Turn it on if it wasn't already */
120 }
121 crm_write_blackbox(nsig, NULL);
122}
123
124void
126{
127 if (glib_log_default != NULL) {
128 g_log_set_default_handler(glib_log_default, NULL);
129 }
130}
131
132#define FMT_MAX 256
133
134static void
135set_format_string(int method, const char *daemon)
136{
137 if (method == QB_LOG_SYSLOG) {
138 // The system log gets a simplified, user-friendly format
139 crm_extended_logging(method, QB_FALSE);
140 qb_log_format_set(method, "%g %p: %b");
141
142 } else {
143 // Everything else gets more detail, for advanced troubleshooting
144
145 int offset = 0;
146 char fmt[FMT_MAX];
147
148 if (method > QB_LOG_STDERR) {
149 struct utsname res;
150 const char *nodename = "localhost";
151
152 if (uname(&res) == 0) {
153 nodename = res.nodename;
154 }
155
156 // If logging to file, prefix with timestamp, node name, daemon ID
157 offset += snprintf(fmt + offset, FMT_MAX - offset,
158 TIMESTAMP_FORMAT_SPEC " %s %-20s[%lu] ",
159 nodename, daemon, (unsigned long) getpid());
160 }
161
162 // Add function name (in parentheses)
163 offset += snprintf(fmt + offset, FMT_MAX - offset, "(%%n");
164 if (crm_tracing_enabled()) {
165 // When tracing, add file and line number
166 offset += snprintf(fmt + offset, FMT_MAX - offset, "@%%f:%%l");
167 }
168 offset += snprintf(fmt + offset, FMT_MAX - offset, ")");
169
170 // Add tag (if any), severity, and actual message
171 offset += snprintf(fmt + offset, FMT_MAX - offset, " %%g\t%%p: %%b");
172
173 CRM_LOG_ASSERT(offset > 0);
174 qb_log_format_set(method, fmt);
175 }
176}
177
178#define DEFAULT_LOG_FILE CRM_LOG_DIR "/pacemaker.log"
179
180static bool
181logfile_disabled(const char *filename)
182{
183 return pcmk__str_eq(filename, "none", pcmk__str_casei)
184 || pcmk__str_eq(filename, "/dev/null", pcmk__str_none);
185}
186
196static int
197chown_logfile(const char *filename, int logfd)
198{
199 uid_t pcmk_uid = 0;
200 gid_t pcmk_gid = 0;
201 struct stat st;
202 int rc;
203
204 // Get the log file's current ownership and permissions
205 if (fstat(logfd, &st) < 0) {
206 return errno;
207 }
208
209 // Any other errors don't prevent file from being used as log
210
211 rc = pcmk_daemon_user(&pcmk_uid, &pcmk_gid);
212 if (rc != pcmk_ok) {
214 crm_warn("Not changing '%s' ownership because user information "
215 "unavailable: %s", filename, pcmk_rc_str(rc));
216 return pcmk_rc_ok;
217 }
218 if ((st.st_gid == pcmk_gid)
219 && ((st.st_mode & S_IRWXG) == (S_IRGRP|S_IWGRP))) {
220 return pcmk_rc_ok;
221 }
222 if (fchown(logfd, pcmk_uid, pcmk_gid) < 0) {
223 crm_warn("Couldn't change '%s' ownership to user %s gid %d: %s",
224 filename, CRM_DAEMON_USER, pcmk_gid, strerror(errno));
225 }
226 return pcmk_rc_ok;
227}
228
229// Reset log file permissions (using environment variable if set)
230static void
231chmod_logfile(const char *filename, int logfd)
232{
233 const char *modestr = getenv("PCMK_logfile_mode");
234 mode_t filemode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
235
236 if (modestr != NULL) {
237 long filemode_l = strtol(modestr, NULL, 8);
238
239 if ((filemode_l != LONG_MIN) && (filemode_l != LONG_MAX)) {
240 filemode = (mode_t) filemode_l;
241 }
242 }
243 if ((filemode != 0) && (fchmod(logfd, filemode) < 0)) {
244 crm_warn("Couldn't change '%s' mode to %04o: %s",
245 filename, filemode, strerror(errno));
246 }
247}
248
249// If we're root, correct a log file's permissions if needed
250static int
251set_logfile_permissions(const char *filename, FILE *logfile)
252{
253 if (geteuid() == 0) {
254 int logfd = fileno(logfile);
255 int rc = chown_logfile(filename, logfd);
256
257 if (rc != pcmk_rc_ok) {
258 return rc;
259 }
260 chmod_logfile(filename, logfd);
261 }
262 return pcmk_rc_ok;
263}
264
265// Enable libqb logging to a new log file
266static void
267enable_logfile(int fd)
268{
269 qb_log_ctl(fd, QB_LOG_CONF_ENABLED, QB_TRUE);
270#if 0
271 qb_log_ctl(fd, QB_LOG_CONF_FILE_SYNC, 1); // Turn on synchronous writes
272#endif
273
274#ifdef HAVE_qb_log_conf_QB_LOG_CONF_MAX_LINE_LEN
275 // Longer than default, for logging long XML lines
276 qb_log_ctl(fd, QB_LOG_CONF_MAX_LINE_LEN, 800);
277#endif
278
280}
281
282static inline void
283disable_logfile(int fd)
284{
285 qb_log_ctl(fd, QB_LOG_CONF_ENABLED, QB_FALSE);
286}
287
288static void
289setenv_logfile(const char *filename)
290{
291 // Some resource agents will log only if environment variable is set
292 if (pcmk__env_option("logfile") == NULL) {
293 pcmk__set_env_option("logfile", filename);
294 }
295}
296
304int
305pcmk__add_logfile(const char *filename)
306{
307 /* No log messages from this function will be logged to the new log!
308 * If another target such as syslog has already been added, the messages
309 * should show up there.
310 */
311
312 int fd = 0;
313 int rc = pcmk_rc_ok;
314 FILE *logfile = NULL;
315 bool is_default = false;
316
317 static int default_fd = -1;
318 static bool have_logfile = false;
319
320 // Use default if caller didn't specify (and we don't already have one)
321 if (filename == NULL) {
322 if (have_logfile) {
323 return pcmk_rc_ok;
324 }
325 filename = DEFAULT_LOG_FILE;
326 }
327
328 // If the user doesn't want logging, we're done
329 if (logfile_disabled(filename)) {
330 return pcmk_rc_ok;
331 }
332
333 // If the caller wants the default and we already have it, we're done
334 is_default = pcmk__str_eq(filename, DEFAULT_LOG_FILE, pcmk__str_none);
335 if (is_default && (default_fd >= 0)) {
336 return pcmk_rc_ok;
337 }
338
339 // Check whether we have write access to the file
340 logfile = fopen(filename, "a");
341 if (logfile == NULL) {
342 rc = errno;
343 crm_warn("Logging to '%s' is disabled: %s " CRM_XS " uid=%u gid=%u",
344 filename, strerror(rc), geteuid(), getegid());
345 return rc;
346 }
347
348 rc = set_logfile_permissions(filename, logfile);
349 if (rc != pcmk_rc_ok) {
350 crm_warn("Logging to '%s' is disabled: %s " CRM_XS " permissions",
351 filename, strerror(rc));
352 fclose(logfile);
353 return rc;
354 }
355
356 // Close and reopen as libqb logging target
357 fclose(logfile);
358 fd = qb_log_file_open(filename);
359 if (fd < 0) {
360 crm_warn("Logging to '%s' is disabled: %s " CRM_XS " qb_log_file_open",
361 filename, strerror(-fd));
362 return -fd; // == +errno
363 }
364
365 if (is_default) {
366 default_fd = fd;
367 setenv_logfile(filename);
368
369 } else if (default_fd >= 0) {
370 crm_notice("Switching logging to %s", filename);
371 disable_logfile(default_fd);
372 }
373
374 crm_notice("Additional logging available in %s", filename);
375 enable_logfile(fd);
376 have_logfile = true;
377 return pcmk_rc_ok;
378}
379
380static int blackbox_trigger = 0;
381static volatile char *blackbox_file_prefix = NULL;
382
383static void
384blackbox_logger(int32_t t, struct qb_log_callsite *cs, log_time_t timestamp,
385 const char *msg)
386{
387 if(cs && cs->priority < LOG_ERR) {
388 crm_write_blackbox(SIGTRAP, cs); /* Bypass the over-dumping logic */
389 } else {
390 crm_write_blackbox(0, cs);
391 }
392}
393
394static void
395crm_control_blackbox(int nsig, bool enable)
396{
397 int lpc = 0;
398
399 if (blackbox_file_prefix == NULL) {
400 pid_t pid = getpid();
401
402 blackbox_file_prefix = crm_strdup_printf("%s/%s-%lu",
405 (unsigned long) pid);
406 }
407
408 if (enable && qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
409 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 5 * 1024 * 1024); /* Any size change drops existing entries */
410 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); /* Setting the size seems to disable it */
411
412 /* Enable synchronous logging */
413 for (lpc = QB_LOG_BLACKBOX; lpc < QB_LOG_TARGET_MAX; lpc++) {
414 qb_log_ctl(lpc, QB_LOG_CONF_FILE_SYNC, QB_TRUE);
415 }
416
417 crm_notice("Initiated blackbox recorder: %s", blackbox_file_prefix);
418
419 /* Save to disk on abnormal termination */
420 crm_signal_handler(SIGSEGV, crm_trigger_blackbox);
421 crm_signal_handler(SIGABRT, crm_trigger_blackbox);
422 crm_signal_handler(SIGILL, crm_trigger_blackbox);
423 crm_signal_handler(SIGBUS, crm_trigger_blackbox);
424 crm_signal_handler(SIGFPE, crm_trigger_blackbox);
425
427
428 blackbox_trigger = qb_log_custom_open(blackbox_logger, NULL, NULL, NULL);
429 qb_log_ctl(blackbox_trigger, QB_LOG_CONF_ENABLED, QB_TRUE);
430 crm_trace("Trigger: %d is %d %d", blackbox_trigger,
431 qb_log_ctl(blackbox_trigger, QB_LOG_CONF_STATE_GET, 0), QB_LOG_STATE_ENABLED);
432
434
435 } else if (!enable && qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0) == QB_LOG_STATE_ENABLED) {
436 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
437
438 /* Disable synchronous logging again when the blackbox is disabled */
439 for (lpc = QB_LOG_BLACKBOX; lpc < QB_LOG_TARGET_MAX; lpc++) {
440 qb_log_ctl(lpc, QB_LOG_CONF_FILE_SYNC, QB_FALSE);
441 }
442 }
443}
444
445void
447{
448 crm_control_blackbox(nsig, TRUE);
449}
450
451void
453{
454 crm_control_blackbox(nsig, FALSE);
455}
456
467void
468crm_write_blackbox(int nsig, struct qb_log_callsite *cs)
469{
470 static volatile int counter = 1;
471 static volatile time_t last = 0;
472
473 char buffer[NAME_MAX];
474 time_t now = time(NULL);
475
476 if (blackbox_file_prefix == NULL) {
477 return;
478 }
479
480 switch (nsig) {
481 case 0:
482 case SIGTRAP:
483 /* The graceful case - such as assertion failure or user request */
484
485 if (nsig == 0 && now == last) {
486 /* Prevent over-dumping */
487 return;
488 }
489
490 snprintf(buffer, NAME_MAX, "%s.%d", blackbox_file_prefix, counter++);
491 if (nsig == SIGTRAP) {
492 crm_notice("Blackbox dump requested, please see %s for contents", buffer);
493
494 } else if (cs) {
495 syslog(LOG_NOTICE,
496 "Problem detected at %s:%d (%s), please see %s for additional details",
497 cs->function, cs->lineno, cs->filename, buffer);
498 } else {
499 crm_notice("Problem detected, please see %s for additional details", buffer);
500 }
501
502 last = now;
503 qb_log_blackbox_write_to_file(buffer);
504
505 /* Flush the existing contents
506 * A size change would also work
507 */
508 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
509 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
510 break;
511
512 default:
513 /* Do as little as possible, just try to get what we have out
514 * We logged the filename when the blackbox was enabled
515 */
516 crm_signal_handler(nsig, SIG_DFL);
517 qb_log_blackbox_write_to_file((const char *)blackbox_file_prefix);
518 qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
519 raise(nsig);
520 break;
521 }
522}
523
524static const char *
525crm_quark_to_string(uint32_t tag)
526{
527 const char *text = g_quark_to_string(tag);
528
529 if (text) {
530 return text;
531 }
532 return "";
533}
534
535static void
536crm_log_filter_source(int source, const char *trace_files, const char *trace_fns,
537 const char *trace_fmts, const char *trace_tags, const char *trace_blackbox,
538 struct qb_log_callsite *cs)
539{
540 if (qb_log_ctl(source, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
541 return;
542 } else if (cs->tags != crm_trace_nonlog && source == QB_LOG_BLACKBOX) {
543 /* Blackbox gets everything if enabled */
544 qb_bit_set(cs->targets, source);
545
546 } else if (source == blackbox_trigger && blackbox_trigger > 0) {
547 /* Should this log message result in the blackbox being dumped */
548 if (cs->priority <= LOG_ERR) {
549 qb_bit_set(cs->targets, source);
550
551 } else if (trace_blackbox) {
552 char *key = crm_strdup_printf("%s:%d", cs->function, cs->lineno);
553
554 if (strstr(trace_blackbox, key) != NULL) {
555 qb_bit_set(cs->targets, source);
556 }
557 free(key);
558 }
559
560 } else if (source == QB_LOG_SYSLOG) { /* No tracing to syslog */
561 if (cs->priority <= crm_log_priority && cs->priority <= crm_log_level) {
562 qb_bit_set(cs->targets, source);
563 }
564 /* Log file tracing options... */
565 } else if (cs->priority <= crm_log_level) {
566 qb_bit_set(cs->targets, source);
567 } else if (trace_files && strstr(trace_files, cs->filename) != NULL) {
568 qb_bit_set(cs->targets, source);
569 } else if (trace_fns && strstr(trace_fns, cs->function) != NULL) {
570 qb_bit_set(cs->targets, source);
571 } else if (trace_fmts && strstr(trace_fmts, cs->format) != NULL) {
572 qb_bit_set(cs->targets, source);
573 } else if (trace_tags
574 && cs->tags != 0
575 && cs->tags != crm_trace_nonlog && g_quark_to_string(cs->tags) != NULL) {
576 qb_bit_set(cs->targets, source);
577 }
578}
579
580static void
581crm_log_filter(struct qb_log_callsite *cs)
582{
583 int lpc = 0;
584 static int need_init = 1;
585 static const char *trace_fns = NULL;
586 static const char *trace_tags = NULL;
587 static const char *trace_fmts = NULL;
588 static const char *trace_files = NULL;
589 static const char *trace_blackbox = NULL;
590
591 if (need_init) {
592 need_init = 0;
593 trace_fns = getenv("PCMK_trace_functions");
594 trace_fmts = getenv("PCMK_trace_formats");
595 trace_tags = getenv("PCMK_trace_tags");
596 trace_files = getenv("PCMK_trace_files");
597 trace_blackbox = getenv("PCMK_trace_blackbox");
598
599 if (trace_tags != NULL) {
600 uint32_t tag;
601 char token[500];
602 const char *offset = NULL;
603 const char *next = trace_tags;
604
605 do {
606 offset = next;
607 next = strchrnul(offset, ',');
608 snprintf(token, sizeof(token), "%.*s", (int)(next - offset), offset);
609
610 tag = g_quark_from_string(token);
611 crm_info("Created GQuark %u from token '%s' in '%s'", tag, token, trace_tags);
612
613 if (next[0] != 0) {
614 next++;
615 }
616
617 } while (next != NULL && next[0] != 0);
618 }
619 }
620
621 cs->targets = 0; /* Reset then find targets to enable */
622 for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
623 crm_log_filter_source(lpc, trace_files, trace_fns, trace_fmts, trace_tags, trace_blackbox,
624 cs);
625 }
626}
627
628gboolean
629crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
630{
631 gboolean refilter = FALSE;
632
633 if (cs == NULL) {
634 return FALSE;
635 }
636
637 if (cs->priority != level) {
638 cs->priority = level;
639 refilter = TRUE;
640 }
641
642 if (cs->tags != tags) {
643 cs->tags = tags;
644 refilter = TRUE;
645 }
646
647 if (refilter) {
648 crm_log_filter(cs);
649 }
650
651 if (cs->targets == 0) {
652 return FALSE;
653 }
654 return TRUE;
655}
656
657void
659{
660 static gboolean log = TRUE;
661
662 if (log) {
663 log = FALSE;
665 ("Enabling callsites based on priority=%d, files=%s, functions=%s, formats=%s, tags=%s",
666 crm_log_level, getenv("PCMK_trace_files"), getenv("PCMK_trace_functions"),
667 getenv("PCMK_trace_formats"), getenv("PCMK_trace_tags"));
668 }
669 qb_log_filter_fn_set(crm_log_filter);
670}
671
672static gboolean
673crm_tracing_enabled(void)
674{
675 if (crm_log_level == LOG_TRACE) {
676 return TRUE;
677 } else if (getenv("PCMK_trace_files") || getenv("PCMK_trace_functions")
678 || getenv("PCMK_trace_formats") || getenv("PCMK_trace_tags")) {
679 return TRUE;
680 }
681 return FALSE;
682}
683
684static int
685crm_priority2int(const char *name)
686{
687 struct syslog_names {
688 const char *name;
689 int priority;
690 };
691 static struct syslog_names p_names[] = {
692 {"emerg", LOG_EMERG},
693 {"alert", LOG_ALERT},
694 {"crit", LOG_CRIT},
695 {"error", LOG_ERR},
696 {"warning", LOG_WARNING},
697 {"notice", LOG_NOTICE},
698 {"info", LOG_INFO},
699 {"debug", LOG_DEBUG},
700 {NULL, -1}
701 };
702 int lpc;
703
704 for (lpc = 0; name != NULL && p_names[lpc].name != NULL; lpc++) {
705 if (pcmk__str_eq(p_names[lpc].name, name, pcmk__str_none)) {
706 return p_names[lpc].priority;
707 }
708 }
709 return crm_log_priority;
710}
711
712
713static void
714set_identity(const char *entity, int argc, char **argv)
715{
716 if (crm_system_name != NULL) {
717 return; // Already set, don't overwrite
718 }
719
720 if (entity != NULL) {
721 crm_system_name = strdup(entity);
722
723 } else if ((argc > 0) && (argv != NULL)) {
724 char *mutable = strdup(argv[0]);
725 char *modified = basename(mutable);
726
727 if (strstr(modified, "lt-") == modified) {
728 modified += 3;
729 }
730 crm_system_name = strdup(modified);
731 free(mutable);
732
733 } else {
734 crm_system_name = strdup("Unknown");
735 }
736
738
739 setenv("PCMK_service", crm_system_name, 1);
740}
741
742void
743crm_log_preinit(const char *entity, int argc, char **argv)
744{
745 /* Configure libqb logging with nothing turned on */
746
747 int lpc = 0;
748 int32_t qb_facility = 0;
749
750 static bool have_logging = FALSE;
751
752 if(have_logging == FALSE) {
753 have_logging = TRUE;
754
755 crm_xml_init(); /* Sets buffer allocation strategy */
756
757 if (crm_trace_nonlog == 0) {
758 crm_trace_nonlog = g_quark_from_static_string("Pacemaker non-logging tracepoint");
759 }
760
761 umask(S_IWGRP | S_IWOTH | S_IROTH);
762
763 /* Redirect messages from glib functions to our handler */
764 glib_log_default = g_log_set_default_handler(crm_glib_handler, NULL);
765
766 /* and for good measure... - this enum is a bit field (!) */
767 g_log_set_always_fatal((GLogLevelFlags) 0); /*value out of range */
768
769 /* Set crm_system_name, which is used as the logging name. It may also
770 * be used for other purposes such as an IPC client name.
771 */
772 set_identity(entity, argc, argv);
773
774 qb_facility = qb_log_facility2int("local0");
775 qb_log_init(crm_system_name, qb_facility, LOG_ERR);
776 crm_log_level = LOG_CRIT;
777
778 /* Nuke any syslog activity until it's asked for */
779 qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
780#ifdef HAVE_qb_log_conf_QB_LOG_CONF_MAX_LINE_LEN
781 // Shorter than default, generous for what we *should* send to syslog
782 qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_MAX_LINE_LEN, 256);
783#endif
784
785 /* Set format strings and disable threading
786 * Pacemaker and threads do not mix well (due to the amount of forking)
787 */
788 qb_log_tags_stringify_fn_set(crm_quark_to_string);
789 for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
790 qb_log_ctl(lpc, QB_LOG_CONF_THREADED, QB_FALSE);
791#ifdef HAVE_qb_log_conf_QB_LOG_CONF_ELLIPSIS
792 // End truncated lines with '...'
793 qb_log_ctl(lpc, QB_LOG_CONF_ELLIPSIS, QB_TRUE);
794#endif
795 set_format_string(lpc, crm_system_name);
796 }
797 }
798}
799
800gboolean
801crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_stderr,
802 int argc, char **argv, gboolean quiet)
803{
804 const char *syslog_priority = NULL;
805 const char *facility = pcmk__env_option("logfacility");
806 const char *f_copy = facility;
807
809 crm_log_preinit(entity, argc, argv);
810
811 if (level > LOG_TRACE) {
812 level = LOG_TRACE;
813 }
814 if(level > crm_log_level) {
815 crm_log_level = level;
816 }
817
818 /* Should we log to syslog */
819 if (facility == NULL) {
820 if (pcmk__is_daemon) {
821 facility = "daemon";
822 } else {
823 facility = "none";
824 }
825 pcmk__set_env_option("logfacility", facility);
826 }
827
828 if (pcmk__str_eq(facility, "none", pcmk__str_casei)) {
829 quiet = TRUE;
830
831
832 } else {
833 qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_FACILITY, qb_log_facility2int(facility));
834 }
835
837 /* Override the default setting */
838 crm_log_level = LOG_DEBUG;
839 }
840
841 /* What lower threshold do we have for sending to syslog */
842 syslog_priority = pcmk__env_option("logpriority");
843 if (syslog_priority) {
844 crm_log_priority = crm_priority2int(syslog_priority);
845 }
846 qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*",
847 crm_log_priority);
848
849 // Log to syslog unless requested to be quiet
850 if (!quiet) {
851 qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE);
852 }
853
854 /* Should we log to stderr */
856 /* Override the default setting */
857 to_stderr = TRUE;
858 }
859 crm_enable_stderr(to_stderr);
860
861 // Log to a file if we're a daemon or user asked for one
862 {
863 const char *logfile = pcmk__env_option("logfile");
864
865 if (!pcmk__str_eq("none", logfile, pcmk__str_casei)
866 && (pcmk__is_daemon || (logfile != NULL))) {
867 // Daemons always get a log file, unless explicitly set to "none"
868 pcmk__add_logfile(logfile);
869 }
870 }
871
875 }
876
877 /* Summary */
878 crm_trace("Quiet: %d, facility %s", quiet, f_copy);
879 pcmk__env_option("logfile");
880 pcmk__env_option("logfacility");
881
883
884 /* Ok, now we can start logging... */
885
886 // Disable daemon request if user isn't root or Pacemaker daemon user
887 if (pcmk__is_daemon) {
888 const char *user = getenv("USER");
889
890 if (user != NULL && !pcmk__strcase_any_of(user, "root", CRM_DAEMON_USER, NULL)) {
891 crm_trace("Not switching to corefile directory for %s", user);
892 pcmk__is_daemon = false;
893 }
894 }
895
896 if (pcmk__is_daemon) {
897 int user = getuid();
898 const char *base = CRM_CORE_DIR;
899 struct passwd *pwent = getpwuid(user);
900
901 if (pwent == NULL) {
902 crm_perror(LOG_ERR, "Cannot get name for uid: %d", user);
903
904 } else if (!pcmk__strcase_any_of(pwent->pw_name, "root", CRM_DAEMON_USER, NULL)) {
905 crm_trace("Don't change active directory for regular user: %s", pwent->pw_name);
906
907 } else if (chdir(base) < 0) {
908 crm_perror(LOG_INFO, "Cannot change active directory to %s", base);
909
910 } else {
911 crm_info("Changed active directory to %s", base);
912#if 0
913 {
914 char path[512];
915
916 snprintf(path, 512, "%s-%lu", crm_system_name, (unsigned long) getpid());
917 mkdir(path, 0750);
918 chdir(path);
919 crm_info("Changed active directory to %s/%s/%s", base, pwent->pw_name, path);
920 }
921#endif
922 }
923
924 /* Original meanings from signal(7)
925 *
926 * Signal Value Action Comment
927 * SIGTRAP 5 Core Trace/breakpoint trap
928 * SIGUSR1 30,10,16 Term User-defined signal 1
929 * SIGUSR2 31,12,17 Term User-defined signal 2
930 *
931 * Our usage is as similar as possible
932 */
935 mainloop_add_signal(SIGTRAP, crm_trigger_blackbox);
936
937 } else if (!quiet) {
938 crm_log_args(argc, argv);
939 }
940
941 return TRUE;
942}
943
944/* returns the old value */
945unsigned int
946set_crm_log_level(unsigned int level)
947{
948 unsigned int old = crm_log_level;
949
950 if (level > LOG_TRACE) {
951 level = LOG_TRACE;
952 }
953 crm_log_level = level;
955 crm_trace("New log level: %d", level);
956 return old;
957}
958
959void
961{
962 if (enable && qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
963 qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
965
966 } else if (enable == FALSE) {
967 qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_FALSE);
968 }
969}
970
980void
981crm_bump_log_level(int argc, char **argv)
982{
983 if (qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0)
984 != QB_LOG_STATE_ENABLED) {
985 crm_enable_stderr(TRUE);
986 } else {
988 }
989}
990
991unsigned int
993{
994 return crm_log_level;
995}
996
1006void
1007crm_log_args(int argc, char **argv)
1008{
1009 static bool logged = false;
1010 gchar *arg_string = NULL;
1011
1012 if ((argc == 0) || (argv == NULL) || logged) {
1013 return;
1014 }
1015 logged = true;
1016 arg_string = g_strjoinv(" ", argv);
1017 crm_notice("Invoked: %s", arg_string);
1018 g_free(arg_string);
1019}
1020
1021void
1022crm_log_output_fn(const char *file, const char *function, int line, int level, const char *prefix,
1023 const char *output)
1024{
1025 const char *next = NULL;
1026 const char *offset = NULL;
1027
1028 if (level == LOG_NEVER) {
1029 return;
1030 }
1031
1032 if (output == NULL) {
1033 if (level != LOG_STDOUT) {
1034 level = LOG_TRACE;
1035 }
1036 output = "-- empty --";
1037 }
1038
1039 next = output;
1040 do {
1041 offset = next;
1042 next = strchrnul(offset, '\n');
1043 do_crm_log_alias(level, file, function, line, "%s [ %.*s ]", prefix,
1044 (int)(next - offset), offset);
1045 if (next[0] != 0) {
1046 next++;
1047 }
1048
1049 } while (next != NULL && next[0] != 0);
1050}
1051
1052void
1053pcmk__cli_init_logging(const char *name, unsigned int verbosity)
1054{
1055 crm_log_init(name, LOG_ERR, FALSE, FALSE, 0, NULL, TRUE);
1056
1057 for (int i = 0; i < verbosity; i++) {
1058 /* These arguments are ignored, so pass placeholders. */
1059 crm_bump_log_level(0, NULL);
1060 }
1061}
1062
1063// Deprecated functions kept only for backward API compatibility
1064
1066
1067gboolean
1068crm_log_cli_init(const char *entity)
1069{
1070 pcmk__cli_init_logging(entity, 0);
1071 return TRUE;
1072}
1073
1074gboolean
1075crm_add_logfile(const char *filename)
1076{
1077 return pcmk__add_logfile(filename) == pcmk_rc_ok;
1078}
1079
1080// End deprecated API
uint64_t flags
Definition remote.c:3
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition utils.c:169
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition utils.c:440
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define CRM_CORE_DIR
Definition config.h:23
#define CRM_BLACKBOX_DIR
Definition config.h:11
#define CRM_DAEMON_USER
Definition config.h:32
char uname[MAX_NAME]
Definition cpg.c:5
uint32_t pid
Definition cpg.c:1
A dumping ground.
char * crm_system_name
Definition utils.c:54
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
Definition logging.c:629
unsigned int get_crm_log_level(void)
Definition logging.c:992
int pcmk__add_logfile(const char *filename)
Add a file to be used as a Pacemaker detail log.
Definition logging.c:305
void crm_log_deinit(void)
Definition logging.c:125
gboolean crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_stderr, int argc, char **argv, gboolean quiet)
Definition logging.c:801
#define FMT_MAX
Definition logging.c:132
gboolean crm_add_logfile(const char *filename)
Definition logging.c:1075
#define DEFAULT_LOG_FILE
Definition logging.c:178
void crm_disable_blackbox(int nsig)
Definition logging.c:452
void crm_update_callsites(void)
Definition logging.c:658
void crm_write_blackbox(int nsig, struct qb_log_callsite *cs)
Definition logging.c:468
void crm_enable_stderr(int enable)
Definition logging.c:960
void crm_log_preinit(const char *entity, int argc, char **argv)
Definition logging.c:743
void crm_enable_blackbox(int nsig)
Definition logging.c:446
unsigned int crm_trace_nonlog
Definition logging.c:46
void crm_log_output_fn(const char *file, const char *function, int line, int level, const char *prefix, const char *output)
Definition logging.c:1022
void pcmk__cli_init_logging(const char *name, unsigned int verbosity)
Definition logging.c:1053
unsigned int set_crm_log_level(unsigned int level)
Definition logging.c:946
time_t log_time_t
Definition logging.c:42
#define TIMESTAMP_FORMAT_SPEC
Definition logging.c:41
#define NAME_MAX
Definition logging.c:103
void crm_log_args(int argc, char **argv)
Log the command line (once)
Definition logging.c:1007
bool pcmk__is_daemon
Definition logging.c:47
void crm_bump_log_level(int argc, char **argv)
Make logging more verbose.
Definition logging.c:981
unsigned int crm_log_level
Definition logging.c:45
gboolean crm_log_cli_init(const char *entity)
Definition logging.c:1068
#define crm_info(fmt, args...)
Definition logging.h:353
#define do_crm_log(level, fmt, args...)
Log a message.
Definition logging.h:159
#define crm_warn(fmt, args...)
Definition logging.h:351
#define CRM_XS
Definition logging.h:54
#define LOG_STDOUT
Definition logging.h:41
#define CRM_LOG_ASSERT(expr)
Definition logging.h:202
#define crm_notice(fmt, args...)
Definition logging.h:352
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:301
#define crm_debug(fmt, args...)
Definition logging.h:355
#define LOG_NEVER
Definition logging.h:46
#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:273
#define crm_trace(fmt, args...)
Definition logging.h:356
#define LOG_TRACE
Definition logging.h:36
Deprecated Pacemaker logging API.
Wrappers for and extensions to glib mainloop.
gboolean mainloop_add_signal(int sig, void(*dispatch)(int sig))
Definition mainloop.c:355
sighandler_t crm_signal_handler(int sig, sighandler_t dispatch)
Definition mainloop.c:307
void pcmk__set_env_option(const char *option, const char *value)
Set or unset a Pacemaker environment variable option.
Definition options.c:318
bool pcmk__env_option_enabled(const char *daemon, const char *option)
Definition options.c:355
const char * pcmk__env_option(const char *option)
Definition options.c:285
char * name
Definition pcmk_fence.c:31
stonith_t * st
Definition pcmk_fence.c:28
int rc
Definition pcmk_fence.c:35
char * strerror(int errnum)
int setenv(const char *name, const char *value, int why)
char * strchrnul(const char *s, int c_in)
int daemon(int nochdir, int noclose)
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:420
@ pcmk_rc_ok
Definition results.h:142
#define pcmk_ok
Definition results.h:67
int pcmk_legacy2rc(int legacy_rc)
Definition results.c:450
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:955
@ pcmk__str_none
@ pcmk__str_casei
void crm_xml_init(void)
Definition xml.c:2831