1/*
2 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2007 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 *
6 * This software is available to you under a choice of one of two
7 * licenses.  You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * OpenIB.org BSD license below:
11 *
12 *     Redistribution and use in source and binary forms, with or
13 *     without modification, are permitted provided that the following
14 *     conditions are met:
15 *
16 *      - Redistributions of source code must retain the above
17 *        copyright notice, this list of conditions and the following
18 *        disclaimer.
19 *
20 *      - Redistributions in binary form must reproduce the above
21 *        copyright notice, this list of conditions and the following
22 *        disclaimer in the documentation and/or other materials
23 *        provided with the distribution.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE.
33 *
34 */
35
36/*
37 * Abstract:
38 *    Implementation of osm_log_t.
39 * This object represents the log file.
40 * This object is part of the opensm family of objects.
41 */
42
43#if HAVE_CONFIG_H
44#  include <config.h>
45#endif				/* HAVE_CONFIG_H */
46
47#include <opensm/osm_file_ids.h>
48#define FILE_ID OSM_FILE_LOG_C
49#include <opensm/osm_log.h>
50#include <stdlib.h>
51#include <stdio.h>
52#include <stdarg.h>
53#include <fcntl.h>
54#include <sys/types.h>
55#include <sys/stat.h>
56#include <errno.h>
57#include <pthread_np.h>
58
59static int log_exit_count = 0;
60
61#ifndef __WIN__
62#include <sys/time.h>
63#include <unistd.h>
64#include <complib/cl_timer.h>
65
66static const char *month_str[] = {
67	"Jan",
68	"Feb",
69	"Mar",
70	"Apr",
71	"May",
72	"Jun",
73	"Jul",
74	"Aug",
75	"Sep",
76	"Oct",
77	"Nov",
78	"Dec"
79};
80#else
81void OsmReportState(IN const char *p_str);
82#endif				/* ndef __WIN__ */
83
84#ifndef __WIN__
85
86static void truncate_log_file(osm_log_t * p_log)
87{
88	int fd = fileno(p_log->out_port);
89	if (ftruncate(fd, 0) < 0)
90		fprintf(stderr, "truncate_log_file: cannot truncate: %s\n",
91			strerror(errno));
92	if (lseek(fd, 0, SEEK_SET) < 0)
93		fprintf(stderr, "truncate_log_file: cannot rewind: %s\n",
94			strerror(errno));
95	p_log->count = 0;
96}
97
98#else				/* Windows */
99
100static void truncate_log_file(osm_log_t * p_log)
101{
102	int fd = _fileno(p_log->out_port);
103	HANDLE hFile = (HANDLE) _get_osfhandle(fd);
104
105	if (_lseek(fd, 0, SEEK_SET) < 0)
106		fprintf(stderr, "truncate_log_file: cannot rewind: %s\n",
107			strerror(errno));
108	SetEndOfFile(hFile);
109	p_log->count = 0;
110}
111#endif				/* ndef __WIN__ */
112
113void osm_log(IN osm_log_t * p_log, IN osm_log_level_t verbosity,
114	     IN const char *p_str, ...)
115{
116	char buffer[LOG_ENTRY_SIZE_MAX];
117	va_list args;
118	int ret;
119#ifdef __WIN__
120	SYSTEMTIME st;
121	uint32_t pid = GetCurrentThreadId();
122#else
123	pid_t pid;
124	time_t tim;
125	struct tm result;
126	uint64_t time_usecs;
127	uint32_t usecs;
128#endif				/* __WIN__ */
129
130	/* If this is a call to syslog - always print it */
131	if (!(verbosity & p_log->level))
132		return;
133
134	va_start(args, p_str);
135#ifndef __WIN__
136	if (p_log->log_prefix == NULL)
137		vsprintf(buffer, p_str, args);
138	else {
139		int n = snprintf(buffer, sizeof(buffer), "%s: ", p_log->log_prefix);
140		vsprintf(buffer + n, p_str, args);
141	}
142#else
143	if (p_log->log_prefix == NULL)
144		_vsnprintf(buffer, 1024, (LPSTR)p_str, args);
145	else {
146		int n = snprintf(buffer, sizeof(buffer), "%s: ", p_log->log_prefix);
147		_vsnprintf(buffer + n, (1024 - n), (LPSTR)p_str, args);
148	}
149#endif
150	va_end(args);
151
152	/* this is a call to the syslog */
153	if (verbosity & OSM_LOG_SYS) {
154		syslog(LOG_INFO, "%s\n", buffer);
155
156		/* SYSLOG should go to stdout too */
157		if (p_log->out_port != stdout) {
158			printf("%s\n", buffer);
159			fflush(stdout);
160		}
161#ifdef __WIN__
162		OsmReportState(buffer);
163#endif				/* __WIN__ */
164	}
165
166	/* regular log to default out_port */
167	cl_spinlock_acquire(&p_log->lock);
168
169	if (p_log->max_size && p_log->count > p_log->max_size) {
170		/* truncate here */
171		fprintf(stderr,
172			"osm_log: log file exceeds the limit %lu. Truncating.\n",
173			p_log->max_size);
174		truncate_log_file(p_log);
175	}
176#ifdef __WIN__
177	GetLocalTime(&st);
178_retry:
179	ret =
180	    fprintf(p_log->out_port,
181		    "[%02d:%02d:%02d:%03d][%04X] 0x%02x -> %s",
182		    st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
183		    pid, verbosity, buffer);
184#else
185	time_usecs = cl_get_time_stamp();
186	tim = time_usecs / 1000000;
187	usecs = time_usecs % 1000000;
188	localtime_r(&tim, &result);
189	pid = pthread_getthreadid_np();
190_retry:
191	ret =
192	    fprintf(p_log->out_port,
193		    "%s %02d %02d:%02d:%02d %06d [%04X] 0x%02x -> %s",
194		    (result.tm_mon <
195		     12 ? month_str[result.tm_mon] : "???"),
196		    result.tm_mday, result.tm_hour, result.tm_min,
197		    result.tm_sec, usecs, pid, verbosity, buffer);
198#endif
199
200	/*  flush log */
201	if (ret > 0 &&
202	    (p_log->flush || (verbosity & (OSM_LOG_ERROR | OSM_LOG_SYS)))
203	    && fflush(p_log->out_port) < 0)
204		ret = -1;
205
206	if (ret >= 0) {
207		log_exit_count = 0;
208		p_log->count += ret;
209	} else if (log_exit_count < 3) {
210		log_exit_count++;
211		if (errno == ENOSPC && p_log->max_size) {
212			fprintf(stderr,
213				"osm_log: write failed: %s. Truncating log file.\n",
214				strerror(errno));
215			truncate_log_file(p_log);
216			goto _retry;
217		}
218		fprintf(stderr, "osm_log: write failed: %s\n", strerror(errno));
219	}
220
221	cl_spinlock_release(&p_log->lock);
222}
223
224void osm_log_v2(IN osm_log_t * p_log, IN osm_log_level_t verbosity,
225		IN const int file_id, IN const char *p_str, ...)
226{
227	char buffer[LOG_ENTRY_SIZE_MAX];
228	va_list args;
229	int ret;
230#ifdef __WIN__
231	SYSTEMTIME st;
232	uint32_t pid = GetCurrentThreadId();
233#else
234	struct timeval tv;
235	pid_t pid = 0;
236	time_t tim;
237	struct tm result;
238	uint64_t time_usecs;
239	uint32_t usecs;
240#endif				/* __WIN__ */
241
242	/* If this is a call to syslog - always print it */
243	if (!(verbosity & p_log->level)) {
244		if (!(verbosity & p_log->per_mod_log_tbl[file_id]))
245			return;
246	}
247
248	va_start(args, p_str);
249#ifndef __WIN__
250	if (p_log->log_prefix == NULL)
251		vsprintf(buffer, p_str, args);
252	else {
253		int n = snprintf(buffer, sizeof(buffer), "%s: ", p_log->log_prefix);
254		vsprintf(buffer + n, p_str, args);
255	}
256#else
257	if (p_log->log_prefix == NULL)
258		_vsnprintf(buffer, 1024, (LPSTR)p_str, args);
259	else {
260		int n = snprintf(buffer, sizeof(buffer), "%s: ", p_log->log_prefix);
261		_vsnprintf(buffer + n, (1024 - n), (LPSTR)p_str, args);
262	}
263#endif
264	va_end(args);
265
266	/* this is a call to the syslog */
267	if (verbosity & OSM_LOG_SYS) {
268		syslog(LOG_INFO, "%s\n", buffer);
269
270		/* SYSLOG should go to stdout too */
271		if (p_log->out_port != stdout) {
272			printf("%s\n", buffer);
273			fflush(stdout);
274		}
275#ifdef __WIN__
276		OsmReportState(buffer);
277#endif				/* __WIN__ */
278	}
279
280	/* regular log to default out_port */
281	cl_spinlock_acquire(&p_log->lock);
282
283	if (p_log->max_size && p_log->count > p_log->max_size) {
284		/* truncate here */
285		fprintf(stderr,
286			"osm_log: log file exceeds the limit %lu. Truncating.\n",
287			p_log->max_size);
288		truncate_log_file(p_log);
289	}
290#ifdef __WIN__
291	GetLocalTime(&st);
292_retry:
293	ret =
294	    fprintf(p_log->out_port,
295		    "[%02d:%02d:%02d:%03d][%04X] 0x%02x -> %s",
296		    st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
297		    pid, verbosity, buffer);
298#else
299	gettimeofday(&tv, NULL);
300	/* Convert the time of day into a microsecond timestamp */
301	time_usecs = ((uint64_t) tv.tv_sec * 1000000) + (uint64_t) tv.tv_usec;
302	tim = time_usecs / 1000000;
303	usecs = time_usecs % 1000000;
304	localtime_r(&tim, &result);
305	pid = pthread_getthreadid_np();
306_retry:
307	ret =
308	    fprintf(p_log->out_port,
309		    "%s %02d %02d:%02d:%02d %06d [%04X] 0x%02x -> %s",
310		    (result.tm_mon <
311		     12 ? month_str[result.tm_mon] : "???"),
312		    result.tm_mday, result.tm_hour, result.tm_min,
313		    result.tm_sec, usecs, pid, verbosity, buffer);
314#endif
315
316	/*  flush log */
317	if (ret > 0 &&
318	    (p_log->flush || (verbosity & (OSM_LOG_ERROR | OSM_LOG_SYS)))
319	    && fflush(p_log->out_port) < 0)
320		ret = -1;
321
322	if (ret >= 0) {
323		log_exit_count = 0;
324		p_log->count += ret;
325	} else if (log_exit_count < 3) {
326		log_exit_count++;
327		if (errno == ENOSPC && p_log->max_size) {
328			fprintf(stderr,
329				"osm_log: write failed: %s. Truncating log file.\n",
330				strerror(errno));
331			truncate_log_file(p_log);
332			goto _retry;
333		}
334		fprintf(stderr, "osm_log: write failed: %s\n", strerror(errno));
335	}
336
337	cl_spinlock_release(&p_log->lock);
338}
339
340void osm_log_raw(IN osm_log_t * p_log, IN osm_log_level_t verbosity,
341		 IN const char *p_buf)
342{
343	if (p_log->level & verbosity) {
344		cl_spinlock_acquire(&p_log->lock);
345		printf("%s", p_buf);
346		cl_spinlock_release(&p_log->lock);
347
348		/*
349		   Flush log on errors too.
350		 */
351		if (p_log->flush || (verbosity & OSM_LOG_ERROR))
352			fflush(stdout);
353	}
354}
355
356void osm_log_msg_box(IN osm_log_t * log, osm_log_level_t level,
357		     const char *func_name, const char *msg)
358{
359#define MSG_BOX_LENGTH 66
360	char buf[MSG_BOX_LENGTH + 1];
361	int i, n;
362
363	if (!osm_log_is_active(log, level))
364		return;
365
366	n = (MSG_BOX_LENGTH - strlen(msg)) / 2 - 1;
367	if (n < 0)
368		n = 0;
369	for (i = 0; i < n; i++)
370		sprintf(buf + i, "*");
371	n += snprintf(buf + n, sizeof(buf) - n, " %s ", msg);
372	for (i = n; i < MSG_BOX_LENGTH; i++)
373		buf[i] = '*';
374	buf[i] = '\0';
375
376	osm_log(log, level, "%s:\n\n\n"
377		"*********************************************"
378		"*********************\n%s\n"
379		"*********************************************"
380		"*********************\n\n\n", func_name, buf);
381}
382
383void osm_log_msg_box_v2(IN osm_log_t * log, osm_log_level_t level,
384			const int file_id, const char *func_name,
385			const char *msg)
386{
387#define MSG_BOX_LENGTH 66
388	char buf[MSG_BOX_LENGTH + 1];
389	int i, n;
390
391	if (!osm_log_is_active_v2(log, level, file_id))
392		return;
393
394	n = (MSG_BOX_LENGTH - strlen(msg)) / 2 - 1;
395	if (n < 0)
396		n = 0;
397	for (i = 0; i < n; i++)
398		sprintf(buf + i, "*");
399	n += snprintf(buf + n, sizeof(buf) - n, " %s ", msg);
400	for (i = n; i < MSG_BOX_LENGTH; i++)
401		buf[i] = '*';
402	buf[i] = '\0';
403
404	osm_log_v2(log, level, file_id, "%s:\n\n\n"
405		   "*********************************************"
406		   "*********************\n%s\n"
407		   "*********************************************"
408		   "*********************\n\n\n", func_name, buf);
409}
410
411boolean_t osm_is_debug(void)
412{
413#if defined( _DEBUG_ )
414	return TRUE;
415#else
416	return FALSE;
417#endif				/* defined( _DEBUG_ ) */
418}
419
420static int open_out_port(IN osm_log_t * p_log)
421{
422	struct stat st;
423
424	if (p_log->accum_log_file)
425		p_log->out_port = fopen(p_log->log_file_name, "a+");
426	else
427		p_log->out_port = fopen(p_log->log_file_name, "w+");
428
429	if (!p_log->out_port) {
430		syslog(LOG_CRIT, "Cannot open file \'%s\' for %s: %s\n",
431		       p_log->log_file_name,
432		       p_log->accum_log_file ? "appending" : "writing",
433		       strerror(errno));
434		fprintf(stderr, "Cannot open file \'%s\': %s\n",
435			p_log->log_file_name, strerror(errno));
436		return -1;
437	}
438
439	if (fstat(fileno(p_log->out_port), &st) == 0)
440		p_log->count = st.st_size;
441
442	syslog(LOG_NOTICE, "%s log file opened\n", p_log->log_file_name);
443
444	if (p_log->daemon) {
445		dup2(fileno(p_log->out_port), 0);
446		dup2(fileno(p_log->out_port), 1);
447		dup2(fileno(p_log->out_port), 2);
448	}
449
450	return 0;
451}
452
453int osm_log_reopen_file(osm_log_t * p_log)
454{
455	int ret;
456
457	if (p_log->out_port == stdout || p_log->out_port == stderr)
458		return 0;
459	cl_spinlock_acquire(&p_log->lock);
460	fclose(p_log->out_port);
461	ret = open_out_port(p_log);
462	cl_spinlock_release(&p_log->lock);
463	return ret;
464}
465
466ib_api_status_t osm_log_init_v2(IN osm_log_t * p_log, IN boolean_t flush,
467				IN uint8_t log_flags, IN const char *log_file,
468				IN unsigned long max_size,
469				IN boolean_t accum_log_file)
470{
471	p_log->level = log_flags | OSM_LOG_SYS;
472	p_log->flush = flush;
473	p_log->count = 0;
474	p_log->max_size = max_size << 20; /* convert size in MB to bytes */
475	p_log->accum_log_file = accum_log_file;
476	p_log->log_file_name = (char *)log_file;
477	memset(p_log->per_mod_log_tbl, 0, sizeof(p_log->per_mod_log_tbl));
478
479	openlog("OpenSM", LOG_CONS | LOG_PID, LOG_USER);
480
481	if (log_file == NULL || !strcmp(log_file, "-") ||
482	    !strcmp(log_file, "stdout"))
483		p_log->out_port = stdout;
484	else if (!strcmp(log_file, "stderr"))
485		p_log->out_port = stderr;
486	else if (open_out_port(p_log))
487		return IB_ERROR;
488
489	if (cl_spinlock_init(&p_log->lock) == CL_SUCCESS)
490		return IB_SUCCESS;
491	else
492		return IB_ERROR;
493}
494
495ib_api_status_t osm_log_init(IN osm_log_t * p_log, IN boolean_t flush,
496			     IN uint8_t log_flags, IN const char *log_file,
497			     IN boolean_t accum_log_file)
498{
499	return osm_log_init_v2(p_log, flush, log_flags, log_file, 0,
500			       accum_log_file);
501}
502
503osm_log_level_t osm_get_log_per_module(IN osm_log_t * p_log,
504				       IN const int file_id)
505{
506	return p_log->per_mod_log_tbl[file_id];
507}
508
509void osm_set_log_per_module(IN osm_log_t * p_log, IN const int file_id,
510			    IN osm_log_level_t level)
511{
512	p_log->per_mod_log_tbl[file_id] = level;
513}
514
515void osm_reset_log_per_module(IN osm_log_t * p_log)
516{
517	memset(p_log->per_mod_log_tbl, 0, sizeof(p_log->per_mod_log_tbl));
518}
519