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(&timestamp_t, &timestamp_tm)) {
193 		fprintf(stderr, "localtime_r() failed\n");
194 		return 0;
195 	}
196 	return strftime(timestamp_buf, max, timestamp_fmt, &timestamp_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