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