xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_log.c (revision 2654012f83cec5dc15b61dfe3e4a4915f186e7a6)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * BSD 3 Clause License
8  *
9  * Copyright (c) 2007, The Storage Networking Industry Association.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 	- Redistributions of source code must retain the above copyright
15  *	  notice, this list of conditions and the following disclaimer.
16  *
17  * 	- Redistributions in binary form must reproduce the above copyright
18  *	  notice, this list of conditions and the following disclaimer in
19  *	  the documentation and/or other materials provided with the
20  *	  distribution.
21  *
22  *	- Neither the name of The Storage Networking Industry Association (SNIA)
23  *	  nor the names of its contributors may be used to endorse or promote
24  *	  products derived from this software without specific prior written
25  *	  permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 /* Copyright (c) 2007, The Storage Networking Industry Association. */
40 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
41 
42 #include <errno.h>
43 #include <limits.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <syslog.h>
48 #include <time.h>
49 #include <string.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <pthread.h>
53 #include <errno.h>
54 #include "ndmpd_log.h"
55 #include "ndmpd.h"
56 #include "ndmpd_common.h"
57 
58 #define	LOG_FNAME	"ndmplog.%d"
59 #define	LOG_FILE_CNT	5
60 #define	LOG_FILE_SIZE	4 * 1024 * 1024
61 #define	LOG_SIZE_INT	256
62 
63 static boolean_t debug_level = 0;
64 static FILE *logfp;
65 static int ndmp_synclog = 1;
66 
67 
68 /*
69  * Since we use buffered file I/O for log file, the thread may lose CPU.
70  * At this time, another thread can destroy the contents of the buffer
71  * that must be written to the log file.  The following mutex is used
72  * to allow only one thread to write into the log file.
73  */
74 mutex_t log_lock;
75 mutex_t idx_lock;
76 
77 static char *priority_str[] = {
78 	"EMERGENCY",
79 	"ALERT",
80 	"CRITICAL",
81 	"ERROR",
82 	"WARNING",
83 	"NOTICE",
84 	"INFO",
85 	"DEBUG",
86 };
87 
88 
89 /*
90  * mk_pathname
91  *
92  * Append the NDMP working directory path to the specified file
93  */
94 static char *
95 mk_pathname(char *fname, int idx)
96 {
97 	static char buf[PATH_MAX];
98 	static char name[NAME_MAX];
99 	char *path, *fmt;
100 	int len;
101 
102 	path = ndmpd_get_prop(NDMP_DEBUG_PATH);
103 	if (!path || *path == 0)
104 		return (NULL);
105 
106 	len = strlen(path);
107 	fmt = (path[len - 1] == '/') ? "%s%s" : "%s/%s";
108 
109 	/* LINTED variable format specifier */
110 	(void) snprintf(name, NAME_MAX, fname, idx);
111 
112 	/* LINTED variable format specifier */
113 	(void) snprintf(buf, PATH_MAX, fmt, path, name);
114 	return (buf);
115 }
116 
117 
118 /*
119  * openlogfile
120  *
121  * Open the NDMP log file
122  */
123 static int
124 openlogfile(char *fname, char *mode)
125 {
126 	int rv;
127 
128 	if (fname == NULL || *fname == '\0' || mode == NULL || *mode == '\0')
129 		return (-1);
130 
131 	(void) mutex_lock(&log_lock);
132 	rv = 0;
133 	if (logfp != NULL) {
134 		NDMP_LOG(LOG_DEBUG, "Log file already opened.");
135 		rv = -1;
136 	} else if ((logfp = fopen(fname, mode)) == NULL) {
137 		syslog(LOG_ERR, "Error opening logfile %s, %m.", fname);
138 		syslog(LOG_ERR, "Using system log for logging.");
139 		rv = -1;
140 	}
141 
142 	(void) mutex_unlock(&log_lock);
143 	return (rv);
144 }
145 
146 
147 /*
148  * log_write_cur_time
149  *
150  * Add the current time for each log entry
151  */
152 static void
153 log_write_cur_time(void)
154 {
155 	struct tm tm;
156 	time_t secs;
157 
158 	secs = time(NULL);
159 	(void) localtime_r(&secs, &tm);
160 	(void) fprintf(logfp, "%2d/%02d %2d:%02d:%02d ",
161 	    tm.tm_mon + 1, tm.tm_mday,
162 	    tm.tm_hour, tm.tm_min, tm.tm_sec);
163 }
164 
165 
166 /*
167  * add_newline
168  *
169  * The new line at the end of each log
170  */
171 static void
172 add_newline(char *fmt)
173 {
174 	if (fmt[strlen(fmt) - 1] != '\n')
175 		(void) fputc('\n', logfp);
176 }
177 
178 
179 /*
180  * log_append
181  *
182  * Append the message to the end of the log
183  */
184 static void
185 log_append(char *msg)
186 {
187 	log_write_cur_time();
188 	(void) fwrite(msg, 1, strlen(msg), logfp);
189 	add_newline(msg);
190 	if (ndmp_synclog)
191 		(void) fflush(logfp);
192 }
193 
194 
195 /*
196  * ndmp_log_openfile
197  *
198  * Open the log file either for append or write mode.
199  */
200 int
201 ndmp_log_open_file(void)
202 {
203 	char *fname, *mode;
204 	char oldfname[PATH_MAX];
205 	struct stat st;
206 	int i;
207 
208 	/*
209 	 * NDMP log file name will be {logfilename}.0 to {logfilename}.5, where
210 	 * {logfilename}.0 will always be the latest and the {logfilename}.5
211 	 * will be the oldest available file on the system. We keep maximum of 5
212 	 * log files. With the new session the files are shifted to next number
213 	 * and if the last file {logfilename}.5 exist, it will be overwritten
214 	 * with {logfilename}.4.
215 	 */
216 	if (get_debug_level()) {
217 		i = LOG_FILE_CNT - 1;
218 		while (i >= 0) {
219 			fname = mk_pathname(LOG_FNAME, i);
220 			(void) strncpy(oldfname, fname, PATH_MAX);
221 			if (stat(oldfname, &st) == -1) {
222 				i--;
223 				continue;
224 			}
225 
226 			fname = mk_pathname(LOG_FNAME, i + 1);
227 			if (rename(oldfname, fname))
228 				syslog(LOG_DEBUG,
229 				    "Could not rename from %s to %s",
230 				    oldfname, fname);
231 			i--;
232 		}
233 	}
234 
235 	fname = mk_pathname(LOG_FNAME, 0);
236 
237 	/*
238 	 * Append only if debug is not enable.
239 	 */
240 	if (get_debug_level())
241 		mode = "w";
242 	else
243 		mode = "a";
244 
245 	return (openlogfile(fname, mode));
246 }
247 
248 /*
249  * ndmp_log_close_file
250  *
251  * Close the log file
252  */
253 void
254 ndmp_log_close_file(void)
255 {
256 	(void) mutex_lock(&log_lock);
257 	if (logfp != NULL) {
258 		(void) fclose(logfp);
259 		logfp = NULL;
260 	}
261 	(void) mutex_unlock(&log_lock);
262 }
263 
264 /*
265  * set_debug_level
266  *
267  * Sets the current debug level.
268  * Parameters:
269  *   level (input) - new debug level.
270  *
271  * Returns:
272  *   old debug level.
273  */
274 boolean_t
275 set_debug_level(boolean_t level)
276 {
277 	boolean_t old = debug_level;
278 
279 	debug_level = level;
280 	return (old);
281 }
282 
283 
284 /*
285  * get_debug_level
286  *
287  * Returns the current debug level.
288  *
289  * Parameters:
290  *   None.
291  *
292  * Returns:
293  *   debug level.
294  */
295 boolean_t
296 get_debug_level(void)
297 {
298 	return (debug_level);
299 }
300 
301 void
302 ndmp_log(ulong_t priority, char *ndmp_log_info, char *fmt, ...)
303 {
304 	int c;
305 	va_list args;
306 	char *f, *b;
307 	char ndmp_log_buf[PATH_MAX+KILOBYTE];
308 	char ndmp_syslog_buf[PATH_MAX+KILOBYTE];
309 	char buf[PATH_MAX+KILOBYTE];
310 	char *errstr;
311 
312 	if ((priority == LOG_DEBUG) && (debug_level == FALSE))
313 		return;
314 
315 	(void) mutex_lock(&log_lock);
316 
317 	if (priority > 7)
318 		priority = LOG_ERR;
319 
320 	va_start(args, fmt);
321 	/* Replace text error messages if fmt contains %m */
322 	b = buf;
323 	f = fmt;
324 	while (((c = *f++) != '\0') && (c != '\n') &&
325 	    (b < &buf[PATH_MAX+KILOBYTE])) {
326 		if (c != '%') {
327 			*b++ = c;
328 			continue;
329 		}
330 		if ((c = *f++) != 'm') {
331 			*b++ = '%';
332 			*b++ = c;
333 			continue;
334 		}
335 
336 		if ((errstr = strerror(errno)) == NULL) {
337 			(void) snprintf(b, &buf[PATH_MAX+KILOBYTE] - b,
338 			    "error %d", errno);
339 		} else {
340 			while ((*errstr != '\0') &&
341 			    (b < &buf[PATH_MAX+KILOBYTE])) {
342 				if (*errstr == '%') {
343 					(void) strncpy(b, "%%", 2);
344 					b += 2;
345 				} else {
346 					*b++ = *errstr;
347 				}
348 				errstr++;
349 			}
350 			*b = '\0';
351 		}
352 		b += strlen(b);
353 	}
354 	*b = '\0';
355 
356 	/* LINTED variable format specifier */
357 	(void) vsnprintf(ndmp_syslog_buf, sizeof (ndmp_syslog_buf), buf, args);
358 	va_end(args);
359 
360 	/* Send all logs other than debug, to syslog log file. */
361 	if (priority != LOG_DEBUG)
362 		syslog(priority, "%s", ndmp_syslog_buf);
363 
364 	/* ndmp_log_buf will have priority string and log info also */
365 	(void) snprintf(ndmp_log_buf, sizeof (ndmp_log_buf), "%s: %s:%s",
366 	    priority_str[priority], ndmp_log_info, ndmp_syslog_buf);
367 
368 	if (logfp != NULL)
369 		log_append(ndmp_log_buf);
370 
371 	(void) mutex_unlock(&log_lock);
372 }
373