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