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