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