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 == NULL))
 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 }