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 *
mk_pathname(char * fname,char * path,int idx)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
openlogfile(char * fname,char * mode)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
log_write_cur_time(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
add_newline(char * fmt)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
log_append(char * msg)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
ndmp_log_open_file(void)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
ndmp_log_close_file(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
set_debug_level(boolean_t level)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
get_debug_level(void)307 get_debug_level(void)
308 {
309 return (debug_level);
310 }
311
312 void
ndmp_log(ulong_t priority,char * ndmp_log_info,char * fmt,...)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