1 /*****************************************************************************\
2 * log.c - slurm logging facilities
3 *****************************************************************************
4 * Copyright (C) 2002-2007 The Regents of the University of California.
5 * Copyright (C) 2008-2010 Lawrence Livermore National Security.
6 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
7 * Written by Mark Grondona <mgrondona@llnl.gov>
8 * CODE-OCEC-09-009. All rights reserved.
9 *
10 * Much of this code was derived or adapted from the log.c component of
11 * openssh which contains the following notices:
12 *****************************************************************************
13 * Author: Tatu Ylonen <ylo@cs.hut.fi>
14 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
15 * All rights reserved
16 *
17 * As far as I am concerned, the code I have written for this software
18 * can be used freely for any purpose. Any derived versions of this
19 * software must be clearly marked as such, and if the derived work is
20 * incompatible with the protocol description in the RFC file, it must be
21 * called by a name other than "ssh" or "Secure Shell".
22 *****************************************************************************
23 * Copyright (c) 2000 Markus Friedl. All rights reserved.
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 * 1. Redistributions of source code must retain the above copyright
29 * notice, this list of conditions and the following disclaimer.
30 * 2. Redistributions in binary form must reproduce the above copyright
31 * notice, this list of conditions and the following disclaimer in the
32 * documentation and/or other materials provided with the distribution.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
35 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
37 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
38 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
39 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
40 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
41 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 \*****************************************************************************/
45
46 /*
47 ** MT safe
48 */
49 #define _GNU_SOURCE
50
51 #include "config.h"
52
53 #if HAVE_SYS_PRCTL_H
54 # include <sys/prctl.h>
55 #endif
56
57 #include <errno.h>
58 #include <poll.h>
59 #include <pthread.h>
60 #include <stdarg.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <sys/socket.h>
64 #include <sys/stat.h>
65 #include <sys/time.h>
66 #include <sys/types.h>
67 #include <unistd.h>
68
69 #include "slurm/slurm_errno.h"
70 #include "src/common/fd.h"
71 #include "src/common/log.h"
72 #include "src/common/macros.h"
73 #include "src/common/slurm_protocol_api.h"
74 #include "src/common/slurm_time.h"
75 #include "src/common/xmalloc.h"
76 #include "src/common/xstring.h"
77 #include "src/slurmctld/slurmctld.h"
78
79 #ifndef LINEBUFSIZE
80 # define LINEBUFSIZE 256
81 #endif
82
83 #define NAMELEN 16
84
85 #define LOG_MACRO(level, sched, fmt) { \
86 if ((level <= highest_log_level) || \
87 (sched && (level <= highest_sched_log_level))) { \
88 va_list ap; \
89 va_start(ap, fmt); \
90 _log_msg(level, sched, false, fmt, ap); \
91 va_end(ap); \
92 } \
93 }
94
95 /*
96 ** Define slurm-specific aliases for use by plugins, see slurm_xlator.h
97 ** for details.
98 */
99 strong_alias(get_log_level, slurm_get_log_level);
100 strong_alias(get_sched_log_level, slurm_get_sched_log_level);
101 strong_alias(log_init, slurm_log_init);
102 strong_alias(log_reinit, slurm_log_reinit);
103 strong_alias(log_fini, slurm_log_fini);
104 strong_alias(log_alter, slurm_log_alter);
105 strong_alias(log_alter_with_fp, slurm_log_alter_with_fp);
106 strong_alias(log_set_fpfx, slurm_log_set_fpfx);
107 strong_alias(log_fp, slurm_log_fp);
108 strong_alias(log_oom, slurm_log_oom);
109 strong_alias(log_has_data, slurm_log_has_data);
110 strong_alias(log_flush, slurm_log_flush);
111 strong_alias(log_var, slurm_log_var);
112 strong_alias(fatal, slurm_fatal) __attribute__((noreturn));
113 strong_alias(fatal_abort, slurm_fatal_abort) __attribute__((noreturn));
114 strong_alias(error, slurm_error);
115 strong_alias(spank_log, slurm_spank_log);
116 strong_alias(info, slurm_info);
117 strong_alias(verbose, slurm_verbose);
118 strong_alias(sched_error, slurm_sched_error);
119 strong_alias(sched_info, slurm_sched_info);
120 strong_alias(sched_verbose, slurm_sched_verbose);
121
122 /*
123 ** struct defining a "log" type
124 */
125 typedef struct {
126 char *argv0;
127 char *fpfx; /* optional prefix for logfile entries */
128 FILE *logfp; /* log file pointer */
129 cbuf_t *buf; /* stderr data buffer */
130 cbuf_t *fbuf; /* logfile data buffer */
131 log_facility_t facility;
132 log_options_t opt;
133 unsigned initialized:1;
134 uint16_t fmt; /* Flag for specifying timestamp format */
135 uint64_t debug_flags;
136 } log_t;
137
138 char *slurm_prog_name = NULL;
139
140 /* static variables */
141 static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
142 static log_t *log = NULL;
143 static log_t *sched_log = NULL;
144
145 static volatile log_level_t highest_log_level = LOG_LEVEL_END;
146 static volatile log_level_t highest_sched_log_level = LOG_LEVEL_QUIET;
147
148 #define LOG_INITIALIZED ((log != NULL) && (log->initialized))
149 #define SCHED_LOG_INITIALIZED ((sched_log != NULL) && (sched_log->initialized))
150 /* define a default argv0 */
151 #if HAVE_PROGRAM_INVOCATION_NAME
152 /* This used to use program_invocation_short_name, but on some systems
153 * that gets truncated at 16 bytes, too short for our needs. */
154 extern char * program_invocation_name;
155 # define default_name program_invocation_name
156 #else
157 # define default_name ""
158 #endif
159
160
161 /*
162 * pthread_atfork handlers:
163 */
_atfork_prep()164 static void _atfork_prep() { slurm_mutex_lock(&log_lock); }
_atfork_parent()165 static void _atfork_parent() { slurm_mutex_unlock(&log_lock); }
_atfork_child()166 static void _atfork_child() { slurm_mutex_unlock(&log_lock); }
167 static bool at_forked = false;
168 #define atfork_install_handlers() \
169 while (!at_forked) { \
170 pthread_atfork(_atfork_prep, _atfork_parent, _atfork_child); \
171 at_forked = true; \
172 }
173
174 static void _log_flush(log_t *log);
175
_highest_level(log_level_t a,log_level_t b,log_level_t c)176 static log_level_t _highest_level(log_level_t a, log_level_t b, log_level_t c)
177 {
178 if (a >= b) {
179 return (a >= c) ? a : c;
180 } else {
181 return (b >= c) ? b : c;
182 }
183 }
184
185 /* Write the current local time into the provided buffer. Returns the
186 * number of characters written into the buffer. */
_make_timestamp(char * timestamp_buf,size_t max,const char * timestamp_fmt)187 static size_t _make_timestamp(char *timestamp_buf, size_t max,
188 const char *timestamp_fmt)
189 {
190 time_t timestamp_t = time(NULL);
191 struct tm timestamp_tm;
192 if (!localtime_r(×tamp_t, ×tamp_tm)) {
193 fprintf(stderr, "localtime_r() failed\n");
194 return 0;
195 }
196 return strftime(timestamp_buf, max, timestamp_fmt, ×tamp_tm);
197 }
198
rfc2822_timestamp(char * s,size_t max)199 size_t rfc2822_timestamp(char *s, size_t max)
200 {
201 return _make_timestamp(s, max, "%a, %d %b %Y %H:%M:%S %z");
202 }
203
log_timestamp(char * s,size_t max)204 size_t log_timestamp(char *s, size_t max)
205 {
206 if (!log)
207 return _make_timestamp(s, max, "%Y-%m-%dT%T");
208 switch (log->fmt) {
209 case LOG_FMT_RFC5424_MS:
210 case LOG_FMT_RFC5424:
211 {
212 size_t written = _make_timestamp(s, max, "%Y-%m-%dT%T%z");
213 if (max >= 26 && written == 24) {
214 /* The strftime %z format creates timezone offsets of
215 * the form (+/-)hhmm, whereas the RFC 5424 format is
216 * (+/-)hh:mm. So shift the minutes one step back and
217 * insert the semicolon. */
218 s[25] = '\0';
219 s[24] = s[23];
220 s[23] = s[22];
221 s[22] = ':';
222 return written + 1;
223 }
224 return written;
225 }
226 case LOG_FMT_SHORT:
227 return _make_timestamp(s, max, "%b %d %T");
228 break;
229 default:
230 return _make_timestamp(s, max, "%Y-%m-%dT%T");
231 break;
232 }
233 }
234
235 /* check to see if a file is writeable,
236 * RET 1 if file can be written now,
237 * 0 if can not be written to within 5 seconds
238 * -1 if file has been closed POLLHUP
239 */
_fd_writeable(int fd)240 static int _fd_writeable(int fd)
241 {
242 struct pollfd ufds;
243 struct stat stat_buf;
244 int write_timeout = 5000;
245 int rc;
246 char temp[2];
247
248 ufds.fd = fd;
249 ufds.events = POLLOUT;
250 while ((rc = poll(&ufds, 1, write_timeout)) < 0) {
251 switch (errno) {
252 case EINTR:
253 case EAGAIN:
254 continue;
255 default:
256 return -1;
257 }
258 }
259 if (rc == 0)
260 return 0;
261
262 /*
263 * Check here to make sure that if this is a socket that it really
264 * is still connected. If not then exit out and notify the sender.
265 * This is here since a write doesn't always tell you the socket is
266 * gone, but getting 0 back from a nonblocking read means just that.
267 */
268 if ((ufds.revents & POLLHUP) || fstat(fd, &stat_buf) ||
269 ((S_ISSOCK(stat_buf.st_mode) &&
270 (rc = recv(fd, &temp, 1, MSG_DONTWAIT) <= 0) &&
271 ((rc == 0) || ((errno != EAGAIN) && (errno != EWOULDBLOCK))))))
272 return -1;
273 else if ((ufds.revents & POLLNVAL)
274 || (ufds.revents & POLLERR)
275 || !(ufds.revents & POLLOUT))
276 return 0;
277 /* revents == POLLOUT */
278 return 1;
279 }
280
281 /*
282 * Initialize log with
283 * prog = program name to tag error messages with
284 * opt = log_options_t specifying max log levels for syslog, stderr, and file
285 * fac = log facility for syslog (unused if syslog level == LOG_QUIET)
286 * logfile =
287 * logfile name if logfile level > LOG_QUIET
288 */
289 static int
_log_init(char * prog,log_options_t opt,log_facility_t fac,char * logfile)290 _log_init(char *prog, log_options_t opt, log_facility_t fac, char *logfile )
291 {
292 int rc = 0;
293
294 if (!log) {
295 log = xmalloc(sizeof(log_t));
296 log->logfp = NULL;
297 log->argv0 = NULL;
298 log->buf = NULL;
299 log->fbuf = NULL;
300 log->fpfx = NULL;
301 atfork_install_handlers();
302 }
303
304 if (prog) {
305 if (log->argv0)
306 xfree(log->argv0);
307 log->argv0 = xstrdup(xbasename(prog));
308 } else if (!log->argv0) {
309 const char *short_name = strrchr(default_name, '/');
310 if (short_name)
311 short_name++;
312 else
313 short_name = default_name;
314 log->argv0 = xstrdup(short_name);
315 }
316
317 /* Only take the first one here. In some situations it can change. */
318 if (!slurm_prog_name && log->argv0 && (strlen(log->argv0) > 0))
319 slurm_prog_name = xstrdup(log->argv0);
320
321 if (!log->fpfx)
322 log->fpfx = xstrdup("");
323
324 log->opt = opt;
325
326 if (log->buf) {
327 cbuf_destroy(log->buf);
328 log->buf = NULL;
329 }
330 if (log->fbuf) {
331 cbuf_destroy(log->fbuf);
332 log->fbuf = NULL;
333 }
334
335 if (log->opt.buffered) {
336 log->buf = cbuf_create(128, 8192);
337 log->fbuf = cbuf_create(128, 8192);
338 }
339
340 if (log->opt.syslog_level > LOG_LEVEL_QUIET)
341 log->facility = fac;
342
343 if (logfile && (log->opt.logfile_level > LOG_LEVEL_QUIET)) {
344 int mode = O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC;
345 int fd;
346 FILE *fp;
347
348 fd = open(logfile, mode, S_IRUSR | S_IWUSR);
349 if (fd >= 0)
350 fp = fdopen(fd, "a");
351
352 if ((fd < 0) || !fp) {
353 char *errmsg = slurm_strerror(errno);
354 fprintf(stderr,
355 "%s: %s: Unable to open logfile `%s': %s\n",
356 prog, __func__, logfile, errmsg);
357 if (fd >= 0)
358 close(fd);
359 rc = errno;
360 goto out;
361 }
362
363 if (log->logfp)
364 fclose(log->logfp); /* Ignore errors */
365
366 log->logfp = fp;
367 }
368
369 if (log->logfp && (fileno(log->logfp) < 0))
370 log->logfp = NULL;
371
372 highest_log_level = _highest_level(log->opt.syslog_level,
373 log->opt.logfile_level,
374 log->opt.stderr_level);
375
376 log->initialized = 1;
377 out:
378 return rc;
379 }
380
381 /*
382 * Initialize scheduler log with
383 * prog = program name to tag error messages with
384 * opt = log_options_t specifying max log levels for syslog, stderr, and file
385 * fac = log facility for syslog (unused if syslog level == LOG_QUIET)
386 * logfile = logfile name if logfile level > LOG_QUIET
387 */
388 static int
_sched_log_init(char * prog,log_options_t opt,log_facility_t fac,char * logfile)389 _sched_log_init(char *prog, log_options_t opt, log_facility_t fac,
390 char *logfile)
391 {
392 int rc = 0;
393
394 if (!sched_log) {
395 sched_log = xmalloc(sizeof(log_t));
396 atfork_install_handlers();
397 }
398
399 if (prog) {
400 xfree(sched_log->argv0);
401 sched_log->argv0 = xstrdup(xbasename(prog));
402 } else if (!sched_log->argv0) {
403 const char *short_name;
404 short_name = strrchr((const char *) default_name, '/');
405 if (short_name)
406 short_name++;
407 else
408 short_name = default_name;
409 sched_log->argv0 = xstrdup(short_name);
410 }
411
412 if (!sched_log->fpfx)
413 sched_log->fpfx = xstrdup("");
414
415 sched_log->opt = opt;
416
417 if (sched_log->buf) {
418 cbuf_destroy(sched_log->buf);
419 sched_log->buf = NULL;
420 }
421 if (sched_log->fbuf) {
422 cbuf_destroy(sched_log->fbuf);
423 sched_log->fbuf = NULL;
424 }
425
426 if (sched_log->opt.buffered) {
427 sched_log->buf = cbuf_create(128, 8192);
428 sched_log->fbuf = cbuf_create(128, 8192);
429 }
430
431 if (sched_log->opt.syslog_level > LOG_LEVEL_QUIET)
432 sched_log->facility = fac;
433
434 if (logfile) {
435 int mode = O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC;
436 int fd;
437 FILE *fp;
438
439 fd = open(logfile, mode, S_IRUSR | S_IWUSR);
440 if (fd >= 0)
441 fp = fdopen(fd, "a");
442
443 if ((fd < 0) || !fp) {
444 char *errmsg = slurm_strerror(errno);
445 fprintf(stderr,
446 "%s: %s: Unable to open logfile `%s': %s\n",
447 prog, __func__, logfile, errmsg);
448 if (fd >= 0)
449 close(fd);
450 rc = errno;
451 goto out;
452 }
453
454 if (sched_log->logfp)
455 fclose(sched_log->logfp); /* Ignore errors */
456
457 sched_log->logfp = fp;
458 }
459
460 if (sched_log->logfp && (fileno(sched_log->logfp) < 0))
461 sched_log->logfp = NULL;
462
463 highest_sched_log_level = _highest_level(sched_log->opt.syslog_level,
464 sched_log->opt.logfile_level,
465 sched_log->opt.stderr_level);
466
467 /*
468 * The sched_log_level is (ab)used as a boolean. Force it to the end
469 * if set so that the LOG_MACRO checks at least stay relatively clean,
470 * and it's easier for us to introduce the idea of this log level
471 * varying in the future.
472 */
473 if (highest_sched_log_level > LOG_LEVEL_QUIET)
474 highest_sched_log_level = LOG_LEVEL_END;
475
476 sched_log->initialized = 1;
477 out:
478 return rc;
479 }
480
481 /* initialize log mutex, then initialize log data structures
482 */
log_init(char * prog,log_options_t opt,log_facility_t fac,char * logfile)483 int log_init(char *prog, log_options_t opt, log_facility_t fac, char *logfile)
484 {
485 int rc = 0;
486
487 slurm_mutex_lock(&log_lock);
488 rc = _log_init(prog, opt, fac, logfile);
489 slurm_mutex_unlock(&log_lock);
490 return rc;
491 }
492
493 /* initialize log mutex, then initialize scheduler log data structures
494 */
sched_log_init(char * prog,log_options_t opt,log_facility_t fac,char * logfile)495 int sched_log_init(char *prog, log_options_t opt, log_facility_t fac, char *logfile)
496 {
497 int rc = 0;
498
499 slurm_mutex_lock(&log_lock);
500 rc = _sched_log_init(prog, opt, fac, logfile);
501 slurm_mutex_unlock(&log_lock);
502 if (rc)
503 fatal("sched_log_alter could not open %s: %m", logfile);
504 return rc;
505 }
506
log_fini(void)507 void log_fini(void)
508 {
509 if (!log)
510 return;
511
512 slurm_mutex_lock(&log_lock);
513 _log_flush(log);
514 xfree(log->argv0);
515 xfree(log->fpfx);
516 if (log->buf)
517 cbuf_destroy(log->buf);
518 if (log->fbuf)
519 cbuf_destroy(log->fbuf);
520 if (log->logfp)
521 fclose(log->logfp);
522 xfree(log);
523 xfree(slurm_prog_name);
524 slurm_mutex_unlock(&log_lock);
525 }
526
sched_log_fini(void)527 void sched_log_fini(void)
528 {
529 if (!sched_log)
530 return;
531
532 slurm_mutex_lock(&log_lock);
533 _log_flush(sched_log);
534 xfree(sched_log->argv0);
535 xfree(sched_log->fpfx);
536 if (sched_log->buf)
537 cbuf_destroy(sched_log->buf);
538 if (sched_log->fbuf)
539 cbuf_destroy(sched_log->fbuf);
540 if (sched_log->logfp)
541 fclose(sched_log->logfp);
542 xfree(sched_log);
543 slurm_mutex_unlock(&log_lock);
544 }
545
log_reinit(void)546 void log_reinit(void)
547 {
548 slurm_mutex_init(&log_lock);
549 }
550
log_set_fpfx(char ** prefix)551 void log_set_fpfx(char **prefix)
552 {
553 slurm_mutex_lock(&log_lock);
554 xfree(log->fpfx);
555 if (!prefix || !*prefix)
556 log->fpfx = xstrdup("");
557 else {
558 log->fpfx = *prefix;
559 *prefix = NULL;
560 }
561 slurm_mutex_unlock(&log_lock);
562 }
563
log_set_argv0(char * argv0)564 void log_set_argv0(char *argv0)
565 {
566 slurm_mutex_lock(&log_lock);
567 if (log->argv0)
568 xfree(log->argv0);
569 if (!argv0)
570 log->argv0 = xstrdup("");
571 else
572 log->argv0 = xstrdup(argv0);
573 slurm_mutex_unlock(&log_lock);
574 }
575
576 /* reinitialize log data structures. Like log_init, but do not init
577 * the log mutex
578 */
log_alter(log_options_t opt,log_facility_t fac,char * logfile)579 int log_alter(log_options_t opt, log_facility_t fac, char *logfile)
580 {
581 int rc = 0;
582 slurm_mutex_lock(&log_lock);
583 rc = _log_init(NULL, opt, fac, logfile);
584 slurm_mutex_unlock(&log_lock);
585 log_set_debug_flags();
586 return rc;
587 }
588
589 /* log_set_debug_flags()
590 * Set or reset the debug flags based on the configuration
591 * file or the scontrol command.
592 */
log_set_debug_flags(void)593 void log_set_debug_flags(void)
594 {
595 uint64_t debug_flags = slurm_get_debug_flags();
596
597 slurm_mutex_lock(&log_lock);
598 log->debug_flags = debug_flags;
599 slurm_mutex_unlock(&log_lock);
600 }
601
602 /* reinitialize log data structures. Like log_init, but do not init
603 * the log mutex
604 */
log_alter_with_fp(log_options_t opt,log_facility_t fac,FILE * fp_in)605 int log_alter_with_fp(log_options_t opt, log_facility_t fac, FILE *fp_in)
606 {
607 int rc = 0;
608 slurm_mutex_lock(&log_lock);
609 rc = _log_init(NULL, opt, fac, NULL);
610 if (log->logfp)
611 fclose(log->logfp); /* Ignore errors */
612 log->logfp = fp_in;
613 if (log->logfp) {
614 int fd;
615 if ((fd = fileno(log->logfp)) < 0)
616 log->logfp = NULL;
617 /* don't close fd on out since this fd was made
618 * outside of the logger */
619 }
620 slurm_mutex_unlock(&log_lock);
621 return rc;
622 }
623
624 /* reinitialize scheduler log data structures. Like sched_log_init,
625 * but do not init the log mutex
626 */
sched_log_alter(log_options_t opt,log_facility_t fac,char * logfile)627 int sched_log_alter(log_options_t opt, log_facility_t fac, char *logfile)
628 {
629 int rc = 0;
630 slurm_mutex_lock(&log_lock);
631 rc = _sched_log_init(NULL, opt, fac, logfile);
632 slurm_mutex_unlock(&log_lock);
633 if (rc)
634 fatal("sched_log_alter could not open %s: %m", logfile);
635 return rc;
636 }
637
638 /* Return the FILE * of the current logfile (or stderr if not logging to
639 * a file, but NOT both). Also see log_oom() below. */
log_fp(void)640 FILE *log_fp(void)
641 {
642 FILE *fp;
643 slurm_mutex_lock(&log_lock);
644 if (log && log->logfp) {
645 fp = log->logfp;
646 } else
647 fp = stderr;
648 slurm_mutex_unlock(&log_lock);
649 return fp;
650 }
651
652 /* Log out of memory without message buffering */
log_oom(const char * file,int line,const char * func)653 void log_oom(const char *file, int line, const char *func)
654 {
655 if (log && log->logfp) {
656 fprintf(log->logfp, "%s:%d: %s: malloc failed\n",
657 file, line, func);
658 }
659 if (!log || log->opt.stderr_level) {
660 fprintf(stderr, "%s:%d: %s: malloc failed\n",
661 file, line, func);
662 }
663 }
664
665
666 /* Set the timestamp format flag */
log_set_timefmt(unsigned fmtflag)667 void log_set_timefmt(unsigned fmtflag)
668 {
669 if (log) {
670 slurm_mutex_lock(&log_lock);
671 log->fmt = fmtflag;
672 slurm_mutex_unlock(&log_lock);
673 } else {
674 fprintf(stderr, "%s:%d: %s Slurm log not initialized\n",
675 __FILE__, __LINE__, __func__);
676 }
677 }
678
679 /*
680 * _set_idbuf()
681 * Write in the input buffer the current time and milliseconds
682 * the process id and the current thread id.
683 */
_set_idbuf(char * idbuf,size_t size)684 static void _set_idbuf(char *idbuf, size_t size)
685 {
686 struct timeval now;
687 char time[25];
688 char thread_name[NAMELEN];
689 int max_len = 12; /* handles current longest thread name */
690
691 gettimeofday(&now, NULL);
692 #if HAVE_SYS_PRCTL_H
693 if (prctl(PR_GET_NAME, thread_name, NULL, NULL, NULL) < 0) {
694 error("failed to get thread name: %m");
695 max_len = 0;
696 thread_name[0] = '\0';
697 }
698 #else
699 /* skip printing thread name if not available */
700 max_len = 0;
701 thread_name[0] = '\0';
702 #endif
703 slurm_ctime2_r(&now.tv_sec, time);
704
705 snprintf(idbuf, size, "%.15s.%-6d %5d %-*s %p",
706 time + 4, (int) now.tv_usec, (int) getpid(), max_len,
707 thread_name, (void *) pthread_self());
708 }
709
710 /*
711 * jobid2fmt() - print a job ID as "JobId=..." including, as applicable,
712 * the job array or hetjob component information with the raw jobid in
713 * parenthesis.
714 */
_jobid2fmt(job_record_t * job_ptr,char * buf,int buf_size)715 static char *_jobid2fmt(job_record_t *job_ptr, char *buf, int buf_size)
716 {
717 /*
718 * NOTE: You will notice we put a %.0s in front of the string.
719 * This is to handle the fact that we can't remove the job_ptr
720 * argument from the va_list directly. So when we call vsnprintf()
721 * to handle the va_list this will effectively skip this argument.
722 */
723 if (job_ptr == NULL)
724 return "%.0sJobId=Invalid";
725
726 if (job_ptr->magic != JOB_MAGIC)
727 return "%.0sJobId=CORRUPT";
728
729 if (job_ptr->het_job_id) {
730 snprintf(buf, buf_size, "%%.0sJobId=%u+%u(%u)",
731 job_ptr->het_job_id, job_ptr->het_job_offset,
732 job_ptr->job_id);
733 } else if (job_ptr->array_recs && (job_ptr->array_task_id == NO_VAL)) {
734 snprintf(buf, buf_size, "%%.0sJobId=%u_*",
735 job_ptr->array_job_id);
736 } else if (job_ptr->array_task_id == NO_VAL) {
737 snprintf(buf, buf_size, "%%.0sJobId=%u", job_ptr->job_id);
738 } else {
739 snprintf(buf, buf_size, "%%.0sJobId=%u_%u(%u)",
740 job_ptr->array_job_id, job_ptr->array_task_id,
741 job_ptr->job_id);
742 }
743
744 return buf;
745 }
746
747 /*
748 * stepid2fmt() - print a step ID as " StepId=...", with Batch and Extern
749 * used as appropriate.
750 * Note that the "%.0s" trick is already included by jobid2fmt above, and
751 * should not be repeated here.
752 */
_stepid2fmt(step_record_t * step_ptr,char * buf,int buf_size)753 static char *_stepid2fmt(step_record_t *step_ptr, char *buf, int buf_size)
754 {
755 if (step_ptr == NULL)
756 return " StepId=Invalid";
757
758 if (step_ptr->magic != STEP_MAGIC)
759 return " StepId=CORRUPT";
760
761 if (step_ptr->step_id == SLURM_EXTERN_CONT) {
762 return " StepId=Extern";
763 } else if (step_ptr->step_id == SLURM_BATCH_SCRIPT) {
764 return " StepId=Batch";
765 } else if (step_ptr->step_id == SLURM_PENDING_STEP) {
766 return " StepId=TBD";
767 } else {
768 snprintf(buf, buf_size, " StepId=%u", step_ptr->step_id);
769 }
770
771 return buf;
772 }
773
774 /*
775 * return a heap allocated string formed from fmt and ap arglist
776 * returned string is allocated with xmalloc, so must free with xfree.
777 *
778 * args are like printf, with the addition of the following format chars:
779 * - %m expands to strerror(errno)
780 * - %M expand to time stamp, format is configuration dependent
781 * - %pJ expands to "JobId=XXXX" for the given job_ptr, with the appropriate
782 * format for job arrays and hetjob components.
783 * - %pS expands to "JobId=XXXX StepId=YYYY" for a given step_ptr.
784 * - %t expands to strftime("%x %X") [ locally preferred short date/time ]
785 * - %T expands to rfc2822 date time [ "dd, Mon yyyy hh:mm:ss GMT offset" ]
786 *
787 * these formats are expanded first, leaving all others to be passed to
788 * vsnprintf() to complete the expansion using the ap arglist.
789 */
vxstrfmt(const char * fmt,va_list ap)790 static char *vxstrfmt(const char *fmt, va_list ap)
791 {
792 char *intermediate_fmt = NULL;
793 char *out_string = NULL;
794 char *p;
795 bool found_other_formats = false;
796 int cnt = 0;
797
798 while (*fmt != '\0') {
799 bool is_our_format = false;
800
801 p = (char *)strchr(fmt, '%');
802 if (p == NULL) {
803 /*
804 * no more format sequences, append the rest of
805 * fmt and exit the loop:
806 */
807 xstrcat(intermediate_fmt, fmt);
808 break;
809 }
810
811 /*
812 * make sure it's one of our format specifiers, skipping
813 * any that aren't:
814 */
815 do {
816 switch (*(p + 1)) {
817 case 'm':
818 case 't':
819 case 'T':
820 case 'M':
821 is_our_format = true;
822 break;
823 case 'p':
824 switch (*(p + 2)) {
825 case 'J':
826 case 'S':
827 is_our_format = true;
828 /*
829 * Need to set found_other_formats to
830 * still consume the %.0s if not other
831 * format strings are included.
832 */
833 found_other_formats = true;
834 break;
835 default:
836 found_other_formats = true;
837 break;
838 }
839 break;
840 default:
841 found_other_formats = true;
842 break;
843 }
844 cnt++;
845 } while (!is_our_format &&
846 (p = (char *)strchr(p + 1, '%')));
847
848 if (is_our_format) {
849 char *substitute = NULL;
850 char substitute_on_stack[256];
851 int should_xfree = 1;
852
853 /*
854 * p points to the leading % of one of our formats;
855 * append anything from fmt up to p to the intermediate
856 * format string:
857 */
858 xstrncat(intermediate_fmt, fmt, p - fmt);
859 fmt = p + 1;
860
861 /*
862 * fill the substitute buffer with whatever text we want
863 * to substitute for the format sequence in question:
864 */
865 switch (*fmt) {
866 case 'p':
867 fmt++;
868 switch (*fmt) {
869 case 'J': /* "%pJ" => "JobId=..." */
870 {
871 int i;
872 void *ptr = NULL;
873 job_record_t *job_ptr;
874 va_list ap_copy;
875
876 va_copy(ap_copy, ap);
877 for (i = 0; i < cnt; i++ )
878 ptr = va_arg(ap_copy, void *);
879 job_ptr = ptr;
880 xstrcat(intermediate_fmt,
881 _jobid2fmt(
882 job_ptr,
883 substitute_on_stack,
884 sizeof(substitute_on_stack)));
885 va_end(ap_copy);
886 break;
887 }
888 /* "%pS" => "JobId=... StepId=..." */
889 case 'S':
890 {
891 int i;
892 void *ptr = NULL;
893 step_record_t *step_ptr = NULL;
894 job_record_t *job_ptr = NULL;
895 va_list ap_copy;
896
897 va_copy(ap_copy, ap);
898 for (i = 0; i < cnt; i++ )
899 ptr = va_arg(ap_copy, void *);
900 step_ptr = ptr;
901 if (step_ptr &&
902 (step_ptr->magic == STEP_MAGIC))
903 job_ptr = step_ptr->job_ptr;
904 xstrcat(intermediate_fmt,
905 _jobid2fmt(
906 job_ptr,
907 substitute_on_stack,
908 sizeof(substitute_on_stack)));
909 xstrcat(intermediate_fmt,
910 _stepid2fmt(
911 step_ptr,
912 substitute_on_stack,
913 sizeof(substitute_on_stack)));
914 va_end(ap_copy);
915 break;
916 }
917 default:
918 /* Unknown */
919 break;
920 }
921 break;
922 case 'm': /* "%m" => strerror(errno) */
923 substitute = slurm_strerror(errno);
924 should_xfree = 0;
925 break;
926 case 't': /* "%t" => locally preferred date/time*/
927 xstrftimecat(substitute,
928 "%x %X");
929 break;
930 case 'T': /* "%T" => "dd, Mon yyyy hh:mm:ss off" */
931 xstrftimecat(substitute,
932 "%a, %d %b %Y %H:%M:%S %z");
933 break;
934 case 'M':
935 if (!log) {
936 xiso8601timecat(substitute, true);
937 break;
938 }
939 switch (log->fmt) {
940 case LOG_FMT_ISO8601_MS:
941 /* "%M" => "yyyy-mm-ddThh:mm:ss.fff" */
942 xiso8601timecat(substitute, true);
943 break;
944 case LOG_FMT_ISO8601:
945 /* "%M" => "yyyy-mm-ddThh:mm:ss.fff" */
946 xiso8601timecat(substitute, false);
947 break;
948 case LOG_FMT_RFC5424_MS:
949 /* "%M" => "yyyy-mm-ddThh:mm:ss.fff(+/-)hh:mm" */
950 xrfc5424timecat(substitute, true);
951 break;
952 case LOG_FMT_RFC5424:
953 /* "%M" => "yyyy-mm-ddThh:mm:ss.fff(+/-)hh:mm" */
954 xrfc5424timecat(substitute, false);
955 break;
956 case LOG_FMT_CLOCK:
957 /* "%M" => "usec" */
958 #if defined(__DragonFly__)
959 snprintf(substitute_on_stack,
960 sizeof(substitute_on_stack),
961 "%d", clock());
962 #else
963 snprintf(substitute_on_stack,
964 sizeof(substitute_on_stack),
965 "%ld", clock());
966 #endif
967 substitute = substitute_on_stack;
968 should_xfree = 0;
969 break;
970 case LOG_FMT_SHORT:
971 /* "%M" => "Mon DD hh:mm:ss" */
972 xstrftimecat(substitute, "%b %d %T");
973 break;
974 case LOG_FMT_THREAD_ID:
975 _set_idbuf(substitute_on_stack,
976 sizeof(substitute_on_stack));
977 substitute = substitute_on_stack;
978 should_xfree = 0;
979 break;
980 }
981 break;
982 }
983 fmt++;
984
985 if (substitute) {
986 char *s = substitute;
987
988 while (*s && (p = (char *)strchr(s, '%'))) {
989 /* append up through the '%' */
990 xstrncat(intermediate_fmt, s, p - s);
991 xstrcat(intermediate_fmt, "%%");
992 s = p + 1;
993 }
994 if (*s) {
995 /* append whatever's left of the substitution: */
996 xstrcat(intermediate_fmt, s);
997 }
998
999 /* deallocate substitute if necessary: */
1000 if (should_xfree) {
1001 xfree(substitute);
1002 }
1003 }
1004 } else {
1005 /*
1006 * no more format sequences for us, append the rest of
1007 * fmt and exit the loop:
1008 */
1009 xstrcat(intermediate_fmt, fmt);
1010 break;
1011 }
1012 }
1013
1014 if (intermediate_fmt && found_other_formats) {
1015 char tmp[LINEBUFSIZE];
1016 int actual_len;
1017 va_list ap_copy;
1018
1019 va_copy(ap_copy, ap);
1020 actual_len = vsnprintf(tmp, sizeof(tmp),
1021 intermediate_fmt, ap_copy);
1022 va_end(ap_copy);
1023
1024 if (actual_len >= 0) {
1025 if (actual_len < sizeof(tmp)) {
1026 out_string = xstrdup(tmp);
1027 } else {
1028 /*
1029 * our C library's vsnprintf() was nice enough
1030 * to return the necessary size of the buffer!
1031 */
1032 out_string = xmalloc(actual_len + 1);
1033 if (out_string) {
1034 va_copy(ap_copy, ap);
1035 vsnprintf(out_string, actual_len + 1,
1036 intermediate_fmt, ap_copy);
1037 va_end(ap_copy);
1038 }
1039 }
1040 } else {
1041 size_t growable_tmp_size = LINEBUFSIZE;
1042 char *growable_tmp = NULL;
1043
1044 /*
1045 * our C library's vsnprintf() doesn't return the
1046 * necessary buffer size on overflow, it considers that
1047 * an error condition. So we need to iteratively grow
1048 * a buffer until it accommodates the vsnprintf() call
1049 */
1050 do {
1051 growable_tmp_size += LINEBUFSIZE;
1052 growable_tmp = xrealloc(growable_tmp,
1053 growable_tmp_size);
1054 if (!growable_tmp)
1055 break;
1056 va_copy(ap_copy, ap);
1057 actual_len = vsnprintf(growable_tmp,
1058 growable_tmp_size,
1059 intermediate_fmt,
1060 ap_copy);
1061 va_end(ap_copy);
1062 } while (actual_len < 0);
1063 out_string = growable_tmp;
1064 }
1065 xfree(intermediate_fmt);
1066 } else if (intermediate_fmt) {
1067 /*
1068 * no additional format sequences, so we can just return the
1069 * intermediate_fmt string
1070 */
1071 out_string = intermediate_fmt;
1072 }
1073
1074 return out_string;
1075 }
1076
1077 /*
1078 * concatenate result of xstrfmt() to dst, expanding dst if necessary
1079 */
xlogfmtcat(char ** dst,const char * fmt,...)1080 static void xlogfmtcat(char **dst, const char *fmt, ...)
1081 {
1082 va_list ap;
1083 char *buf = NULL;
1084
1085 va_start(ap, fmt);
1086 buf = vxstrfmt(fmt, ap);
1087 va_end(ap);
1088
1089 xstrcat(*dst, buf);
1090
1091 xfree(buf);
1092
1093 }
1094
_log_printf(log_t * log,cbuf_t * cb,FILE * stream,const char * fmt,...)1095 static void _log_printf(log_t *log, cbuf_t *cb, FILE *stream,
1096 const char *fmt, ...)
1097 {
1098 va_list ap;
1099 int fd = -1;
1100
1101 /* If the fd is less than 0 just return since we can't do anything here.
1102 * This can happen if a calling program is the one that set up the io.
1103 */
1104 if (stream)
1105 fd = fileno(stream);
1106 if (fd < 0)
1107 return;
1108
1109 /* If the socket has gone away we just return like all is
1110 well. */
1111 if (_fd_writeable(fd) != 1)
1112 return;
1113
1114 va_start(ap, fmt);
1115 if (log->opt.buffered && (cb != NULL)) {
1116 char *buf = vxstrfmt(fmt, ap);
1117 int len = strlen(buf);
1118 int dropped;
1119
1120 cbuf_write(cb, buf, len, &dropped);
1121 cbuf_read_to_fd(cb, fd, -1);
1122 xfree(buf);
1123 } else {
1124 vfprintf(stream, fmt, ap);
1125 }
1126 va_end(ap);
1127
1128 }
1129
1130 /*
1131 * log a message at the specified level to facilities that have been
1132 * configured to receive messages at that level
1133 */
_log_msg(log_level_t level,bool sched,bool spank,const char * fmt,va_list args)1134 static void _log_msg(log_level_t level, bool sched, bool spank, const char *fmt, va_list args)
1135 {
1136 char *pfx = "";
1137 char *buf = NULL;
1138 char *msgbuf = NULL;
1139 int priority = LOG_INFO;
1140
1141 slurm_mutex_lock(&log_lock);
1142
1143 if (!LOG_INITIALIZED) {
1144 log_options_t opts = LOG_OPTS_STDERR_ONLY;
1145 _log_init(NULL, opts, 0, NULL);
1146 }
1147
1148 if (SCHED_LOG_INITIALIZED && sched &&
1149 (highest_sched_log_level > LOG_LEVEL_QUIET)) {
1150 buf = vxstrfmt(fmt, args);
1151 xlogfmtcat(&msgbuf, "[%M] %s%s%s", sched_log->fpfx, pfx, buf);
1152 _log_printf(sched_log, sched_log->fbuf, sched_log->logfp,
1153 "sched: %s\n", msgbuf);
1154 fflush(sched_log->logfp);
1155 xfree(msgbuf);
1156 }
1157
1158 if (level > highest_log_level) {
1159 slurm_mutex_unlock(&log_lock);
1160 xfree(buf);
1161 return;
1162 }
1163
1164 if (log->opt.prefix_level || (log->opt.syslog_level > level)) {
1165 switch (level) {
1166 case LOG_LEVEL_FATAL:
1167 priority = LOG_CRIT;
1168 pfx = "fatal: ";
1169 break;
1170
1171 case LOG_LEVEL_ERROR:
1172 priority = LOG_ERR;
1173 pfx = sched? "error: sched: " : "error: ";
1174 pfx = spank ? "" : pfx;
1175 break;
1176
1177 case LOG_LEVEL_INFO:
1178 case LOG_LEVEL_VERBOSE:
1179 priority = LOG_INFO;
1180 pfx = sched ? "sched: " : "";
1181 break;
1182
1183 case LOG_LEVEL_DEBUG:
1184 priority = LOG_DEBUG;
1185 pfx = sched ? "debug: sched: " : "debug: ";
1186 break;
1187
1188 case LOG_LEVEL_DEBUG2:
1189 priority = LOG_DEBUG;
1190 pfx = sched ? "debug2: sched: " : "debug2: ";
1191 break;
1192
1193 case LOG_LEVEL_DEBUG3:
1194 priority = LOG_DEBUG;
1195 pfx = sched ? "debug3: sched: " : "debug3: ";
1196 break;
1197
1198 case LOG_LEVEL_DEBUG4:
1199 priority = LOG_DEBUG;
1200 pfx = "debug4: ";
1201 break;
1202
1203 case LOG_LEVEL_DEBUG5:
1204 priority = LOG_DEBUG;
1205 pfx = "debug5: ";
1206 break;
1207
1208 default:
1209 priority = LOG_ERR;
1210 pfx = "internal error: ";
1211 break;
1212 }
1213
1214 }
1215
1216 if (!buf) {
1217 /* format the basic message,
1218 * if not already done for scheduling log */
1219 buf = vxstrfmt(fmt, args);
1220 }
1221
1222 if (level <= log->opt.stderr_level) {
1223
1224 fflush(stdout);
1225 if (spank) {
1226 _log_printf(log, log->buf, stderr, "%s\n", buf);
1227 } else if (log->fmt == LOG_FMT_THREAD_ID) {
1228 char tmp[64];
1229 _set_idbuf(tmp, sizeof(tmp));
1230 _log_printf(log, log->buf, stderr, "%s: %s%s\n",
1231 tmp, pfx, buf);
1232 } else {
1233 _log_printf(log, log->buf, stderr, "%s: %s%s\n",
1234 log->argv0, pfx, buf);
1235 }
1236 fflush(stderr);
1237 }
1238
1239 if ((level <= log->opt.logfile_level) && (log->logfp != NULL)) {
1240
1241 xlogfmtcat(&msgbuf, "[%M] %s%s%s", log->fpfx, pfx, buf);
1242 _log_printf(log, log->fbuf, log->logfp, "%s\n", msgbuf);
1243 fflush(log->logfp);
1244
1245 xfree(msgbuf);
1246 }
1247
1248 if (level <= log->opt.syslog_level) {
1249
1250 /* Avoid changing errno if syslog fails */
1251 int orig_errno = slurm_get_errno();
1252 xlogfmtcat(&msgbuf, "%s%s", pfx, buf);
1253 openlog(log->argv0, LOG_PID, log->facility);
1254 syslog(priority, "%.500s", msgbuf);
1255 closelog();
1256 slurm_seterrno(orig_errno);
1257
1258 xfree(msgbuf);
1259 }
1260
1261 slurm_mutex_unlock(&log_lock);
1262
1263 xfree(buf);
1264 }
1265
1266 bool
log_has_data()1267 log_has_data()
1268 {
1269 bool rc = false;
1270 slurm_mutex_lock(&log_lock);
1271 if (log->opt.buffered)
1272 rc = (cbuf_used(log->buf) > 0);
1273 slurm_mutex_unlock(&log_lock);
1274 return rc;
1275 }
1276
1277 static void
_log_flush(log_t * log)1278 _log_flush(log_t *log)
1279 {
1280 if (!log->opt.buffered)
1281 return;
1282
1283 if (log->opt.stderr_level)
1284 cbuf_read_to_fd(log->buf, fileno(stderr), -1);
1285 else if (log->logfp && (fileno(log->logfp) > 0))
1286 cbuf_read_to_fd(log->fbuf, fileno(log->logfp), -1);
1287 }
1288
1289 void
log_flush()1290 log_flush()
1291 {
1292 slurm_mutex_lock(&log_lock);
1293 _log_flush(log);
1294 slurm_mutex_unlock(&log_lock);
1295 }
1296
1297 /*
1298 * attempt to log message and exit()
1299 */
fatal(const char * fmt,...)1300 void fatal(const char *fmt, ...)
1301 {
1302 LOG_MACRO(LOG_LEVEL_FATAL, false, fmt);
1303 log_flush();
1304
1305 exit(1);
1306 }
1307
1308 /*
1309 * attempt to log message and exit()
1310 */
fatal_abort(const char * fmt,...)1311 void fatal_abort(const char *fmt, ...)
1312 {
1313 LOG_MACRO(LOG_LEVEL_FATAL, false, fmt);
1314 log_flush();
1315
1316 abort();
1317 }
1318
1319 /*
1320 * Attempt to log message at a variable log level
1321 */
log_var(const log_level_t log_lvl,const char * fmt,...)1322 void log_var(const log_level_t log_lvl, const char *fmt, ...)
1323 {
1324 LOG_MACRO(log_lvl, false, fmt);
1325
1326 if (log_lvl == LOG_LEVEL_FATAL) {
1327 log_flush();
1328 exit(1);
1329 }
1330 }
1331
sched_log_var(const log_level_t log_lvl,const char * fmt,...)1332 void sched_log_var(const log_level_t log_lvl, const char *fmt, ...)
1333 {
1334 LOG_MACRO(log_lvl, true, fmt);
1335
1336 if (log_lvl == LOG_LEVEL_FATAL) {
1337 log_flush();
1338 exit(1);
1339 }
1340 }
1341
error(const char * fmt,...)1342 int error(const char *fmt, ...)
1343 {
1344 LOG_MACRO(LOG_LEVEL_ERROR, false, fmt);
1345
1346 /*
1347 * Return SLURM_ERROR so calling functions can
1348 * do "return error (...);"
1349 */
1350 return SLURM_ERROR;
1351 }
1352
1353 /*
1354 * Like error(), but printed without the error: prefix so SPANK plugins
1355 * can have a convenient way to return messages to the user.
1356 */
spank_log(const char * fmt,...)1357 void spank_log(const char *fmt, ...)
1358 {
1359 va_list ap;
1360 va_start(ap, fmt);
1361 _log_msg(LOG_LEVEL_ERROR, false, true, fmt, ap);
1362 va_end(ap);
1363 }
1364
info(const char * fmt,...)1365 void info(const char *fmt, ...)
1366 {
1367 LOG_MACRO(LOG_LEVEL_INFO, false, fmt);
1368 }
1369
verbose(const char * fmt,...)1370 void verbose(const char *fmt, ...)
1371 {
1372 LOG_MACRO(LOG_LEVEL_VERBOSE, false, fmt);
1373 }
1374
slurm_debug(const char * fmt,...)1375 void slurm_debug(const char *fmt, ...)
1376 {
1377 LOG_MACRO(LOG_LEVEL_DEBUG, false, fmt);
1378 }
1379
slurm_debug2(const char * fmt,...)1380 void slurm_debug2(const char *fmt, ...)
1381 {
1382 LOG_MACRO(LOG_LEVEL_DEBUG2, false, fmt);
1383 }
1384
slurm_debug3(const char * fmt,...)1385 void slurm_debug3(const char *fmt, ...)
1386 {
1387 LOG_MACRO(LOG_LEVEL_DEBUG3, false, fmt);
1388 }
1389
1390 /*
1391 * Debug levels higher than debug3 are not written to stderr in the
1392 * slurmstepd process after stderr is connected back to the client (srun).
1393 */
slurm_debug4(const char * fmt,...)1394 void slurm_debug4(const char *fmt, ...)
1395 {
1396 LOG_MACRO(LOG_LEVEL_DEBUG4, false, fmt);
1397 }
1398
slurm_debug5(const char * fmt,...)1399 void slurm_debug5(const char *fmt, ...)
1400 {
1401 LOG_MACRO(LOG_LEVEL_DEBUG5, false, fmt);
1402 }
1403
sched_error(const char * fmt,...)1404 int sched_error(const char *fmt, ...)
1405 {
1406 LOG_MACRO(LOG_LEVEL_ERROR, true, fmt);
1407
1408 /*
1409 * Return SLURM_ERROR so calling functions can
1410 * do "return error (...);"
1411 */
1412 return SLURM_ERROR;
1413 }
1414
sched_info(const char * fmt,...)1415 void sched_info(const char *fmt, ...)
1416 {
1417 LOG_MACRO(LOG_LEVEL_INFO, true, fmt);
1418 }
1419
sched_verbose(const char * fmt,...)1420 void sched_verbose(const char *fmt, ...)
1421 {
1422 LOG_MACRO(LOG_LEVEL_VERBOSE, true, fmt);
1423 }
1424
1425 /* Return the highest LOG_LEVEL_* used for any logging mechanism.
1426 * For example, if LOG_LEVEL_INFO is returned, we know that all verbose and
1427 * debug type messages will be ignored. */
get_log_level(void)1428 extern int get_log_level(void)
1429 {
1430 return highest_log_level;
1431 }
1432
get_sched_log_level(void)1433 extern int get_sched_log_level(void)
1434 {
1435 return MAX(highest_log_level, highest_sched_log_level);
1436 }
1437