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 char ndmp_log_info[256]; 69 static boolean_t debug = B_FALSE; 70 static boolean_t log_to_stderr = B_FALSE; 71 static FILE *logfp; 72 static int ndmp_synclog = 1; 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