xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_log.c (revision 86c48bbfeb72d5a6ee171e713059939bab658b77)
1 /*
2  * Copyright 2009 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 <libgen.h>
53 #include <pthread.h>
54 #include <errno.h>
55 #include "ndmpd_log.h"
56 #include "ndmpd.h"
57 #include "ndmpd_common.h"
58 
59 #define	LOG_FNAME	"ndmplog.%d"
60 #define	LOG_FILE_CNT	5
61 #define	LOG_FILE_SIZE	4 * 1024 * 1024
62 #define	LOG_SIZE_INT	256
63 
64 static boolean_t debug_level = 0;
65 static FILE *logfp;
66 static int ndmp_synclog = 1;
67 
68 
69 /*
70  * Since we use buffered file I/O for log file, the thread may lose CPU.
71  * At this time, another thread can destroy the contents of the buffer
72  * that must be written to the log file.  The following mutex is used
73  * to allow only one thread to write into the log file.
74  */
75 mutex_t log_lock;
76 mutex_t idx_lock;
77 
78 static char *priority_str[] = {
79 	"EMERGENCY",
80 	"ALERT",
81 	"CRITICAL",
82 	"ERROR",
83 	"WARNING",
84 	"NOTICE",
85 	"INFO",
86 	"DEBUG",
87 };
88 
89 
90 /*
91  * mk_pathname
92  *
93  * Append the NDMP working directory path to the specified file
94  */
95 static char *
96 mk_pathname(char *fname, char *path, int idx)
97 {
98 	static char buf[PATH_MAX];
99 	static char name[NAME_MAX];
100 	char *fmt;
101 	int len;
102 
103 	len = strlen(path);
104 	fmt = (path[len - 1] == '/') ? "%s%s" : "%s/%s";
105 
106 	/* LINTED variable format specifier */
107 	(void) snprintf(name, NAME_MAX, fname, idx);
108 
109 	/* LINTED variable format specifier */
110 	(void) snprintf(buf, PATH_MAX, fmt, path, name);
111 	return (buf);
112 }
113 
114 
115 /*
116  * openlogfile
117  *
118  * Open the NDMP log file
119  */
120 static int
121 openlogfile(char *fname, char *mode)
122 {
123 	int rv;
124 
125 	if (fname == NULL || *fname == '\0' || mode == NULL || *mode == '\0')
126 		return (-1);
127 
128 	(void) mutex_lock(&log_lock);
129 	rv = 0;
130 	if (logfp != NULL) {
131 		NDMP_LOG(LOG_DEBUG, "Log file already opened.");
132 		rv = -1;
133 	} else if ((logfp = fopen(fname, mode)) == NULL) {
134 		syslog(LOG_ERR, "Error opening logfile %s, %m.", fname);
135 		syslog(LOG_ERR, "Using system log for logging.");
136 		rv = -1;
137 	}
138 
139 	(void) mutex_unlock(&log_lock);
140 	return (rv);
141 }
142 
143 
144 /*
145  * log_write_cur_time
146  *
147  * Add the current time for each log entry
148  */
149 static void
150 log_write_cur_time(void)
151 {
152 	struct tm tm;
153 	time_t secs;
154 
155 	secs = time(NULL);
156 	(void) localtime_r(&secs, &tm);
157 	(void) fprintf(logfp, "%2d/%02d %2d:%02d:%02d ",
158 	    tm.tm_mon + 1, tm.tm_mday,
159 	    tm.tm_hour, tm.tm_min, tm.tm_sec);
160 }
161 
162 
163 /*
164  * add_newline
165  *
166  * The new line at the end of each log
167  */
168 static void
169 add_newline(char *fmt)
170 {
171 	if (fmt[strlen(fmt) - 1] != '\n')
172 		(void) fputc('\n', logfp);
173 }
174 
175 
176 /*
177  * log_append
178  *
179  * Append the message to the end of the log
180  */
181 static void
182 log_append(char *msg)
183 {
184 	log_write_cur_time();
185 	(void) fwrite(msg, 1, strlen(msg), logfp);
186 	add_newline(msg);
187 	if (ndmp_synclog)
188 		(void) fflush(logfp);
189 }
190 
191 
192 /*
193  * ndmp_log_openfile
194  *
195  * Open the log file either for append or write mode.
196  */
197 int
198 ndmp_log_open_file(void)
199 {
200 	char *fname, *mode;
201 	char oldfname[PATH_MAX];
202 	char *lpath;
203 	struct stat64 st;
204 	int i;
205 
206 	/* Create the debug path if doesn't exist */
207 	lpath = ndmpd_get_prop(NDMP_DEBUG_PATH);
208 	if ((lpath == NULL) || (*lpath == NULL))
209 		lpath = "/var/ndmp";
210 
211 	if (stat64(lpath, &st) < 0) {
212 		if (mkdirp(lpath, 0755) < 0) {
213 			NDMP_LOG(LOG_ERR, "Could not create log path %s: %m.",
214 			    lpath);
215 			lpath = "/var";
216 		}
217 	}
218 
219 	/*
220 	 * NDMP log file name will be {logfilename}.0 to {logfilename}.5, where
221 	 * {logfilename}.0 will always be the latest and the {logfilename}.5
222 	 * will be the oldest available file on the system. We keep maximum of 5
223 	 * log files. With the new session the files are shifted to next number
224 	 * and if the last file {logfilename}.5 exist, it will be overwritten
225 	 * with {logfilename}.4.
226 	 */
227 	if (get_debug_level()) {
228 		i = LOG_FILE_CNT - 1;
229 		while (i >= 0) {
230 			fname = mk_pathname(LOG_FNAME, lpath, i);
231 			(void) strncpy(oldfname, fname, PATH_MAX);
232 			if (stat64(oldfname, &st) == -1) {
233 				i--;
234 				continue;
235 			}
236 
237 			fname = mk_pathname(LOG_FNAME, lpath, i + 1);
238 			if (rename(oldfname, fname))
239 				syslog(LOG_DEBUG,
240 				    "Could not rename from %s to %s",
241 				    oldfname, fname);
242 			i--;
243 		}
244 	}
245 
246 	fname = mk_pathname(LOG_FNAME, lpath, 0);
247 
248 	/*
249 	 * Append only if debug is not enable.
250 	 */
251 	if (get_debug_level())
252 		mode = "w";
253 	else
254 		mode = "a";
255 
256 	return (openlogfile(fname, mode));
257 }
258 
259 /*
260  * ndmp_log_close_file
261  *
262  * Close the log file
263  */
264 void
265 ndmp_log_close_file(void)
266 {
267 	(void) mutex_lock(&log_lock);
268 	if (logfp != NULL) {
269 		(void) fclose(logfp);
270 		logfp = NULL;
271 	}
272 	(void) mutex_unlock(&log_lock);
273 }
274 
275 /*
276  * set_debug_level
277  *
278  * Sets the current debug level.
279  * Parameters:
280  *   level (input) - new debug level.
281  *
282  * Returns:
283  *   old debug level.
284  */
285 boolean_t
286 set_debug_level(boolean_t level)
287 {
288 	boolean_t old = debug_level;
289 
290 	debug_level = level;
291 	return (old);
292 }
293 
294 
295 /*
296  * get_debug_level
297  *
298  * Returns the current debug level.
299  *
300  * Parameters:
301  *   None.
302  *
303  * Returns:
304  *   debug level.
305  */
306 boolean_t
307 get_debug_level(void)
308 {
309 	return (debug_level);
310 }
311 
312 void
313 ndmp_log(ulong_t priority, char *ndmp_log_info, char *fmt, ...)
314 {
315 	int c;
316 	va_list args;
317 	char *f, *b;
318 	char ndmp_log_buf[PATH_MAX+KILOBYTE];
319 	char ndmp_syslog_buf[PATH_MAX+KILOBYTE];
320 	char buf[PATH_MAX+KILOBYTE];
321 	char *errstr;
322 
323 	if ((priority == LOG_DEBUG) && (debug_level == FALSE))
324 		return;
325 
326 	(void) mutex_lock(&log_lock);
327 
328 	if (priority > 7)
329 		priority = LOG_ERR;
330 
331 	va_start(args, fmt);
332 	/* Replace text error messages if fmt contains %m */
333 	b = buf;
334 	f = fmt;
335 	while (((c = *f++) != '\0') && (c != '\n') &&
336 	    (b < &buf[PATH_MAX+KILOBYTE])) {
337 		if (c != '%') {
338 			*b++ = c;
339 			continue;
340 		}
341 		if ((c = *f++) != 'm') {
342 			*b++ = '%';
343 			*b++ = c;
344 			continue;
345 		}
346 
347 		if ((errstr = strerror(errno)) == NULL) {
348 			(void) snprintf(b, &buf[PATH_MAX+KILOBYTE] - b,
349 			    "error %d", errno);
350 		} else {
351 			while ((*errstr != '\0') &&
352 			    (b < &buf[PATH_MAX+KILOBYTE])) {
353 				if (*errstr == '%') {
354 					(void) strncpy(b, "%%", 2);
355 					b += 2;
356 				} else {
357 					*b++ = *errstr;
358 				}
359 				errstr++;
360 			}
361 			*b = '\0';
362 		}
363 		b += strlen(b);
364 	}
365 	*b = '\0';
366 
367 	/* LINTED variable format specifier */
368 	(void) vsnprintf(ndmp_syslog_buf, sizeof (ndmp_syslog_buf), buf, args);
369 	va_end(args);
370 
371 	/* Send all logs other than debug, to syslog log file. */
372 	if (priority != LOG_DEBUG)
373 		syslog(priority, "%s", ndmp_syslog_buf);
374 
375 	/* ndmp_log_buf will have priority string and log info also */
376 	(void) snprintf(ndmp_log_buf, sizeof (ndmp_log_buf), "%s: %s:%s",
377 	    priority_str[priority], ndmp_log_info, ndmp_syslog_buf);
378 
379 	if (logfp != NULL)
380 		log_append(ndmp_log_buf);
381 
382 	(void) mutex_unlock(&log_lock);
383 }
384