1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2006 Mellanox Technologies LTD. All rights reserved.
4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5219820Sjeff *
6219820Sjeff * This software is available to you under a choice of one of two
7219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
8219820Sjeff * General Public License (GPL) Version 2, available from the file
9219820Sjeff * COPYING in the main directory of this source tree, or the
10219820Sjeff * OpenIB.org BSD license below:
11219820Sjeff *
12219820Sjeff *     Redistribution and use in source and binary forms, with or
13219820Sjeff *     without modification, are permitted provided that the following
14219820Sjeff *     conditions are met:
15219820Sjeff *
16219820Sjeff *      - Redistributions of source code must retain the above
17219820Sjeff *        copyright notice, this list of conditions and the following
18219820Sjeff *        disclaimer.
19219820Sjeff *
20219820Sjeff *      - Redistributions in binary form must reproduce the above
21219820Sjeff *        copyright notice, this list of conditions and the following
22219820Sjeff *        disclaimer in the documentation and/or other materials
23219820Sjeff *        provided with the distribution.
24219820Sjeff *
25219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32219820Sjeff * SOFTWARE.
33219820Sjeff *
34219820Sjeff */
35219820Sjeff
36219820Sjeff/*
37219820Sjeff * Abstract:
38219820Sjeff *    Implementation of osm_log_t.
39219820Sjeff * This object represents the log file.
40219820Sjeff * This object is part of the opensm family of objects.
41219820Sjeff */
42219820Sjeff
43219820Sjeff#if HAVE_CONFIG_H
44219820Sjeff#  include <config.h>
45219820Sjeff#endif				/* HAVE_CONFIG_H */
46219820Sjeff
47219820Sjeff#include <opensm/osm_log.h>
48219820Sjeff#include <stdlib.h>
49219820Sjeff#include <stdio.h>
50219820Sjeff#include <stdarg.h>
51219820Sjeff#include <fcntl.h>
52219820Sjeff#include <sys/types.h>
53219820Sjeff#include <sys/stat.h>
54219820Sjeff#include <errno.h>
55219820Sjeff
56219820Sjeffstatic int log_exit_count = 0;
57219820Sjeff
58219820Sjeff#ifndef WIN32
59219820Sjeff#include <sys/time.h>
60219820Sjeff#include <unistd.h>
61219820Sjeff#include <complib/cl_timer.h>
62219820Sjeff
63219820Sjeffstatic char *month_str[] = {
64219820Sjeff	"Jan",
65219820Sjeff	"Feb",
66219820Sjeff	"Mar",
67219820Sjeff	"Apr",
68219820Sjeff	"May",
69219820Sjeff	"Jun",
70219820Sjeff	"Jul",
71219820Sjeff	"Aug",
72219820Sjeff	"Sep",
73219820Sjeff	"Oct",
74219820Sjeff	"Nov",
75219820Sjeff	"Dec"
76219820Sjeff};
77219820Sjeff#else
78219820Sjeffvoid OsmReportState(IN const char *p_str);
79219820Sjeff#endif				/* ndef WIN32 */
80219820Sjeff
81219820Sjeff#ifndef WIN32
82219820Sjeff
83219820Sjeffstatic void truncate_log_file(osm_log_t * const p_log)
84219820Sjeff{
85219820Sjeff	int fd = fileno(p_log->out_port);
86219820Sjeff	if (ftruncate(fd, 0) < 0)
87219820Sjeff		fprintf(stderr, "truncate_log_file: cannot truncate: %s\n",
88219820Sjeff			strerror(errno));
89219820Sjeff	if (lseek(fd, 0, SEEK_SET) < 0)
90219820Sjeff		fprintf(stderr, "truncate_log_file: cannot rewind: %s\n",
91219820Sjeff			strerror(errno));
92219820Sjeff	p_log->count = 0;
93219820Sjeff}
94219820Sjeff
95219820Sjeff#else				/* Windows */
96219820Sjeff
97219820Sjeffstatic void truncate_log_file(osm_log_t * const p_log)
98219820Sjeff{
99219820Sjeff	fprintf(stderr,
100219820Sjeff		"truncate_log_file: cannot truncate on windows system (yet)\n");
101219820Sjeff}
102219820Sjeff#endif				/* ndef WIN32 */
103219820Sjeff
104219820Sjeffvoid osm_log(IN osm_log_t * const p_log,
105219820Sjeff	     IN const osm_log_level_t verbosity, IN const char *p_str, ...)
106219820Sjeff{
107219820Sjeff	char buffer[LOG_ENTRY_SIZE_MAX];
108219820Sjeff	va_list args;
109219820Sjeff	int ret;
110219820Sjeff#ifdef WIN32
111219820Sjeff	SYSTEMTIME st;
112219820Sjeff	uint32_t pid = GetCurrentThreadId();
113219820Sjeff#else
114219820Sjeff	pid_t pid = 0;
115219820Sjeff	time_t tim;
116219820Sjeff	struct tm result;
117219820Sjeff	uint64_t time_usecs;
118219820Sjeff	uint32_t usecs;
119219820Sjeff#endif				/* WIN32 */
120219820Sjeff
121219820Sjeff	/* If this is a call to syslog - always print it */
122219820Sjeff	if (!(verbosity & (OSM_LOG_SYS | p_log->level)))
123219820Sjeff		return;
124219820Sjeff
125219820Sjeff	va_start(args, p_str);
126219820Sjeff	vsprintf(buffer, p_str, args);
127219820Sjeff	va_end(args);
128219820Sjeff
129219820Sjeff	/* this is a call to the syslog */
130219820Sjeff	if (verbosity & OSM_LOG_SYS) {
131219820Sjeff		syslog(LOG_INFO, "%s\n", buffer);
132219820Sjeff
133219820Sjeff		/* SYSLOG should go to stdout too */
134219820Sjeff		if (p_log->out_port != stdout) {
135219820Sjeff			printf("%s\n", buffer);
136219820Sjeff			fflush(stdout);
137219820Sjeff		}
138219820Sjeff#ifdef WIN32
139219820Sjeff		OsmReportState(buffer);
140219820Sjeff#endif				/* WIN32 */
141219820Sjeff	}
142219820Sjeff
143219820Sjeff	/* regular log to default out_port */
144219820Sjeff	cl_spinlock_acquire(&p_log->lock);
145219820Sjeff
146219820Sjeff	if (p_log->max_size && p_log->count > p_log->max_size) {
147219820Sjeff		/* truncate here */
148219820Sjeff		fprintf(stderr,
149219820Sjeff			"osm_log: log file exceeds the limit %lu. Truncating.\n",
150219820Sjeff			p_log->max_size);
151219820Sjeff		truncate_log_file(p_log);
152219820Sjeff	}
153219820Sjeff#ifdef WIN32
154219820Sjeff	GetLocalTime(&st);
155219820Sjeff_retry:
156219820Sjeff	ret =
157219820Sjeff	    fprintf(p_log->out_port,
158219820Sjeff		    "[%02d:%02d:%02d:%03d][%04X] 0x%02x -> %s",
159219820Sjeff		    st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
160219820Sjeff		    pid, verbosity, buffer);
161219820Sjeff#else
162219820Sjeff	time_usecs = cl_get_time_stamp();
163219820Sjeff	tim = time_usecs / 1000000;
164219820Sjeff	usecs = time_usecs % 1000000;
165219820Sjeff	localtime_r(&tim, &result);
166219820Sjeff	pid = pthread_self();
167219820Sjeff_retry:
168219820Sjeff	ret =
169219820Sjeff	    fprintf(p_log->out_port,
170219820Sjeff		    "%s %02d %02d:%02d:%02d %06d [%04X] 0x%02x -> %s",
171219820Sjeff		    (result.tm_mon <
172219820Sjeff		     12 ? month_str[result.tm_mon] : "???"),
173219820Sjeff		    result.tm_mday, result.tm_hour, result.tm_min,
174219820Sjeff		    result.tm_sec, usecs, pid, verbosity, buffer);
175219820Sjeff#endif
176219820Sjeff
177219820Sjeff	/*  flush log */
178219820Sjeff	if (ret > 0 &&
179219820Sjeff	    (p_log->flush || (verbosity & (OSM_LOG_ERROR | OSM_LOG_SYS)))
180219820Sjeff	    && fflush(p_log->out_port) < 0)
181219820Sjeff		ret = -1;
182219820Sjeff
183219820Sjeff	if (ret >= 0) {
184219820Sjeff		log_exit_count = 0;
185219820Sjeff		p_log->count += ret;
186219820Sjeff	} else if (log_exit_count < 3) {
187219820Sjeff		log_exit_count++;
188219820Sjeff		if (errno == ENOSPC && p_log->max_size) {
189219820Sjeff			fprintf(stderr,
190219820Sjeff				"osm_log: write failed: %s. Truncating log file.\n",
191219820Sjeff				strerror(errno));
192219820Sjeff			truncate_log_file(p_log);
193219820Sjeff			goto _retry;
194219820Sjeff		}
195219820Sjeff		fprintf(stderr, "osm_log: write failed: %s\n", strerror(errno));
196219820Sjeff	}
197219820Sjeff
198219820Sjeff	cl_spinlock_release(&p_log->lock);
199219820Sjeff}
200219820Sjeff
201219820Sjeffvoid osm_log_raw(IN osm_log_t * const p_log,
202219820Sjeff		 IN const osm_log_level_t verbosity, IN const char *p_buf)
203219820Sjeff{
204219820Sjeff	if (p_log->level & verbosity) {
205219820Sjeff		cl_spinlock_acquire(&p_log->lock);
206219820Sjeff		printf("%s", p_buf);
207219820Sjeff		cl_spinlock_release(&p_log->lock);
208219820Sjeff
209219820Sjeff		/*
210219820Sjeff		   Flush log on errors too.
211219820Sjeff		 */
212219820Sjeff		if (p_log->flush || (verbosity & OSM_LOG_ERROR))
213219820Sjeff			fflush(stdout);
214219820Sjeff	}
215219820Sjeff}
216219820Sjeff
217219820Sjeffvoid osm_log_msg_box(IN osm_log_t * log, osm_log_level_t level,
218219820Sjeff		     const char *func_name, const char *msg)
219219820Sjeff{
220219820Sjeff#define MSG_BOX_LENGTH 66
221219820Sjeff	char buf[MSG_BOX_LENGTH + 1];
222219820Sjeff	int i, n;
223219820Sjeff
224219820Sjeff	if (!osm_log_is_active(log, level))
225219820Sjeff		return;
226219820Sjeff
227219820Sjeff	n = (MSG_BOX_LENGTH - strlen(msg)) / 2 - 1;
228219820Sjeff	if (n < 0)
229219820Sjeff		n = 0;
230219820Sjeff	for (i = 0; i < n; i++)
231219820Sjeff		sprintf(buf + i, "*");
232219820Sjeff	n += snprintf(buf + n, sizeof(buf) - n, " %s ", msg);
233219820Sjeff	for (i = n; i < MSG_BOX_LENGTH; i++)
234219820Sjeff		buf[i] = '*';
235219820Sjeff	buf[i] = '\0';
236219820Sjeff
237219820Sjeff	osm_log(log, level, "%s:\n\n\n"
238219820Sjeff		"*********************************************"
239219820Sjeff		"*********************\n%s\n"
240219820Sjeff		"*********************************************"
241219820Sjeff		"*********************\n\n\n", func_name, buf);
242219820Sjeff}
243219820Sjeff
244219820Sjeffboolean_t osm_is_debug(void)
245219820Sjeff{
246219820Sjeff#if defined( _DEBUG_ )
247219820Sjeff	return TRUE;
248219820Sjeff#else
249219820Sjeff	return FALSE;
250219820Sjeff#endif				/* defined( _DEBUG_ ) */
251219820Sjeff}
252219820Sjeff
253219820Sjeffstatic int open_out_port(IN osm_log_t * p_log)
254219820Sjeff{
255219820Sjeff	struct stat st;
256219820Sjeff
257219820Sjeff	if (p_log->accum_log_file)
258219820Sjeff		p_log->out_port = fopen(p_log->log_file_name, "a+");
259219820Sjeff	else
260219820Sjeff		p_log->out_port = fopen(p_log->log_file_name, "w+");
261219820Sjeff
262219820Sjeff	if (!p_log->out_port) {
263219820Sjeff		syslog(LOG_CRIT, "Cannot open file \'%s\' for %s: %s\n",
264219820Sjeff		       p_log->log_file_name,
265219820Sjeff		       p_log->accum_log_file ? "appending" : "writing",
266219820Sjeff		       strerror(errno));
267219820Sjeff		fprintf(stderr, "Cannot open file \'%s\': %s\n",
268219820Sjeff			p_log->log_file_name, strerror(errno));
269219820Sjeff		return -1;
270219820Sjeff	}
271219820Sjeff
272219820Sjeff	if (fstat(fileno(p_log->out_port), &st) == 0)
273219820Sjeff		p_log->count = st.st_size;
274219820Sjeff
275219820Sjeff	syslog(LOG_NOTICE, "%s log file opened\n", p_log->log_file_name);
276219820Sjeff
277219820Sjeff	if (p_log->daemon) {
278219820Sjeff		dup2(fileno(p_log->out_port), 0);
279219820Sjeff		dup2(fileno(p_log->out_port), 1);
280219820Sjeff		dup2(fileno(p_log->out_port), 2);
281219820Sjeff	}
282219820Sjeff
283219820Sjeff	return 0;
284219820Sjeff}
285219820Sjeff
286219820Sjeffint osm_log_reopen_file(osm_log_t * p_log)
287219820Sjeff{
288219820Sjeff	int ret;
289219820Sjeff
290219820Sjeff	if (p_log->out_port == stdout || p_log->out_port == stderr)
291219820Sjeff		return 0;
292219820Sjeff	cl_spinlock_acquire(&p_log->lock);
293219820Sjeff	fclose(p_log->out_port);
294219820Sjeff	ret = open_out_port(p_log);
295219820Sjeff	cl_spinlock_release(&p_log->lock);
296219820Sjeff	return ret;
297219820Sjeff}
298219820Sjeff
299219820Sjeffib_api_status_t osm_log_init_v2(IN osm_log_t * const p_log,
300219820Sjeff				IN const boolean_t flush,
301219820Sjeff				IN const uint8_t log_flags,
302219820Sjeff				IN const char *log_file,
303219820Sjeff				IN const unsigned long max_size,
304219820Sjeff				IN const boolean_t accum_log_file)
305219820Sjeff{
306219820Sjeff	p_log->level = log_flags;
307219820Sjeff	p_log->flush = flush;
308219820Sjeff	p_log->count = 0;
309219820Sjeff	p_log->max_size = max_size;
310219820Sjeff	p_log->accum_log_file = accum_log_file;
311219820Sjeff	p_log->log_file_name = (char *)log_file;
312219820Sjeff
313219820Sjeff	openlog("OpenSM", LOG_CONS | LOG_PID, LOG_USER);
314219820Sjeff
315219820Sjeff	if (log_file == NULL || !strcmp(log_file, "-") ||
316219820Sjeff	    !strcmp(log_file, "stdout"))
317219820Sjeff		p_log->out_port = stdout;
318219820Sjeff	else if (!strcmp(log_file, "stderr"))
319219820Sjeff		p_log->out_port = stderr;
320219820Sjeff	else if (open_out_port(p_log))
321219820Sjeff		return IB_ERROR;
322219820Sjeff
323219820Sjeff	if (cl_spinlock_init(&p_log->lock) == CL_SUCCESS)
324219820Sjeff		return IB_SUCCESS;
325219820Sjeff	else
326219820Sjeff		return IB_ERROR;
327219820Sjeff}
328219820Sjeff
329219820Sjeffib_api_status_t osm_log_init(IN osm_log_t * const p_log,
330219820Sjeff			     IN const boolean_t flush,
331219820Sjeff			     IN const uint8_t log_flags,
332219820Sjeff			     IN const char *log_file,
333219820Sjeff			     IN const boolean_t accum_log_file)
334219820Sjeff{
335219820Sjeff	return osm_log_init_v2(p_log, flush, log_flags, log_file, 0,
336219820Sjeff			       accum_log_file);
337219820Sjeff}
338