Logo Search packages:      
Sourcecode: jftpgw version File versions  Download package

log.c

/*  
 * Copyright (C) 1999-2004 Joachim Wieland <joe@mcknight.de>
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111, USA.
 */

/*  ----- from tinyproxy for jftpgw
 *
 * Logs the various messages which tinyproxy produces to either a log file or
 * the syslog daemon. Not much to it...
 *
 * Copyright (C) 1998  Steven Young
 * Copyright (C) 1999  Robert James Kaes (rjkaes@flarenet.com)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * log.c - For the manipulation of log files.
 */

#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <syslog.h>
#include <stdlib.h>
#include <unistd.h>

#include "jftpgw.h"
#include "log.h"

#define LENGTH 64
#define LOGSIZE 800

#ifndef HAVE_SNPRINTF
#   include "snprintf.c"
#else
#   ifndef HAVE_VSNPRINTF
#     include "snprintf.c"
#   endif
#endif

struct loginfo_st loginfo;
struct serverinfo srvinfo;
static struct loginfo_st* loginfo_bk;

/*
 * This routine logs messages to either the log file or the syslog function.
 */
void jlog(int level, const char *fmt, ...)
{
      va_list args;
      time_t nowtime;
      FILE *cf;

      static char time_string[LENGTH];
      static char str[LOGSIZE];

      if (level > loginfo.debuglevel) {
            return;
      }

      /* we may dump the log to stderr if we have not yet
       * opened a log file or the syslog */
      /* but don't log if we are run by inetd */
      if (!loginfo.logf
            && !loginfo.syslog
            && srvinfo.servertype == SERVERTYPE_INETD
            && level > 5) {

            return;
      }

      va_start(args, fmt);
      if (!loginfo.syslog) {
            /* log via files */
            nowtime = time(NULL);
            /* Format is month day hour:minute:second (24 time) */
            strftime(time_string, LENGTH, "%b %d %H:%M:%S", localtime(&nowtime));
            if (!(cf = loginfo.logf)) {
                  cf = stderr;
            }

            fprintf(cf, "%s [%ld]: ", time_string, (long int) getpid());
            vfprintf(cf, fmt, args);
            fprintf(cf, "\n");
            fflush(cf);
      } else {
            int logtype = LOG_DEBUG;
            if (level < 8) {
                  logtype = LOG_INFO;
            }
            if (level < 6) {
                  logtype = LOG_WARNING;
            }
            if (level < 4) {
                  logtype = LOG_ERR;
            }
            vsnprintf(str, LOGSIZE - 1, fmt, args);
            syslog(logtype, "%s", str);
      }

      va_end(args);
}




#define NUMBER_BUFFER   30
char* conv_int2char(signed int i) {
      char* s = (char*) malloc( NUMBER_BUFFER );
      enough_mem(s);
      snprintf(s, NUMBER_BUFFER, "%d", i);
      return s;
}

char* conv_uint2char(unsigned int i) {
      return conv_int2char(i);
}

char* conv_lint2char(signed long int i) {
      char* s = (char*) malloc( NUMBER_BUFFER );
      enough_mem(s);
      snprintf(s, NUMBER_BUFFER, "%ld", i);
      return s;
}

char* conv_luint2char(unsigned long int i) {
      return conv_lint2char(i);
}

char* conv_float2char(float f) {
      char* s = (char*) malloc( NUMBER_BUFFER );
      enough_mem(s);
      snprintf(s, NUMBER_BUFFER, "%.2f", f);
      return s;
}

const char* base_name(const char* s) {
      const char* r, *t;

      if (!s) {
            return "(null)";
      }
      t = r = s;
      while ((t = strchr(t, '/'))) {
            t++;
            r = t;
      }
      return r;
}


#define LOG_REPLACE_STRING       1
#define LOG_REPLACE_CHAR         2
#define LOG_REPLACE_UINT         3
#define LOG_REPLACE_LUINT        4
#define LOG_REPLACE_INT          5
#define LOG_REPLACE_LINT         6
#define LOG_REPLACE_FLOAT        7

char* log_replace_char(const char pattern, struct log_cmd_st* lcs) {
      union {
            char* replace_str;
            char replace_char;
            unsigned int replace_uint;
            unsigned long int replace_luint;
            signed int replace_int;
            signed long int replace_lint;
            float replace_float;
      } replace_val;
      int replace_type;

      switch (pattern) {
            case 'c': /* complete */
                  replace_val.replace_char = lcs->complete ? 'c' : 'i';
                  replace_type = LOG_REPLACE_CHAR;
                  break;
            case 'D': /* common log time/date: [12/Feb/2003:13:34:50 +0100] */
                  {
                        time_t nowtime = time(NULL);
                        replace_val.replace_str = malloc(100);
                        enough_mem(replace_val.replace_str);
                        /* XXX %z is a GNU extension */
                        strftime(replace_val.replace_str, 100,
                              "[%d/%b/%Y:%H:%M:%S %z]",
                              localtime(&nowtime));
                        replace_type = LOG_REPLACE_STRING;
                  }
                  break;
            case 'T': /* Time taken to transmit/receive file, in seconds */
                  replace_val.replace_uint = lcs->transfer_duration;
                  replace_type = LOG_REPLACE_UINT;
                  break;
            case 't': /* date/time like Wed Feb 14 01:41:28 2001 */
                  {
                        time_t nowtime = time(NULL);
                        replace_val.replace_str = malloc(100);
                        enough_mem(replace_val.replace_str);
                        strftime(replace_val.replace_str, 100,
                              "%a %b %d %H:%M:%S %Y",
                              localtime(&nowtime));
                        replace_type = LOG_REPLACE_STRING;
                  }
                  break;
            case 'b': /* Bytes sent for request */
                  replace_val.replace_luint = lcs->transferred;
                  replace_type = LOG_REPLACE_LUINT;
                  break;
            case 'R': /* throughput rate in kbyte/s */
                  if (lcs->transfer_duration) {
                        replace_val.replace_float =
                              (lcs->transferred / 1024) /
                                    lcs->transfer_duration;
                        replace_type = LOG_REPLACE_FLOAT;
                  } else {
                        replace_val.replace_char = '-';
                        replace_type = LOG_REPLACE_CHAR;
                  }
                  break;
            case 'f': /* Filename stored or retrieved, absolute path */
                  replace_val.replace_str = strfilldup(lcs->filename, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'F':
                  /* Filename stored or retrieved, as the client sees
                   * it base_name is just a pointer within lcs->filename
                   * */
                  replace_val.replace_str = strfilldup(base_name(lcs->filename), "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'm': /* Command (method) name received from client,
                       e.g., RETR */
                  replace_val.replace_str = strfilldup(lcs->method, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'r': /* full commandline */
                  if (lcs && lcs->cmd && *(lcs->cmd) &&
                              checkbegin(lcs->cmd, "PASS")) {
                        replace_val.replace_str = strdup("PASS *");
                  } else {
                        replace_val.replace_str = strfilldup(lcs->cmd, "-");
                  }
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'P': /* pid */
                  replace_val.replace_uint = getpid();
                  replace_type = LOG_REPLACE_UINT;
                  break;
            case 's': /*  Numeric FTP response code (status) */
                  replace_val.replace_int = lcs->respcode;
                  replace_type = LOG_REPLACE_INT;
                  break;
            case 'y': /* tYpe */
                  replace_val.replace_char = lcs->type;
                  replace_type = LOG_REPLACE_CHAR;
                  break;
            case 'w':  /* direction */
                  replace_val.replace_char = lcs->direction;
                  replace_type = LOG_REPLACE_CHAR;
                  break;
            case 'o':  /* anonymous? */
                  /* logged in ? */
                  if (!lcs->userlogin) {
                        replace_val.replace_char = '-';
                        replace_type = LOG_REPLACE_CHAR;
                        break;
                  }
                  replace_val.replace_char =
                           strcmp(lcs->userlogin, "anonymous") == 0
                        || strcmp(lcs->userlogin, "ftp") == 0 ? 'a':'r';
                  replace_type = LOG_REPLACE_CHAR;
                  break;
            case 'e': /* sErvice */
                  replace_val.replace_str = strfilldup(lcs->service, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'n': /* aNon-user */
                  if (!lcs->userlogin
                        || strcmp(lcs->userlogin, "anonymous") == 0
                        || strcmp(lcs->userlogin, "ftp") == 0) {

                        replace_val.replace_str
                                    = strfilldup(lcs->anon_user, "-");
                        enough_mem(replace_val.replace_str);
                  } else {
                        /* Server user name (login) */
                        replace_val.replace_str
                                    = strfilldup(lcs->userlogin, "-");
                        enough_mem(replace_val.replace_str);
                  }
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'H': /* Server host name */
                  replace_val.replace_str = strfilldup(lcs->svrname, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'A': /* Server host IP */
                  replace_val.replace_str = strfilldup(lcs->svrip, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'd': /*  Server host name as specified in the login  */
                  replace_val.replace_str = strfilldup(lcs->svrlogin, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'h': /* Client host name */
                  replace_val.replace_str = strfilldup(lcs->clntname, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'a': /* Client host IP */
                  replace_val.replace_str = strfilldup(lcs->clntip, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'I': /* Server interface address */
                  replace_val.replace_str = strfilldup(lcs->ifipsvr, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'i': /* Client interface address */
                  replace_val.replace_str = strfilldup(lcs->ifipclnt, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'l': /* Server user name (login) */
                  replace_val.replace_str = strfilldup(lcs->userlogin, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'L': /* Effective server user name */
                  replace_val.replace_str = strfilldup(lcs->usereffective, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'C': /* Forwarded server user name */
                  replace_val.replace_str = strfilldup(lcs->userforwarded, "-");
                  enough_mem(replace_val.replace_str);
                  replace_type = LOG_REPLACE_STRING;
                  break;
            case 'u': /* unix time, seconds since 1970 */
                  replace_val.replace_luint =
                              (unsigned long int) time(NULL);
                  replace_type = LOG_REPLACE_LUINT;
                  break;
            case 'U': /* unix time, seconds since 1970 with milliseconds behind */
                  {
                        struct timeval tv;

                        replace_val.replace_str = malloc(100);
                        if (gettimeofday(&tv, NULL) < 0) {
                              tv.tv_sec = time(NULL);
                              tv.tv_usec = 0;
                        }
                        snprintf(replace_val.replace_str, 100,
                                    "%lu.%03lu",
                                    tv.tv_sec,
                                    (tv.tv_usec / 1000));
                        replace_type = LOG_REPLACE_STRING;
                  }
                  break;
            case '%': /* percent sign */
                  replace_val.replace_char = '%';
                  replace_type = LOG_REPLACE_CHAR;
                  break;
            default:
                  return (char*) 0;
      }

      switch(replace_type) {
            char* s;

            case LOG_REPLACE_STRING:
                  if (replace_val.replace_str) {
                        s = replace_val.replace_str;
                  } else {
                        s = strdup("");
                        enough_mem(s);
                  }
                  return s;
            case LOG_REPLACE_CHAR:
                  s = (char*) malloc(2);
                  enough_mem(s);
                  s[0] = replace_val.replace_char;
                  s[1] = '\0';
                  return s;
            case LOG_REPLACE_INT:
                  return conv_int2char(replace_val.replace_int);
            case LOG_REPLACE_UINT:
                  return conv_uint2char(replace_val.replace_uint);
            case LOG_REPLACE_LINT:
                  return conv_lint2char(replace_val.replace_lint);
            case LOG_REPLACE_LUINT:
                  return conv_luint2char(replace_val.replace_luint);
            case LOG_REPLACE_FLOAT:
                  return conv_float2char(replace_val.replace_float);
      }
      return (char*) 0;
}

char* log_replace_line(const char* line, struct log_cmd_st* lcs) {

      char* replaced_line = (char*) 0;
      char* fragment;
      char* insert;
      int offset = 0;

      /* split up line */

      /* line:  blabla %u blubb %h bla */

      if (line[0] == '%') {
            replaced_line = log_replace_char(line[1], lcs);
            offset = 2;
      }
      while ((fragment = quotstrtok(line, "%", &offset))) {
            if ( ! replaced_line ) {
                  replaced_line = fragment;
            } else {
                  replaced_line = realloc(replaced_line,
                        strlen(replaced_line) + strlen(fragment) + 1);
                  enough_mem(replaced_line);
                  strcat(replaced_line, fragment);
            }
            /* the key is at line[offset+1]; */
            offset++;
            insert = log_replace_char(line[offset], lcs);
            if ( ! insert ) {
                  insert = strdup("<%x not found>");
                  enough_mem(insert);
                  /* replace `x' above */
                  insert[2] = line[offset];
            }
            offset++;
            replaced_line = realloc(replaced_line,
                        strlen(replaced_line) + strlen(insert) + 1);
            enough_mem(replaced_line);
            strcat(replaced_line, insert);
            free(insert);
      }
      return replaced_line;
}



/* Idea by Bernd Eckenfels <ecki@lina.inka.de>
 * check of the command 'needle' is in the list of specified 
 * commands 'haystack'. Haystack may contain '*' which is a match-all
 * criteria */
int incommandpattern(const char *haystack, const char *needle) {
      char* negation = char_prepend(" ", needle);
      /* we now have "  NEEDLE " */
      negation[1] = '-';
      /* we now have " -NEEDLE " */

      if (strstr(haystack, negation)) {
            free(negation);
            return 0;
      }
      free(negation);

      if (strstr(haystack, " * ")) {
            return 1;
      }
      if (strstr(haystack, needle)) {
            return 1;
      }
      return 0;
}


void log_cmd_ent(struct cmdlogent_t* lent, struct log_cmd_st* lcs) {

      char* commandpattern;
      size_t commandpatternsize;
      char* ws;

      commandpatternsize = strlen(lcs->cmd) + 3;
      commandpattern = (char*) malloc(commandpatternsize);
      enough_mem(commandpattern);

      if ((ws = strpbrk(lcs->cmd, " \t")) == NULL) {
            /* Command consisting of a single word */
            snprintf(commandpattern, commandpatternsize, " %s ", lcs->cmd);
      } else {
            commandpattern[0] = ' ';
            strncpy(&commandpattern[1], lcs->cmd, ws - lcs->cmd);
            commandpattern[ws-lcs->cmd+1] = ' ';
            commandpattern[ws-lcs->cmd+2] = '\0';
      }
      toupstr(commandpattern);

      if (incommandpattern(lent->specs, commandpattern)) {
            /* found, log it */
            if (strcmp(lent->style, "commonlog") == 0) {
                  const char* line_pattern =
                        "%A %n %l %D \"%m\" %s %b";
                  char* replaced = log_replace_line(line_pattern, lcs);
                  fprintf(lent->logf, "%s\n", replaced);
                  free(replaced);
            } else if (strcmp(lent->style, "xferlog") == 0) {
                  const char* line_pattern =
                        "%t %T %d %b \"%f\" %y _ %w %o %n %e 0 * %c";
                  char* replaced = log_replace_line(line_pattern, lcs);
                  fprintf(lent->logf, "%s\n", replaced);
                  free(replaced);
            } else {
                  char* line_pattern = lent->style;
                  char* replaced;

                  replaced = log_replace_line(line_pattern, lcs);
                  fprintf(lent->logf, "%s\n", replaced);
                  free(replaced);
            }
            fflush(lent->logf);
      }
      free(commandpattern);
}



void log_cmd(struct log_cmd_st* lcs) {

      /* go through the specifications of the logfiles and write
       * them if they match */

      struct cmdlogent_t* files = loginfo.cmdlogfiles;
      struct cmdlogent_t* dirs  = loginfo.cmdlogdirs;

      while (files && files->logf_name) {
            log_cmd_ent(files, lcs);
            files = files->next;
      }

      while (dirs && dirs->logf_name) {
            log_cmd_ent(dirs, lcs);
            dirs = dirs->next;
      }
      lcs->filename = (char*) 0;
}

static
int log_name_exists(const struct cmdlogent_t* cl, const char* needle) {
      while (cl) {
            if (strcmp(cl->logf_name, needle) == 0) {
                  return 1;
            }
            cl = cl->next;
      }
      return 0;
}

static
int log_init_debuglevel() {
      int debuglevel;
      const char* dbl;

      if (!(dbl = config_get_option("debuglevel"))) {
            jlog(4, "No debug level specified. Using level 7");
            debuglevel = 7;
      } else {
            errno = 0;
            /* with conv_char2* we wouldn't be able to log a warning */
            debuglevel = strtol(dbl, (char**) 0, 10);
            if (errno || debuglevel < 0 || debuglevel > 9) {
                  jlog(4, "Invalid debuglevel specified: \"%s\". Using "
                        "level 7", dbl);
                  debuglevel = 7;
            }
      }
      return debuglevel;
}

static
int log_init_syslog(struct loginfo_st* cfg) {
      const char* log_facility_opt = config_get_option("syslogfacility");
      int log_facility;

      cfg->syslog_facility = strdup(log_facility_opt);
      enough_mem(cfg->syslog_facility);

      if (0) {
#ifdef HAVE_LOG_FACILITY_LOG_AUTH
      } else if (strcasecmp(log_facility_opt, "auth") == 0) {
            log_facility = LOG_AUTH;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_AUTHPRIV
      } else if (strcasecmp(log_facility_opt, "authpriv") == 0) {
            log_facility = LOG_AUTHPRIV;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_CRON
      } else if (strcasecmp(log_facility_opt, "cron") == 0) {
            log_facility = LOG_CRON;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_DAEMON
      } else if (strcasecmp(log_facility_opt, "daemon") == 0) {
            log_facility = LOG_DAEMON;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_FTP
      } else if (strcasecmp(log_facility_opt, "ftp") == 0) {
            log_facility = LOG_FTP;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_KERN
      } else if (strcasecmp(log_facility_opt, "kern") == 0) {
            log_facility = LOG_KERN;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL0
      } else if (strcasecmp(log_facility_opt, "local0") == 0) {
            log_facility = LOG_LOCAL0;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL1
      } else if (strcasecmp(log_facility_opt, "local1") == 0) {
            log_facility = LOG_LOCAL1;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL2
      } else if (strcasecmp(log_facility_opt, "local2") == 0) {
            log_facility = LOG_LOCAL2;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL3
      } else if (strcasecmp(log_facility_opt, "local3") == 0) {
            log_facility = LOG_LOCAL3;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL4
      } else if (strcasecmp(log_facility_opt, "local4") == 0) {
            log_facility = LOG_LOCAL4;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL5
      } else if (strcasecmp(log_facility_opt, "local5") == 0) {
            log_facility = LOG_LOCAL5;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL6
      } else if (strcasecmp(log_facility_opt, "local6") == 0) {
            log_facility = LOG_LOCAL6;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LOCAL7
      } else if (strcasecmp(log_facility_opt, "local7") == 0) {
            log_facility = LOG_LOCAL7;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_LPR
      } else if (strcasecmp(log_facility_opt, "lpr") == 0) {
            log_facility = LOG_LPR;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_MAIL
      } else if (strcasecmp(log_facility_opt, "mail") == 0) {
            log_facility = LOG_MAIL;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_NEWS
      } else if (strcasecmp(log_facility_opt, "news") == 0) {
            log_facility = LOG_NEWS;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_SYSLOG
      } else if (strcasecmp(log_facility_opt, "syslog") == 0) {
            log_facility = LOG_SYSLOG;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_USER
      } else if (strcasecmp(log_facility_opt, "user") == 0) {
            log_facility = LOG_USER;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_UUCP
      } else if (strcasecmp(log_facility_opt, "uucp") == 0) {
            log_facility = LOG_UUCP;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_CONSOLE
      } else if (strcasecmp(log_facility_opt, "console") == 0) {
            log_facility = LOG_CONSOLE;
#endif
#ifdef HAVE_LOG_FACILITY_LOG_SECURITY
      } else if (strcasecmp(log_facility_opt, "security") == 0) {
            log_facility = LOG_SECURITY;
#endif
      } else {
            /* facility not found */
            fprintf(stderr,
                  "Facility %s not recognized, using default facility ",
                  log_facility_opt);

            /* get default facility */
            if (srvinfo.multithread) {
                  log_facility = LOG_DAEMON;
                  fprintf(stderr, "LOG_DAEMON\n");
            } else {
                  log_facility = LOG_USER;
                  fprintf(stderr, "LOG_USER\n");
            }
      }
      openlog(srvinfo.binaryname, LOG_PID, log_facility);
      syslog(LOG_INFO, "jftpgw v"JFTPGW_VERSION" opened syslog");
      cfg->syslog = 1;
      return 0;
}

static
int log_init_logfile(struct loginfo_st* cfg) {
      const char* option;

      if (cfg->logf) {
            /* already logging */
            return 0;
      }
      cfg->syslog = 0;
      /* read the logfile name from the configuration file */
      option = config_get_option("logfile");
      if (!option) {
            /* no logfile set, use the default */
            option = DEFAULTLOGFILE;
      }

      /* now option is set in every case */
      cfg->logf_name = chrooted_path(option);

      if (!(cfg->logf = open_logfile(cfg->logf_name))) {
            return -1;
      }
      jlog(7, "jftpgw v"JFTPGW_VERSION" opened the logfile");
      return 0;
}


static
int log_init_cmdlog(struct loginfo_st* cfg, int open) {
      char* option, *specs, *style;
      struct cmdlogent_t* files = cfg->cmdlogfiles;
      struct cmdlogent_t* new;
      char* logfile;
      int i;

      struct slist_t* opt_names =config_get_option_array("cmdlogfile");
      struct slist_t* opt_styles=config_get_option_array("cmdlogfile-style");
      struct slist_t* opt_specs =config_get_option_array("cmdlogfile-specs");

      int count_names = slist_count(opt_names);
      int count_styles = slist_count(opt_styles);
      int count_specs = slist_count(opt_specs);

      if (!(count_names == count_styles && count_styles == count_specs)) {
            jlog(4, "Counted %d times \"cmdlogfile\" as option",
                        count_names);
            jlog(4, "Counted %d times \"cmdlogfile-style\" as option",
                        count_styles);
            jlog(4, "Counted %d times \"cmdlogfile-specs\" as option",
                        count_specs);
            jlog(4, "This is not balanced, please fix your configuration");
            return -1;
      }

      /* reverse them */
      opt_names  = slist_reverse(opt_names);
      opt_styles = slist_reverse(opt_styles);
      opt_specs  = slist_reverse(opt_specs);

      for (i = 0; i < count_names; i++) {
            option = slist_pop(opt_names);
            specs  = slist_pop(opt_specs);
            style  = slist_pop(opt_styles);
            logfile = chrooted_path(option);
            if (log_name_exists(cfg->cmdlogfiles, logfile)
                || log_name_exists(cfg->cmdlogfiles, option)) {

                  free(logfile);
                  free(option);
                  free(specs);
                  free(style);
                  continue;
            }
            free(option);

            new = malloc(sizeof (struct cmdlogent_t));
            enough_mem(new);
            if (files) {
                  /* there are existing entries */
                  files->next = new;
            } else {
                  /* this is the first entry */
                  cfg->cmdlogfiles = new;
            }
            /* files always keeps the current entry and iterates on */
            files = new;
            files->logf_name = logfile;
            files->specs = char_enclose(" ", specs, " "); free(specs);
            files->style = style;
            files->next = (struct cmdlogent_t*) 0;
            if (open && (files->logf = open_logfile(files->logf_name))
                                                == NULL) {
                  /* the malloc()ed memory will be freed by
                   * reset_loginfo() */
                  if (i < count_names - 1) {
                        /* don't free if we're already at the end */
                        slist_destroy(opt_names);
                        slist_destroy(opt_styles);
                        slist_destroy(opt_specs);
                  }
                  return -1;
            }
      }
      /* The values are already free()ed, just the structures are still
       * malloc()ed */
      free(opt_names);
      free(opt_styles);
      free(opt_specs);
      return 0;
}


#define FILENAME_EXTEND       64
static
FILE* log_init_dirlog_open(const char* fname_pattern) {
      time_t nowtime;
      char* filename, *fname;
      size_t size_max;
      FILE* file;

      size_max = strlen(fname_pattern) + FILENAME_EXTEND;
      fname = (char*) malloc(size_max);
      filename = (char*) malloc(size_max);
      enough_mem(fname);
      enough_mem(filename);

      nowtime = time(NULL);
      strftime(fname, size_max, fname_pattern, localtime(&nowtime));
      snprintf(filename, size_max, fname, getpid());
      free(fname); fname = (char*) 0;

      file = open_logfile(filename);
      free(filename);
      return file;
}

static
int log_init_dirlog(struct loginfo_st* cfg, int open) {
      char* option, *specs, *style;
      char* prefix, *suffix;
      char* logdir, *logfile, *logf_name, *logf_name_chroot;
      struct cmdlogent_t* dirs = cfg->cmdlogdirs;
      struct cmdlogent_t* new;
      int i;

      struct slist_t* opt_names
            = config_get_option_array("connectionlogdir");
      struct slist_t* opt_styles
            = config_get_option_array("connectionlogdir-style");
      struct slist_t* opt_specs
            = config_get_option_array("connectionlogdir-specs");
      struct slist_t* opt_prefix
            = config_get_option_array("connectionlogdir-fileprefix");
      struct slist_t* opt_suffix
            = config_get_option_array("connectionlogdir-filesuffix");

      int count_names = slist_count(opt_names);
      int count_styles = slist_count(opt_styles);
      int count_specs = slist_count(opt_specs);
      int count_prefix = slist_count(opt_prefix);
      int count_suffix = slist_count(opt_suffix);

      if (! (count_names == count_styles
                  && count_styles == count_specs
                  && count_specs == count_prefix
                  && count_prefix == count_suffix)) {

            jlog(4, "Counted %d times \"connectionlogdir\" as option",
                        count_names);
            jlog(4, "Counted %d times \"connectionlogdir-style\" as option",
                        count_styles);
            jlog(4, "Counted %d times \"connectionlogdir-specs\" as option",
                        count_specs);
            jlog(4, "Counted %d times \"connectionlogdir-fileprefix\""
                        " as option", count_prefix);
            jlog(4, "Counted %d times \"connectionlogdir-filesuffix\""
                        " as option", count_suffix);
            jlog(4, "This is not balanced, please fix your configuration");
            return -1;
      }

      /* reverse them */
      opt_names  = slist_reverse(opt_names);
      opt_styles = slist_reverse(opt_styles);
      opt_specs  = slist_reverse(opt_specs);
      opt_prefix = slist_reverse(opt_prefix);
      opt_suffix = slist_reverse(opt_suffix);

      for (i = 0; i < count_names; i++) {
            option = slist_pop(opt_names);
            specs  = slist_pop(opt_specs);
            style  = slist_pop(opt_styles);
            prefix = slist_pop(opt_prefix);
            suffix = slist_pop(opt_suffix);

            logdir = chrooted_path(option);
            logfile = char_enclose(
                        prefix, "%Y-%m-%d--%H:%M:%S-%%d", suffix);
            free(prefix); free(suffix); free(option);

            logf_name = char_enclose(logdir, "/", logfile);
            logf_name_chroot = chrooted_path(logf_name);
            free(logdir);    logdir    = (char*) 0;
            free(logfile);   logfile   = (char*) 0;

            if (log_name_exists(cfg->cmdlogdirs, logf_name)
                  || log_name_exists(cfg->cmdlogdirs, logf_name_chroot)) {

                  free(logf_name);        logf_name        = (char*) 0;
                  free(logf_name_chroot); logf_name_chroot = (char*) 0;
                  free(style);
                  free(specs);
                  continue;
            }
            free(logf_name); logf_name = (char*) 0;

            new = malloc(sizeof (struct cmdlogent_t));
            enough_mem(new);
            if (dirs) {
                  /* there are existing entries */
                  dirs->next = new;
            } else {
                  /* this is the first entry */
                  cfg->cmdlogdirs = new;
            }
            /* dirs always keeps the current entry and iterates on */
            dirs = new;
            dirs->next = (struct cmdlogent_t*) 0;
            dirs->specs = char_enclose(" ", specs, " "); free(specs);
            dirs->logf_name = logf_name_chroot;
            dirs->style = style;

            if (open && (dirs->logf =
                  log_init_dirlog_open(dirs->logf_name)) == NULL) {
                  if (i < count_names - 1) {
                        /* don't free if we're already at the end */
                        slist_destroy(opt_names);
                        slist_destroy(opt_styles);
                        slist_destroy(opt_specs);
                        slist_destroy(opt_prefix);
                        slist_destroy(opt_suffix);
                  }
                  return -1;
            }
      }
      return 0;
}

int log_init() {
      loginfo.debuglevel = log_init_debuglevel();

      /* open the general logfile or syslog */
      if (config_compare_option("logstyle", "syslog")) {
            if (log_init_syslog(&loginfo) < 0) {
                  return -1;
            }
      } else {
            if (log_init_logfile(&loginfo) < 0) {
                  return -1;
            }
      }

      /* Open the command log logfile(s) */
      if (log_init_cmdlog(&loginfo, 1) < 0) {
            return -1;
      }

      /* Do the same with directories */
      if (log_init_dirlog(&loginfo, 1) < 0) {
            return -1;
      }

      return 0;
}


int log_detect_logfile_change() {
      /* syslog is now disabled, it was enabled previously, logging to
       * files is now enabled */
      if (!config_compare_option("logstyle", "syslog")
                                    && loginfo.syslog == 1) {
            return 1;
      }
      /* the name of the logfile has changed */
      if (!config_compare_option("logfile", loginfo.logf_name)) {
            return 1;
      }
      return 0;
}

int log_detect_syslog_change() {
      /* syslog is now enabled, it was not enabled previously */
      if (config_compare_option("logstyle", "syslog")
                                    && loginfo.syslog == 0) {
            return 1;
      }
      /* syslog is enabled, but the facility has changed */
      if (!config_compare_option("syslogfacility", loginfo.syslog_facility)
                                    && loginfo.syslog == 1){
            return 1;
      }
      return 0;
}

void log_cmdlogent_just_free(struct cmdlogent_t* cmd) {
      if (!cmd) {
            return;
      }
      log_cmdlogent_just_free(cmd->next);
      free(cmd->logf_name);
      free(cmd->style);
      free(cmd->specs);
      free(cmd);
}

struct cmdlogent_t* log_entry_in_cmdlogent(struct cmdlogent_t* cmd,
                                 const char* logf_name,
                                 const char* specs,
                                 const char* style) {

      while(cmd) {
            if (strcmp(logf_name, cmd->logf_name) == 0
             && strcmp(specs, cmd->specs) == 0
             && strcmp(style, cmd->style) == 0) {

                  return cmd;
            }
            cmd = cmd->next;
      }
      return 0;
}

int log_cmd_compare(struct cmdlogent_t** cmdn,
                struct cmdlogent_t** cmdo,
                FILE*(*f)(const char* s)) {

      struct cmdlogent_t* cmditer = *cmdn;
      struct cmdlogent_t* cmdpos;
      /* see, what is in cmdn, but not in cmdo. They are new, create them. */

      while (cmditer) {
            if (!(cmdpos = log_entry_in_cmdlogent(*cmdo,
                  cmditer->logf_name, cmditer->specs, cmditer->style))) {
                  /* in cmdn, but not in cmdo */
                  cmditer->logf = (*f)(cmditer->logf_name);
            } else {
                  /* since we have to replace cmdo by cmdn, we also
                   * have to copy the file handle even if the entry
                   * exists in the same way */
                  cmditer->logf = cmdpos->logf;
                  cmditer->logf_size = cmdpos->logf_size;
            }
            cmditer = cmditer->next;
      }

      /* see what is in cmdo, but not in cmdn. They are old, close them */
      cmditer = *cmdo;

      while(cmditer) {
            if (!(cmdpos = log_entry_in_cmdlogent(*cmdn,
                  cmditer->logf_name, cmditer->specs, cmditer->style))) {
                  fclose(cmditer->logf);
            } else {
                  cmdpos->logf = cmditer->logf;
                  cmdpos->logf_size = cmditer->logf_size;
            }
            cmditer = cmditer->next;
      }

      /* delete the old cmdlogent structure */
      log_cmdlogent_just_free(*cmdo);
      /* copy the new over the old */
      *cmdo = *cmdn;

      return 0;
}


int log_detect_cmdlog_change() {
      /* logf_name, specs and style may change */
      loginfo_bk = (struct loginfo_st*) malloc(sizeof(struct loginfo_st));
      enough_mem(loginfo_bk);

      memset(loginfo_bk, (int) 0, sizeof(struct loginfo_st));

      if (log_init_cmdlog(loginfo_bk, 0) < 0) {
            return -1;
      }
      if (log_init_dirlog(loginfo_bk, 0) < 0) {
            return -1;
      }

      /* check for new ones - first step
       * a         a - found, ok
       * b         c - found, ok
       * c         d - found, ok
       * d         e - not found, new
       *           f - not found, new
       *
       *  check for old ones - second step
       * a         a - found, ok
       * c         b - not found, delete
       * d         c - found, ok
       * e         d - found, ok
       * f
       */

      log_cmd_compare(&loginfo_bk->cmdlogfiles, &loginfo.cmdlogfiles,
                                                open_logfile);
      log_cmd_compare(&loginfo_bk->cmdlogdirs, &loginfo.cmdlogdirs,
                                          log_init_dirlog_open);
      free(loginfo_bk);

      return 0;
}

int log_detect_log_change() {
      int newlevel = config_get_ioption("debuglevel", loginfo.debuglevel);
      if (newlevel != loginfo.debuglevel) {
            /* the debug level has changed, very simple to adjust  :-) */
            loginfo.debuglevel = log_init_debuglevel();
      }
      if (log_detect_logfile_change()) {
            /* close the old logfile */
            if (loginfo.logf) {
                  fclose(loginfo.logf);
                  loginfo.logf = (FILE*) 0;
            }
            if (loginfo.logf_name) {
                  free(loginfo.logf_name);
                  loginfo.logf_name = (char*) 0;
            }
            /* open a new logfile */
            log_init_logfile(&loginfo);
      }
      if (log_detect_syslog_change()) {
            /* close the syslog */
            closelog();
            /* and re-open it */
            log_init_syslog(&loginfo);
      }
      log_detect_cmdlog_change();
      return 0;
}


Generated by  Doxygen 1.6.0   Back to index