1/*
2 * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2006 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_log.h>
48#include <stdlib.h>
49#include <stdio.h>
50#include <stdarg.h>
51#include <fcntl.h>
52#include <sys/types.h>
53#include <sys/stat.h>
54#include <errno.h>
55
56static int log_exit_count = 0;
57
58#ifndef WIN32
59#include <sys/time.h>
60#include <unistd.h>
61#include <complib/cl_timer.h>
62
63static char *month_str[] = {
64	"Jan",
65	"Feb",
66	"Mar",
67	"Apr",
68	"May",
69	"Jun",
70	"Jul",
71	"Aug",
72	"Sep",
73	"Oct",
74	"Nov",
75	"Dec"
76};
77#else
78void OsmReportState(IN const char *p_str);
79#endif				/* ndef WIN32 */
80
81#ifndef WIN32
82
83static void truncate_log_file(osm_log_t * const p_log)
84{
85	int fd = fileno(p_log->out_port);
86	if (ftruncate(fd, 0) < 0)
87		fprintf(stderr, "truncate_log_file: cannot truncate: %s\n",
88			strerror(errno));
89	if (lseek(fd, 0, SEEK_SET) < 0)
90		fprintf(stderr, "truncate_log_file: cannot rewind: %s\n",
91			strerror(errno));
92	p_log->count = 0;
93}
94
95#else				/* Windows */
96
97static void truncate_log_file(osm_log_t * const p_log)
98{
99	fprintf(stderr,
100		"truncate_log_file: cannot truncate on windows system (yet)\n");
101}
102#endif				/* ndef WIN32 */
103
104void osm_log(IN osm_log_t * const p_log,
105	     IN const osm_log_level_t verbosity, IN const char *p_str, ...)
106{
107	char buffer[LOG_ENTRY_SIZE_MAX];
108	va_list args;
109	int ret;
110#ifdef WIN32
111	SYSTEMTIME st;
112	uint32_t pid = GetCurrentThreadId();
113#else
114	pid_t pid = 0;
115	time_t tim;
116	struct tm result;
117	uint64_t time_usecs;
118	uint32_t usecs;
119#endif				/* WIN32 */
120
121	/* If this is a call to syslog - always print it */
122	if (!(verbosity & (OSM_LOG_SYS | p_log->level)))
123		return;
124
125	va_start(args, p_str);
126	vsprintf(buffer, p_str, args);
127	va_end(args);
128
129	/* this is a call to the syslog */
130	if (verbosity & OSM_LOG_SYS) {
131		syslog(LOG_INFO, "%s\n", buffer);
132
133		/* SYSLOG should go to stdout too */
134		if (p_log->out_port != stdout) {
135			printf("%s\n", buffer);
136			fflush(stdout);
137		}
138#ifdef WIN32
139		OsmReportState(buffer);
140#endif				/* WIN32 */
141	}
142
143	/* regular log to default out_port */
144	cl_spinlock_acquire(&p_log->lock);
145
146	if (p_log->max_size && p_log->count > p_log->max_size) {
147		/* truncate here */
148		fprintf(stderr,
149			"osm_log: log file exceeds the limit %lu. Truncating.\n",
150			p_log->max_size);
151		truncate_log_file(p_log);
152	}
153#ifdef WIN32
154	GetLocalTime(&st);
155_retry:
156	ret =
157	    fprintf(p_log->out_port,
158		    "[%02d:%02d:%02d:%03d][%04X] 0x%02x -> %s",
159		    st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
160		    pid, verbosity, buffer);
161#else
162	time_usecs = cl_get_time_stamp();
163	tim = time_usecs / 1000000;
164	usecs = time_usecs % 1000000;
165	localtime_r(&tim, &result);
166	pid = pthread_self();
167_retry:
168	ret =
169	    fprintf(p_log->out_port,
170		    "%s %02d %02d:%02d:%02d %06d [%04X] 0x%02x -> %s",
171		    (result.tm_mon <
172		     12 ? month_str[result.tm_mon] : "???"),
173		    result.tm_mday, result.tm_hour, result.tm_min,
174		    result.tm_sec, usecs, pid, verbosity, buffer);
175#endif
176
177	/*  flush log */
178	if (ret > 0 &&
179	    (p_log->flush || (verbosity & (OSM_LOG_ERROR | OSM_LOG_SYS)))
180	    && fflush(p_log->out_port) < 0)
181		ret = -1;
182
183	if (ret >= 0) {
184		log_exit_count = 0;
185		p_log->count += ret;
186	} else if (log_exit_count < 3) {
187		log_exit_count++;
188		if (errno == ENOSPC && p_log->max_size) {
189			fprintf(stderr,
190				"osm_log: write failed: %s. Truncating log file.\n",
191				strerror(errno));
192			truncate_log_file(p_log);
193			goto _retry;
194		}
195		fprintf(stderr, "osm_log: write failed: %s\n", strerror(errno));
196	}
197
198	cl_spinlock_release(&p_log->lock);
199}
200
201void osm_log_raw(IN osm_log_t * const p_log,
202		 IN const osm_log_level_t verbosity, IN const char *p_buf)
203{
204	if (p_log->level & verbosity) {
205		cl_spinlock_acquire(&p_log->lock);
206		printf("%s", p_buf);
207		cl_spinlock_release(&p_log->lock);
208
209		/*
210		   Flush log on errors too.
211		 */
212		if (p_log->flush || (verbosity & OSM_LOG_ERROR))
213			fflush(stdout);
214	}
215}
216
217void osm_log_msg_box(IN osm_log_t * log, osm_log_level_t level,
218		     const char *func_name, const char *msg)
219{
220#define MSG_BOX_LENGTH 66
221	char buf[MSG_BOX_LENGTH + 1];
222	int i, n;
223
224	if (!osm_log_is_active(log, level))
225		return;
226
227	n = (MSG_BOX_LENGTH - strlen(msg)) / 2 - 1;
228	if (n < 0)
229		n = 0;
230	for (i = 0; i < n; i++)
231		sprintf(buf + i, "*");
232	n += snprintf(buf + n, sizeof(buf) - n, " %s ", msg);
233	for (i = n; i < MSG_BOX_LENGTH; i++)
234		buf[i] = '*';
235	buf[i] = '\0';
236
237	osm_log(log, level, "%s:\n\n\n"
238		"*********************************************"
239		"*********************\n%s\n"
240		"*********************************************"
241		"*********************\n\n\n", func_name, buf);
242}
243
244boolean_t osm_is_debug(void)
245{
246#if defined( _DEBUG_ )
247	return TRUE;
248#else
249	return FALSE;
250#endif				/* defined( _DEBUG_ ) */
251}
252
253static int open_out_port(IN osm_log_t * p_log)
254{
255	struct stat st;
256
257	if (p_log->accum_log_file)
258		p_log->out_port = fopen(p_log->log_file_name, "a+");
259	else
260		p_log->out_port = fopen(p_log->log_file_name, "w+");
261
262	if (!p_log->out_port) {
263		syslog(LOG_CRIT, "Cannot open file \'%s\' for %s: %s\n",
264		       p_log->log_file_name,
265		       p_log->accum_log_file ? "appending" : "writing",
266		       strerror(errno));
267		fprintf(stderr, "Cannot open file \'%s\': %s\n",
268			p_log->log_file_name, strerror(errno));
269		return -1;
270	}
271
272	if (fstat(fileno(p_log->out_port), &st) == 0)
273		p_log->count = st.st_size;
274
275	syslog(LOG_NOTICE, "%s log file opened\n", p_log->log_file_name);
276
277	if (p_log->daemon) {
278		dup2(fileno(p_log->out_port), 0);
279		dup2(fileno(p_log->out_port), 1);
280		dup2(fileno(p_log->out_port), 2);
281	}
282
283	return 0;
284}
285
286int osm_log_reopen_file(osm_log_t * p_log)
287{
288	int ret;
289
290	if (p_log->out_port == stdout || p_log->out_port == stderr)
291		return 0;
292	cl_spinlock_acquire(&p_log->lock);
293	fclose(p_log->out_port);
294	ret = open_out_port(p_log);
295	cl_spinlock_release(&p_log->lock);
296	return ret;
297}
298
299ib_api_status_t osm_log_init_v2(IN osm_log_t * const p_log,
300				IN const boolean_t flush,
301				IN const uint8_t log_flags,
302				IN const char *log_file,
303				IN const unsigned long max_size,
304				IN const boolean_t accum_log_file)
305{
306	p_log->level = log_flags;
307	p_log->flush = flush;
308	p_log->count = 0;
309	p_log->max_size = max_size;
310	p_log->accum_log_file = accum_log_file;
311	p_log->log_file_name = (char *)log_file;
312
313	openlog("OpenSM", LOG_CONS | LOG_PID, LOG_USER);
314
315	if (log_file == NULL || !strcmp(log_file, "-") ||
316	    !strcmp(log_file, "stdout"))
317		p_log->out_port = stdout;
318	else if (!strcmp(log_file, "stderr"))
319		p_log->out_port = stderr;
320	else if (open_out_port(p_log))
321		return IB_ERROR;
322
323	if (cl_spinlock_init(&p_log->lock) == CL_SUCCESS)
324		return IB_SUCCESS;
325	else
326		return IB_ERROR;
327}
328
329ib_api_status_t osm_log_init(IN osm_log_t * const p_log,
330			     IN const boolean_t flush,
331			     IN const uint8_t log_flags,
332			     IN const char *log_file,
333			     IN const boolean_t accum_log_file)
334{
335	return osm_log_init_v2(p_log, flush, log_flags, log_file, 0,
336			       accum_log_file);
337}
338