1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
2 /*
3 * Copyright (c) 2004-2010 The Trustees of Indiana University and Indiana
4 * University Research and Technology
5 * Corporation. All rights reserved.
6 * Copyright (c) 2004-2017 The University of Tennessee and The University
7 * of Tennessee Research Foundation. All rights
8 * reserved.
9 * Copyright (c) 2004-2006 High Performance Computing Center Stuttgart,
10 * University of Stuttgart. All rights reserved.
11 * Copyright (c) 2004-2006 The Regents of the University of California.
12 * All rights reserved.
13 * Copyright (c) 2007-2008 Cisco Systems, Inc. All rights reserved.
14 * Copyright (c) 2015 Los Alamos National Security, LLC. All rights
15 * reserved.
16 * Copyright (c) 2015 Research Organization for Information Science
17 * and Technology (RIST). All rights reserved.
18 * Copyright (c) 2017 IBM Corporation. All rights reserved.
19 * Copyright (c) 2017-2018 Intel, Inc. All rights reserved.
20 * $COPYRIGHT$
21 *
22 * Additional copyrights may follow
23 *
24 * $HEADER$
25 */
26
27 #include "opal_config.h"
28
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #ifdef HAVE_SYSLOG_H
33 #include <syslog.h>
34 #endif
35 #include <string.h>
36 #include <fcntl.h>
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #ifdef HAVE_SYS_PARAM_H
41 #include <sys/param.h>
42 #endif
43
44 #include "opal/util/opal_environ.h"
45 #include "opal/util/output.h"
46 #include "opal/threads/mutex.h"
47 #include "opal/constants.h"
48 #include "opal/mca/pmix/pmix.h"
49
50 /*
51 * Private data
52 */
53 static int verbose_stream = -1;
54 static opal_output_stream_t verbose;
55 static char *output_dir = NULL;
56 static char *output_prefix = NULL;
57
58
59 /*
60 * Internal data structures and helpers for the generalized output
61 * stream mechanism.
62 */
63 typedef struct {
64 bool ldi_used;
65 bool ldi_enabled;
66 int ldi_verbose_level;
67
68 bool ldi_syslog;
69 int ldi_syslog_priority;
70
71 char *ldi_syslog_ident;
72 char *ldi_prefix;
73 int ldi_prefix_len;
74
75 char *ldi_suffix;
76 int ldi_suffix_len;
77
78 bool ldi_stdout;
79 bool ldi_stderr;
80
81 bool ldi_file;
82 bool ldi_file_want_append;
83 char *ldi_file_suffix;
84 int ldi_fd;
85 int ldi_file_num_lines_lost;
86 } output_desc_t;
87
88 /*
89 * Private functions
90 */
91 static void construct(opal_object_t *stream);
92 static void destruct(opal_object_t *stream);
93 static int do_open(int output_id, opal_output_stream_t * lds);
94 static int open_file(int i);
95 static void free_descriptor(int output_id);
96 static int make_string(char **no_newline_string, output_desc_t *ldi,
97 const char *format, va_list arglist);
98 static int output(int output_id, const char *format, va_list arglist);
99
100
101 #define OPAL_OUTPUT_MAX_STREAMS 64
102 #if defined(HAVE_SYSLOG)
103 #define USE_SYSLOG 1
104 #else
105 #define USE_SYSLOG 0
106 #endif
107
108 /* global state */
109 bool opal_output_redirected_to_syslog = false;
110 int opal_output_redirected_syslog_pri = -1;
111
112 /*
113 * Local state
114 */
115 static bool initialized = false;
116 static int default_stderr_fd = -1;
117 static output_desc_t info[OPAL_OUTPUT_MAX_STREAMS];
118 static char *temp_str = 0;
119 static size_t temp_str_len = 0;
120 static opal_mutex_t mutex;
121 #if defined(HAVE_SYSLOG)
122 static bool syslog_opened = false;
123 #endif
124 static char *redirect_syslog_ident = NULL;
125
126 OBJ_CLASS_INSTANCE(opal_output_stream_t, opal_object_t, construct, destruct);
127
128 /*
129 * Setup the output stream infrastructure
130 */
opal_output_init(void)131 bool opal_output_init(void)
132 {
133 int i;
134 char hostname[OPAL_MAXHOSTNAMELEN];
135 char *str;
136
137 if (initialized) {
138 return true;
139 }
140
141 str = getenv("OPAL_OUTPUT_STDERR_FD");
142 if (NULL != str) {
143 default_stderr_fd = atoi(str);
144 }
145 str = getenv("OPAL_OUTPUT_REDIRECT");
146 if (NULL != str) {
147 if (0 == strcasecmp(str, "syslog")) {
148 opal_output_redirected_to_syslog = true;
149 }
150 }
151 str = getenv("OPAL_OUTPUT_SYSLOG_PRI");
152 #ifdef HAVE_SYSLOG_H
153 if (NULL != str) {
154 if (0 == strcasecmp(str, "info")) {
155 opal_output_redirected_syslog_pri = LOG_INFO;
156 } else if (0 == strcasecmp(str, "error")) {
157 opal_output_redirected_syslog_pri = LOG_ERR;
158 } else if (0 == strcasecmp(str, "warn")) {
159 opal_output_redirected_syslog_pri = LOG_WARNING;
160 } else {
161 opal_output_redirected_syslog_pri = LOG_ERR;
162 }
163 } else {
164 opal_output_redirected_syslog_pri = LOG_ERR;
165 }
166 #endif /* HAVE_SYSLOG_H */
167 str = getenv("OPAL_OUTPUT_SYSLOG_IDENT");
168 if (NULL != str) {
169 redirect_syslog_ident = strdup(str);
170 }
171
172 OBJ_CONSTRUCT(&verbose, opal_output_stream_t);
173 if (opal_output_redirected_to_syslog) {
174 verbose.lds_want_syslog = true;
175 verbose.lds_syslog_priority = opal_output_redirected_syslog_pri;
176 if (NULL != str) {
177 verbose.lds_syslog_ident = strdup(redirect_syslog_ident);
178 }
179 verbose.lds_want_stderr = false;
180 verbose.lds_want_stdout = false;
181 } else {
182 str = getenv("OPAL_OUTPUT_INTERNAL_TO_STDOUT");
183 if (NULL != str && str[0] == '1') {
184 verbose.lds_want_stdout = true;
185 }
186 else {
187 verbose.lds_want_stderr = true;
188 }
189 }
190 gethostname(hostname, sizeof(hostname));
191 asprintf(&verbose.lds_prefix, "[%s:%05d] ", hostname, getpid());
192
193 for (i = 0; i < OPAL_OUTPUT_MAX_STREAMS; ++i) {
194 info[i].ldi_used = false;
195 info[i].ldi_enabled = false;
196
197 info[i].ldi_syslog = opal_output_redirected_to_syslog;
198 info[i].ldi_file = false;
199 info[i].ldi_file_suffix = NULL;
200 info[i].ldi_file_want_append = false;
201 info[i].ldi_fd = -1;
202 info[i].ldi_file_num_lines_lost = 0;
203 }
204
205 /* Initialize the mutex that protects the output */
206
207 OBJ_CONSTRUCT(&mutex, opal_mutex_t);
208 initialized = true;
209
210 /* Set some defaults */
211
212 asprintf(&output_prefix, "output-pid%d-", getpid());
213 output_dir = strdup(opal_tmp_directory());
214
215 /* Open the default verbose stream */
216 verbose_stream = opal_output_open(&verbose);
217 return true;
218 }
219
220
221 /*
222 * Open a stream
223 */
opal_output_open(opal_output_stream_t * lds)224 int opal_output_open(opal_output_stream_t * lds)
225 {
226 return do_open(-1, lds);
227 }
228
229
230 /*
231 * Reset the parameters on a stream
232 */
opal_output_reopen(int output_id,opal_output_stream_t * lds)233 int opal_output_reopen(int output_id, opal_output_stream_t * lds)
234 {
235 return do_open(output_id, lds);
236 }
237
238
239 /*
240 * Enable and disable output streams
241 */
opal_output_switch(int output_id,bool enable)242 bool opal_output_switch(int output_id, bool enable)
243 {
244 bool ret = false;
245
246 /* Setup */
247
248 if (!initialized) {
249 opal_output_init();
250 }
251
252 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS) {
253 ret = info[output_id].ldi_enabled;
254 info[output_id].ldi_enabled = enable;
255 }
256
257 return ret;
258 }
259
260
261 /*
262 * Reopen all the streams; used during checkpoint/restart.
263 */
opal_output_reopen_all(void)264 void opal_output_reopen_all(void)
265 {
266 char *str;
267 char hostname[OPAL_MAXHOSTNAMELEN];
268
269 str = getenv("OPAL_OUTPUT_STDERR_FD");
270 if (NULL != str) {
271 default_stderr_fd = atoi(str);
272 } else {
273 default_stderr_fd = -1;
274 }
275
276 gethostname(hostname, sizeof(hostname));
277 if( NULL != verbose.lds_prefix ) {
278 free(verbose.lds_prefix);
279 verbose.lds_prefix = NULL;
280 }
281 asprintf(&verbose.lds_prefix, "[%s:%05d] ", hostname, getpid());
282 #if 0
283 int i;
284 opal_output_stream_t lds;
285
286 for (i = 0; i < OPAL_OUTPUT_MAX_STREAMS; ++i) {
287
288 /* scan till we find ldi_used == 0, which is the end-marker */
289
290 if (!info[i].ldi_used) {
291 break;
292 }
293
294 /*
295 * set this to zero to ensure that opal_output_open will
296 * return this same index as the output stream id
297 */
298 info[i].ldi_used = false;
299
300 #if USE_SYSLOG
301 lds.lds_want_syslog = info[i].ldi_syslog;
302 lds.lds_syslog_priority = info[i].ldi_syslog_priority;
303 lds.lds_syslog_ident = info[i].ldi_syslog_ident;
304 #else
305 lds.lds_want_syslog = false;
306 #endif
307 lds.lds_prefix = info[i].ldi_prefix;
308 lds.lds_suffix = info[i].ldi_suffix;
309 lds.lds_want_stdout = info[i].ldi_stdout;
310 lds.lds_want_stderr = info[i].ldi_stderr;
311 lds.lds_want_file = (-1 == info[i].ldi_fd) ? false : true;
312 /* open all streams in append mode */
313 lds.lds_want_file_append = true;
314 lds.lds_file_suffix = info[i].ldi_file_suffix;
315
316 /*
317 * call opal_output_open to open the stream. The return value
318 * is guaranteed to be i. So we can ignore it.
319 */
320 opal_output_open(&lds);
321 }
322 #endif
323 }
324
325
326 /*
327 * Close a stream
328 */
opal_output_close(int output_id)329 void opal_output_close(int output_id)
330 {
331 int i;
332
333 /* Setup */
334
335 if (!initialized) {
336 return;
337 }
338
339 /* If it's valid, used, enabled, and has an open file descriptor,
340 * free the resources associated with the descriptor */
341
342 OPAL_THREAD_LOCK(&mutex);
343 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS &&
344 info[output_id].ldi_used && info[output_id].ldi_enabled) {
345 free_descriptor(output_id);
346
347 /* If no one has the syslog open, we should close it */
348
349 for (i = 0; i < OPAL_OUTPUT_MAX_STREAMS; ++i) {
350 if (info[i].ldi_used && info[i].ldi_syslog) {
351 break;
352 }
353 }
354
355 #if defined(HAVE_SYSLOG) && defined(HAVE_SYSLOG_H)
356 if (i >= OPAL_OUTPUT_MAX_STREAMS && syslog_opened) {
357 closelog();
358 }
359 #endif
360 }
361
362 OPAL_THREAD_UNLOCK(&mutex);
363 }
364
365
366 /*
367 * Main function to send output to a stream
368 */
opal_output(int output_id,const char * format,...)369 void opal_output(int output_id, const char *format, ...)
370 {
371 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS) {
372 va_list arglist;
373 va_start(arglist, format);
374 output(output_id, format, arglist);
375 va_end(arglist);
376 }
377 }
378
379
380 /*
381 * Send a message to a stream if the verbose level is high enough
382 */
opal_output_verbose(int level,int output_id,const char * format,...)383 void opal_output_verbose(int level, int output_id, const char *format, ...)
384 {
385 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS &&
386 info[output_id].ldi_verbose_level >= level) {
387 va_list arglist;
388 va_start(arglist, format);
389 output(output_id, format, arglist);
390 va_end(arglist);
391 }
392 }
393
394
395 /*
396 * Send a message to a stream if the verbose level is high enough
397 */
opal_output_vverbose(int level,int output_id,const char * format,va_list arglist)398 void opal_output_vverbose(int level, int output_id, const char *format,
399 va_list arglist)
400 {
401 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS &&
402 info[output_id].ldi_verbose_level >= level) {
403 output(output_id, format, arglist);
404 }
405 }
406
407
408 /*
409 * Send a message to a string if the verbose level is high enough
410 */
opal_output_string(int level,int output_id,const char * format,...)411 char *opal_output_string(int level, int output_id, const char *format, ...)
412 {
413 int rc;
414 char *ret = NULL;
415
416 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS &&
417 info[output_id].ldi_verbose_level >= level) {
418 va_list arglist;
419 va_start(arglist, format);
420 rc = make_string(&ret, &info[output_id], format, arglist);
421 va_end(arglist);
422 if (OPAL_SUCCESS != rc) {
423 ret = NULL;
424 }
425 }
426
427 return ret;
428 }
429
430
431 /*
432 * Send a message to a string if the verbose level is high enough
433 */
opal_output_vstring(int level,int output_id,const char * format,va_list arglist)434 char *opal_output_vstring(int level, int output_id, const char *format,
435 va_list arglist)
436 {
437 int rc;
438 char *ret = NULL;
439
440 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS &&
441 info[output_id].ldi_verbose_level >= level) {
442 rc = make_string(&ret, &info[output_id], format, arglist);
443 if (OPAL_SUCCESS != rc) {
444 ret = NULL;
445 }
446 }
447
448 return ret;
449 }
450
451
452 /*
453 * Set the verbosity level of a stream
454 */
opal_output_set_verbosity(int output_id,int level)455 void opal_output_set_verbosity(int output_id, int level)
456 {
457 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS) {
458 info[output_id].ldi_verbose_level = level;
459 }
460 }
461
462
463 /*
464 * Control where output flies will go
465 */
opal_output_set_output_file_info(const char * dir,const char * prefix,char ** olddir,char ** oldprefix)466 void opal_output_set_output_file_info(const char *dir,
467 const char *prefix,
468 char **olddir,
469 char **oldprefix)
470 {
471 if (NULL != olddir) {
472 *olddir = strdup(output_dir);
473 }
474 if (NULL != oldprefix) {
475 *oldprefix = strdup(output_prefix);
476 }
477
478 if (NULL != dir) {
479 free(output_dir);
480 output_dir = strdup(dir);
481 }
482 if (NULL != prefix) {
483 free(output_prefix);
484 output_prefix = strdup(prefix);
485 }
486 }
487
488
489 /*
490 * Shut down the output stream system
491 */
opal_output_finalize(void)492 void opal_output_finalize(void)
493 {
494 if (initialized) {
495 if (verbose_stream != -1) {
496 opal_output_close(verbose_stream);
497 }
498 free(verbose.lds_prefix);
499 verbose.lds_prefix = NULL;
500
501 verbose_stream = -1;
502
503 free (output_prefix);
504 output_prefix = NULL;
505
506 free (output_dir);
507 output_dir = NULL;
508
509 if(NULL != temp_str) {
510 free(temp_str);
511 temp_str = NULL;
512 temp_str_len = 0;
513 }
514 OBJ_DESTRUCT(&verbose);
515 OBJ_DESTRUCT(&mutex);
516 }
517
518 initialized = false;
519 }
520
521 /************************************************************************/
522
523 /*
524 * Constructor
525 */
construct(opal_object_t * obj)526 static void construct(opal_object_t *obj)
527 {
528 opal_output_stream_t *stream = (opal_output_stream_t*) obj;
529
530 stream->lds_verbose_level = 0;
531 stream->lds_syslog_priority = 0;
532 stream->lds_syslog_ident = NULL;
533 stream->lds_prefix = NULL;
534 stream->lds_suffix = NULL;
535 stream->lds_is_debugging = false;
536 stream->lds_want_syslog = false;
537 stream->lds_want_stdout = false;
538 stream->lds_want_stderr = false;
539 stream->lds_want_file = false;
540 stream->lds_want_file_append = false;
541 stream->lds_file_suffix = NULL;
542 }
destruct(opal_object_t * obj)543 static void destruct(opal_object_t *obj)
544 {
545 opal_output_stream_t *stream = (opal_output_stream_t*) obj;
546
547 if( NULL != stream->lds_file_suffix ) {
548 free(stream->lds_file_suffix);
549 stream->lds_file_suffix = NULL;
550 }
551 }
552
553 /*
554 * Back-end of open() and reopen(). Necessary to have it as a
555 * back-end function so that we can do the thread locking properly
556 * (especially upon reopen).
557 */
do_open(int output_id,opal_output_stream_t * lds)558 static int do_open(int output_id, opal_output_stream_t * lds)
559 {
560 int i;
561 bool redirect_to_file = false;
562 char *str, *sfx;
563
564 /* Setup */
565
566 if (!initialized) {
567 opal_output_init();
568 }
569
570 str = getenv("OPAL_OUTPUT_REDIRECT");
571 if (NULL != str && 0 == strcasecmp(str, "file")) {
572 redirect_to_file = true;
573 }
574 sfx = getenv("OPAL_OUTPUT_SUFFIX");
575
576 /* If output_id == -1, find an available stream, or return
577 * OPAL_ERROR */
578
579 if (-1 == output_id) {
580 OPAL_THREAD_LOCK(&mutex);
581 for (i = 0; i < OPAL_OUTPUT_MAX_STREAMS; ++i) {
582 if (!info[i].ldi_used) {
583 break;
584 }
585 }
586 if (i >= OPAL_OUTPUT_MAX_STREAMS) {
587 OPAL_THREAD_UNLOCK(&mutex);
588 return OPAL_ERR_OUT_OF_RESOURCE;
589 }
590 }
591
592 /* Otherwise, we're reopening, so we need to free all previous
593 * resources, close files, etc. */
594
595 else {
596 free_descriptor(output_id);
597 i = output_id;
598 }
599
600 /* Special case: if we got NULL for lds, then just use the default
601 * verbose */
602
603 if (NULL == lds) {
604 lds = &verbose;
605 }
606
607 /* Got a stream -- now initialize it and open relevant outputs */
608
609 info[i].ldi_used = true;
610 if (-1 == output_id) {
611 OPAL_THREAD_UNLOCK(&mutex);
612 }
613 info[i].ldi_enabled = lds->lds_is_debugging ?
614 (bool) OPAL_ENABLE_DEBUG : true;
615 info[i].ldi_verbose_level = lds->lds_verbose_level;
616
617 #if USE_SYSLOG
618 #if defined(HAVE_SYSLOG) && defined(HAVE_SYSLOG_H)
619 if (opal_output_redirected_to_syslog) {
620 info[i].ldi_syslog = true;
621 info[i].ldi_syslog_priority = opal_output_redirected_syslog_pri;
622 if (NULL != redirect_syslog_ident) {
623 info[i].ldi_syslog_ident = strdup(redirect_syslog_ident);
624 openlog(redirect_syslog_ident, LOG_PID, LOG_USER);
625 } else {
626 info[i].ldi_syslog_ident = NULL;
627 openlog("opal", LOG_PID, LOG_USER);
628 }
629 syslog_opened = true;
630 } else {
631 #endif
632 info[i].ldi_syslog = lds->lds_want_syslog;
633 if (lds->lds_want_syslog) {
634
635 #if defined(HAVE_SYSLOG) && defined(HAVE_SYSLOG_H)
636 if (NULL != lds->lds_syslog_ident) {
637 info[i].ldi_syslog_ident = strdup(lds->lds_syslog_ident);
638 openlog(lds->lds_syslog_ident, LOG_PID, LOG_USER);
639 } else {
640 info[i].ldi_syslog_ident = NULL;
641 openlog("opal", LOG_PID, LOG_USER);
642 }
643 #endif
644 syslog_opened = true;
645 info[i].ldi_syslog_priority = lds->lds_syslog_priority;
646 }
647
648 #if defined(HAVE_SYSLOG) && defined(HAVE_SYSLOG_H)
649 }
650 #endif
651
652 #else
653 info[i].ldi_syslog = false;
654 #endif
655
656 if (NULL != lds->lds_prefix) {
657 info[i].ldi_prefix = strdup(lds->lds_prefix);
658 info[i].ldi_prefix_len = (int)strlen(lds->lds_prefix);
659 } else {
660 info[i].ldi_prefix = NULL;
661 info[i].ldi_prefix_len = 0;
662 }
663
664 if (NULL != lds->lds_suffix) {
665 info[i].ldi_suffix = strdup(lds->lds_suffix);
666 info[i].ldi_suffix_len = (int)strlen(lds->lds_suffix);
667 } else {
668 info[i].ldi_suffix = NULL;
669 info[i].ldi_suffix_len = 0;
670 }
671
672 if (opal_output_redirected_to_syslog) {
673 /* since all is redirected to syslog, ensure
674 * we don't duplicate the output to the std places
675 */
676 info[i].ldi_stdout = false;
677 info[i].ldi_stderr = false;
678 info[i].ldi_file = false;
679 info[i].ldi_fd = -1;
680 } else {
681 /* since we aren't redirecting to syslog, use what was
682 * given to us
683 */
684 if (NULL != str && redirect_to_file) {
685 info[i].ldi_stdout = false;
686 info[i].ldi_stderr = false;
687 info[i].ldi_file = true;
688 } else {
689 info[i].ldi_stdout = lds->lds_want_stdout;
690 info[i].ldi_stderr = lds->lds_want_stderr;
691
692 info[i].ldi_fd = -1;
693 info[i].ldi_file = lds->lds_want_file;
694 }
695 if (NULL != sfx) {
696 info[i].ldi_file_suffix = strdup(sfx);
697 } else {
698 info[i].ldi_file_suffix = (NULL == lds->lds_file_suffix) ? NULL :
699 strdup(lds->lds_file_suffix);
700 }
701 info[i].ldi_file_want_append = lds->lds_want_file_append;
702 info[i].ldi_file_num_lines_lost = 0;
703 }
704
705 /* Special case: output_id == 0 == verbose_stream
706 * This is the verbose stream, so update the internal 'verbose_stream'
707 * to match the parameters set in the info[i]
708 */
709 if( verbose_stream == i ) {
710 verbose.lds_want_syslog = info[i].ldi_syslog;
711 verbose.lds_syslog_priority = info[i].ldi_syslog_priority;
712 verbose.lds_syslog_ident = info[i].ldi_syslog_ident;
713 verbose.lds_want_stdout = info[i].ldi_stdout;
714 verbose.lds_want_stderr = info[i].ldi_stderr;
715 }
716
717 /* Don't open a file in the session directory now -- do that lazily
718 * so that if there's no output, we don't have an empty file */
719
720 return i;
721 }
722
723
open_file(int i)724 static int open_file(int i)
725 {
726 int flags;
727 char *filename;
728 int n;
729
730 /* first check to see if this file is already open
731 * on someone else's stream - if so, we don't want
732 * to open it twice
733 */
734 for (n=0; n < OPAL_OUTPUT_MAX_STREAMS; n++) {
735 if (i == n) {
736 continue;
737 }
738 if (!info[n].ldi_used) {
739 continue;
740 }
741 if (!info[n].ldi_file) {
742 continue;
743 }
744 if (NULL != info[i].ldi_file_suffix &&
745 NULL != info[n].ldi_file_suffix) {
746 if (0 != strcmp(info[i].ldi_file_suffix, info[n].ldi_file_suffix)) {
747 break;
748 }
749 }
750 if (NULL == info[i].ldi_file_suffix &&
751 NULL != info[n].ldi_file_suffix) {
752 break;
753 }
754 if (NULL != info[i].ldi_file_suffix &&
755 NULL == info[n].ldi_file_suffix) {
756 break;
757 }
758 if (info[n].ldi_fd < 0) {
759 break;
760 }
761 info[i].ldi_fd = info[n].ldi_fd;
762 return OPAL_SUCCESS;
763 }
764
765 /* Setup the filename and open flags */
766
767 if (NULL != output_dir) {
768 filename = (char *) malloc(OPAL_PATH_MAX);
769 if (NULL == filename) {
770 return OPAL_ERR_OUT_OF_RESOURCE;
771 }
772 strncpy(filename, output_dir, OPAL_PATH_MAX);
773 strcat(filename, "/");
774 if (NULL != output_prefix) {
775 strcat(filename, output_prefix);
776 }
777 if (info[i].ldi_file_suffix != NULL) {
778 strcat(filename, info[i].ldi_file_suffix);
779 } else {
780 info[i].ldi_file_suffix = NULL;
781 strcat(filename, "output.txt");
782 }
783 flags = O_CREAT | O_RDWR;
784 if (!info[i].ldi_file_want_append) {
785 flags |= O_TRUNC;
786 }
787
788 /* Actually open the file */
789 info[i].ldi_fd = open(filename, flags, 0644);
790 if (-1 == info[i].ldi_fd) {
791 info[i].ldi_used = false;
792 free(filename); /* release the filename in all cases */
793 return OPAL_ERR_IN_ERRNO;
794 }
795
796 /* Make the file be close-on-exec to prevent child inheritance
797 * problems */
798 if (-1 == fcntl(info[i].ldi_fd, F_SETFD, 1)) {
799 free(filename); /* release the filename in all cases */
800 return OPAL_ERR_IN_ERRNO;
801 }
802
803 /* register it to be ignored */
804 if (NULL != opal_pmix.register_cleanup) {
805 opal_pmix.register_cleanup(filename, false, true, false);
806 }
807 free(filename); /* release the filename in all cases */
808 }
809
810 /* Return successfully even if the session dir did not exist yet;
811 * we'll try opening it later */
812
813 return OPAL_SUCCESS;
814 }
815
816
817 /*
818 * Free all the resources associated with a descriptor.
819 */
free_descriptor(int output_id)820 static void free_descriptor(int output_id)
821 {
822 output_desc_t *ldi;
823
824 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS &&
825 info[output_id].ldi_used && info[output_id].ldi_enabled) {
826 ldi = &info[output_id];
827
828 if (-1 != ldi->ldi_fd) {
829 close(ldi->ldi_fd);
830 }
831 ldi->ldi_used = false;
832
833 /* If we strduped a prefix, suffix, or syslog ident, free it */
834
835 if (NULL != ldi->ldi_prefix) {
836 free(ldi->ldi_prefix);
837 }
838 ldi->ldi_prefix = NULL;
839
840 if (NULL != ldi->ldi_suffix) {
841 free(ldi->ldi_suffix);
842 }
843 ldi->ldi_suffix = NULL;
844
845 if (NULL != ldi->ldi_file_suffix) {
846 free(ldi->ldi_file_suffix);
847 }
848 ldi->ldi_file_suffix = NULL;
849
850 if (NULL != ldi->ldi_syslog_ident) {
851 free(ldi->ldi_syslog_ident);
852 }
853 ldi->ldi_syslog_ident = NULL;
854 }
855 }
856
857
make_string(char ** no_newline_string,output_desc_t * ldi,const char * format,va_list arglist)858 static int make_string(char **no_newline_string, output_desc_t *ldi,
859 const char *format, va_list arglist)
860 {
861 size_t len, total_len;
862 bool want_newline = false;
863
864 /* Make the formatted string */
865
866 vasprintf(no_newline_string, format, arglist);
867 total_len = len = strlen(*no_newline_string);
868 if ('\n' != (*no_newline_string)[len - 1]) {
869 want_newline = true;
870 ++total_len;
871 } else if (NULL != ldi->ldi_suffix) {
872 /* if we have a suffix, then we don't want a
873 * newline to appear before it
874 */
875 (*no_newline_string)[len - 1] = '\0';
876 want_newline = true; /* add newline to end after suffix */
877 /* total_len won't change since we just moved the newline
878 * to appear after the suffix
879 */
880 }
881 if (NULL != ldi->ldi_prefix) {
882 total_len += strlen(ldi->ldi_prefix);
883 }
884 if (NULL != ldi->ldi_suffix) {
885 total_len += strlen(ldi->ldi_suffix);
886 }
887 if (temp_str_len < total_len + want_newline) {
888 if (NULL != temp_str) {
889 free(temp_str);
890 }
891 temp_str = (char *) malloc(total_len * 2);
892 if (NULL == temp_str) {
893 return OPAL_ERR_OUT_OF_RESOURCE;
894 }
895 temp_str_len = total_len * 2;
896 }
897 if (NULL != ldi->ldi_prefix && NULL != ldi->ldi_suffix) {
898 if (want_newline) {
899 snprintf(temp_str, temp_str_len, "%s%s%s\n",
900 ldi->ldi_prefix, *no_newline_string, ldi->ldi_suffix);
901 } else {
902 snprintf(temp_str, temp_str_len, "%s%s%s", ldi->ldi_prefix,
903 *no_newline_string, ldi->ldi_suffix);
904 }
905 } else if (NULL != ldi->ldi_prefix) {
906 if (want_newline) {
907 snprintf(temp_str, temp_str_len, "%s%s\n",
908 ldi->ldi_prefix, *no_newline_string);
909 } else {
910 snprintf(temp_str, temp_str_len, "%s%s", ldi->ldi_prefix,
911 *no_newline_string);
912 }
913 } else if (NULL != ldi->ldi_suffix) {
914 if (want_newline) {
915 snprintf(temp_str, temp_str_len, "%s%s\n",
916 *no_newline_string, ldi->ldi_suffix);
917 } else {
918 snprintf(temp_str, temp_str_len, "%s%s",
919 *no_newline_string, ldi->ldi_suffix);
920 }
921 } else {
922 if (want_newline) {
923 snprintf(temp_str, temp_str_len, "%s\n", *no_newline_string);
924 } else {
925 snprintf(temp_str, temp_str_len, "%s", *no_newline_string);
926 }
927 }
928
929 return OPAL_SUCCESS;
930 }
931
932 /*
933 * Do the actual output. Take a va_list so that we can be called from
934 * multiple different places, even functions that took "..." as input
935 * arguments.
936 */
output(int output_id,const char * format,va_list arglist)937 static int output(int output_id, const char *format, va_list arglist)
938 {
939 int rc = OPAL_SUCCESS;
940 char *str, *out = NULL;
941 output_desc_t *ldi;
942
943 /* Setup */
944
945 if (!initialized) {
946 opal_output_init();
947 }
948
949 /* If it's valid, used, and enabled, output */
950
951 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS &&
952 info[output_id].ldi_used && info[output_id].ldi_enabled) {
953 OPAL_THREAD_LOCK(&mutex);
954 ldi = &info[output_id];
955
956 /* Make the strings */
957 if (OPAL_SUCCESS != (rc = make_string(&str, ldi, format, arglist))) {
958 OPAL_THREAD_UNLOCK(&mutex);
959 return rc;
960 }
961
962 /* Syslog output -- does not use the newline-appended string */
963 #if defined(HAVE_SYSLOG) && defined(HAVE_SYSLOG_H)
964 if (ldi->ldi_syslog) {
965 syslog(ldi->ldi_syslog_priority, "%s", str);
966 }
967 #endif
968
969 /* All others (stdout, stderr, file) use temp_str, potentially
970 with a newline appended */
971
972 out = temp_str;
973
974 /* stdout output */
975 if (ldi->ldi_stdout) {
976 write(fileno(stdout), out, (int)strlen(out));
977 fflush(stdout);
978 }
979
980 /* stderr output */
981 if (ldi->ldi_stderr) {
982 write((-1 == default_stderr_fd) ?
983 fileno(stderr) : default_stderr_fd,
984 out, (int)strlen(out));
985 fflush(stderr);
986 }
987
988 /* File output -- first check to see if the file opening was
989 * delayed. If so, try to open it. If we failed to open it,
990 * then just discard (there are big warnings in the
991 * opal_output.h docs about this!). */
992
993 if (ldi->ldi_file) {
994 if (ldi->ldi_fd == -1) {
995 if (OPAL_SUCCESS != open_file(output_id)) {
996 ++ldi->ldi_file_num_lines_lost;
997 } else if (ldi->ldi_file_num_lines_lost > 0) {
998 char buffer[BUFSIZ];
999 char *out = buffer;
1000 memset(buffer, 0, BUFSIZ);
1001 snprintf(buffer, BUFSIZ - 1,
1002 "[WARNING: %d lines lost because the Open MPI process session directory did\n not exist when opal_output() was invoked]\n",
1003 ldi->ldi_file_num_lines_lost);
1004 write(ldi->ldi_fd, buffer, (int)strlen(buffer));
1005 ldi->ldi_file_num_lines_lost = 0;
1006 if (out != buffer) {
1007 free(out);
1008 }
1009 }
1010 }
1011 if (ldi->ldi_fd != -1) {
1012 write(ldi->ldi_fd, out, (int)strlen(out));
1013 }
1014 }
1015 OPAL_THREAD_UNLOCK(&mutex);
1016 free(str);
1017 }
1018
1019 return rc;
1020 }
1021
opal_output_get_verbosity(int output_id)1022 int opal_output_get_verbosity(int output_id)
1023 {
1024 if (output_id >= 0 && output_id < OPAL_OUTPUT_MAX_STREAMS && info[output_id].ldi_used) {
1025 return info[output_id].ldi_verbose_level;
1026 } else {
1027 return -1;
1028 }
1029 }
1030