mod_log.c
上传用户:pycemail
上传日期:2007-01-04
资源大小:329k
文件大小:18k
源码类别:

Ftp客户端

开发平台:

Unix_Linux

  1. /*
  2.  * ProFTPD - FTP server daemon
  3.  * Copyright (c) 1997, 1998 Public Flood Software
  4.  * Copyright (C) 1999, MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
  5.  *  
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
  19.  */
  20. /*
  21.  * Flexible logging module for proftpd
  22.  * $Id: mod_log.c,v 1.8 1999/10/01 07:57:31 macgyver Exp $
  23.  */
  24. #include "conf.h"
  25. #include "privs.h"
  26. extern response_t *resp_list,*resp_err_list;
  27. #define LOGBUF_SIZE 1025
  28. typedef struct logformat_struc logformat_t;
  29. typedef struct logfile_struc  logfile_t;
  30. struct logformat_struc {
  31.   logformat_t *next,*prev;
  32.   char *lf_nickname;
  33.   unsigned char *lf_format;
  34. };
  35. struct logfile_struc {
  36.   logfile_t *next,*prev;
  37.   int lf_fd;
  38.   int lf_classes;
  39.   char *lf_filename;
  40.   logformat_t *lf_format;
  41.   config_rec *lf_conf; /* pointer to the "owning" configuration */
  42. };
  43.   
  44. #define META_START 0xff
  45. #define META_ARG_END 0xfe
  46. #define META_ARG 1
  47. #define META_BYTES_SENT 2
  48. #define META_FILENAME 3
  49. #define META_ENV_VAR 4
  50. #define META_REMOTE_HOST 5
  51. #define META_REMOTE_IP 6
  52. #define META_IDENT_USER 7
  53. #define META_SERVER_PORT 8
  54. #define META_PID 9
  55. #define META_TIME 10
  56. #define META_SECONDS 11
  57. #define META_COMMAND 12
  58. #define META_SERVERNAME 13
  59. #define META_USER 14
  60. #define META_RESPONSE_CODE 15
  61. #define META_CLASS 16
  62. static pool *log_pool;
  63. static logformat_t *formats = NULL;
  64. static xaset_t *format_set = NULL;
  65. static logfile_t *logs = NULL;
  66. static xaset_t *log_set = NULL;
  67. /* format string args:
  68.    %c - class
  69.    %b - bytes sent for request
  70.    %f - filename
  71.    %{FOOBAR}e - contents of environment variable FOOBAR
  72.    %h - remote host
  73.    %a - remote IP-address
  74.    %l - remote logname (from identd)
  75.    %p - Port of server serving request
  76.    %P - Process ID of child serving request
  77.    %r - Full request (command)
  78.    %t - Time
  79.    %{format}t - formatted time (strftime(3) format)
  80.    %T - Time taken to serve request, in seconds
  81.    %s - Reponse code (status)
  82.    %u - Local user
  83.    %v - Servername of server serving request
  84. */
  85. static
  86. void add_meta(unsigned char **s, unsigned char meta, int args, ...)
  87. {
  88.   int arglen;
  89.   char *arg;
  90.   **s = META_START;
  91.   (*s) = (*s) + 1;
  92.   **s = meta;
  93.   (*s) = (*s) + 1;
  94.   if(args) {
  95.     va_list ap;
  96.     va_start(ap,args);
  97.     while(args--) {
  98.       arglen = va_arg(ap,int);
  99.       arg = va_arg(ap,char*);
  100.       bcopy(arg,*s,arglen);
  101.       (*s) = (*s) + arglen;
  102.       **s = META_ARG_END;
  103.       (*s) = (*s) + 1;
  104.     }
  105.     va_end(ap);
  106.   }
  107. }
  108. static
  109. char *preparse_arg(char **s)
  110. {
  111.   char *ret = (*s) + 1;
  112.   (*s) = (*s) + 1;
  113.   while(**s && **s != '}')
  114.     (*s) = (*s) + 1;
  115.   **s = 0;
  116.   (*s) = (*s) + 1;
  117.   return ret;
  118. }
  119. static
  120. void logformat(char *nickname, char *fmts)
  121. {
  122.   char *tmp,*arg;
  123.   unsigned char format[1024],*outs;
  124.   logformat_t *lf;
  125.   outs = format;
  126.   for(tmp = fmts; *tmp; ) {
  127.     if(*tmp == '%') {
  128.       arg = NULL;
  129.       tmp++;
  130.       for(;;) {
  131.         switch(*tmp) {
  132.         case '{':
  133.           arg = preparse_arg(&tmp);
  134.           continue;
  135.         case 'b':
  136.           add_meta(&outs,META_BYTES_SENT,0);
  137.           break;
  138.         case 'c':
  139.           add_meta(&outs,META_CLASS,0);
  140.           break;
  141.         case 'f':
  142.           add_meta(&outs,META_FILENAME,0);
  143.           break;
  144.         case 'e':
  145.           if(arg) {
  146.             add_meta(&outs,META_ENV_VAR,0);
  147.             add_meta(&outs,META_ARG,1,(int)strlen(arg),arg);
  148.           }
  149.           break;
  150.         case 'h':
  151.           add_meta(&outs,META_REMOTE_HOST,0);
  152.           break;
  153.         case 'a':
  154.           add_meta(&outs,META_REMOTE_IP,0);
  155.           break;
  156.         case 'l':
  157.           add_meta(&outs,META_IDENT_USER,0);
  158.           break;
  159.         case 'p': 
  160.           add_meta(&outs,META_SERVER_PORT,0);
  161.           break;
  162.         case 'P':
  163.           add_meta(&outs,META_PID,0);
  164.           break;
  165.         case 't':
  166.           add_meta(&outs,META_TIME,0);
  167.           if(arg)
  168.             add_meta(&outs,META_ARG,1,(int)strlen(arg),arg);
  169.           break;
  170.         case 'T':
  171.           add_meta(&outs,META_SECONDS,0);
  172.           break;
  173.         case 'u':
  174.           add_meta(&outs,META_USER,0);
  175.           break;
  176.         case 'r':
  177.           add_meta(&outs,META_COMMAND,0);
  178.           break;
  179.         case 'v':
  180.           add_meta(&outs,META_SERVERNAME,0);
  181.           break;
  182.         case 's':
  183.           add_meta(&outs,META_RESPONSE_CODE,0);
  184.           break;
  185.         case '%':
  186.           *outs++ = '%';
  187.           break;
  188.         }
  189. tmp++;
  190. break;
  191.       }
  192.     } else
  193.       *outs++ = *tmp++;
  194.   }
  195.   *outs++ = 0;
  196.   lf = (logformat_t*)pcalloc(log_pool,sizeof(logformat_t));
  197.   lf->lf_nickname = pstrdup(log_pool,nickname);
  198.   lf->lf_format = palloc(log_pool,outs - format);
  199.   bcopy(format,lf->lf_format,outs-format);
  200.   if(!format_set)
  201.     format_set = xaset_create(log_pool,NULL);
  202.   xaset_insert_end(format_set,(xasetmember_t*)lf);
  203.   formats = (logformat_t*)format_set->xas_list;
  204. }
  205. /* Syntax: LogFormat nickname "format string"
  206.  */
  207. MODRET add_logformat(cmd_rec *cmd)
  208. {
  209.   CHECK_ARGS(cmd,2);
  210.   CHECK_CONF(cmd,CONF_ROOT);
  211.   logformat(cmd->argv[1],cmd->argv[2]);
  212.   return HANDLED(cmd);
  213. }
  214. static int _parse_classes(char *s)
  215. {
  216.   int classes = 0;
  217.   char *nextp = NULL;
  218.   do {
  219.     if((nextp = strchr(s,',')))
  220.       *nextp++ = '';
  221.     if(!nextp) {
  222.       if((nextp = strchr(s,'|')))
  223.         *nextp++ = '';
  224.     }
  225.     if(!strcasecmp(s,"NONE"))
  226.       { classes = CL_NONE; break; }
  227.     if(!strcasecmp(s,"ALL"))
  228.       { classes = CL_ALL; break; }
  229.     else if(!strcasecmp(s,"AUTH"))
  230.       classes |= CL_AUTH;
  231.     else if(!strcasecmp(s,"INFO"))
  232.       classes |= CL_INFO;
  233.     else if(!strcasecmp(s,"DIRS"))
  234.       classes |= CL_DIRS;
  235.     else if(!strcasecmp(s,"READ"))
  236.       classes |= CL_READ;
  237.     else if(!strcasecmp(s,"WRITE"))
  238.       classes |= CL_WRITE;
  239.     else if(!strcasecmp(s,"MISC"))
  240.       classes |= CL_MISC;
  241.   } while((s = nextp));
  242.   return classes;
  243. }
  244. /* Syntax: ExtendedLog <log-filename> [<cmd-classes> [<format-nickname>]]
  245.  */
  246. MODRET add_extendedlog(cmd_rec *cmd)
  247. {
  248.   config_rec *c;
  249.   int argc;
  250.   char **argv;
  251.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  252.   argc = cmd->argc;
  253.   argv = cmd->argv;
  254.   if(argc < 2)
  255.     CONF_ERROR(cmd,"Syntax: ExtendedLog <log-filename> [<command-classes> [<format-nickname>]]");
  256.   c = add_config_param("ExtendedLog",3,NULL,NULL,NULL);
  257.   if(cmd->argv[1][0] != '/')
  258.     c->argv[0] = dir_canonical_path(log_pool,cmd->argv[1]);
  259.   else
  260.     c->argv[0] = pstrdup(log_pool,cmd->argv[1]);
  261.   if(argc > 2)
  262.     c->argv[1] = pstrdup(log_pool,cmd->argv[2]);
  263.   if(argc > 3)
  264.     c->argv[2] = pstrdup(log_pool,cmd->argv[3]);
  265.   c->argc = argc-1;
  266.   return HANDLED(cmd);
  267. }
  268. /* Syntax: SystemLog <filename> */
  269. MODRET set_systemlog(cmd_rec *cmd)
  270. {
  271.   char *syslogfn;
  272.   int ret;
  273.   
  274.   CHECK_ARGS(cmd,1);
  275.   CHECK_CONF(cmd,CONF_ROOT);
  276.   log_closesyslog();
  277.   syslogfn = cmd->argv[1];
  278.   if(strcasecmp(syslogfn,"NONE") == 0) {
  279.     log_discard();
  280.     return HANDLED(cmd);
  281.   }
  282.   if(*syslogfn != '/') 
  283.     syslogfn = dir_canonical_path(cmd->tmp_pool,syslogfn);
  284.   block_signals();
  285.   PRIVS_ROOT
  286.     if((ret = log_opensyslog(syslogfn)) < 0) {
  287.       int xerrno = errno;
  288.       
  289.       PRIVS_RELINQUISH
  290.       unblock_signals();
  291.       
  292.       if(ret == -2) {
  293. CONF_ERROR(cmd, "you are attempting to log to a world writeable directory");
  294.       } else {
  295. CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"unable to redirect logging to '",
  296.        syslogfn,"': ",strerror(xerrno),NULL));
  297.       }
  298.     }
  299.   
  300.   PRIVS_RELINQUISH
  301.   unblock_signals();
  302.   return HANDLED(cmd);
  303. }
  304. #ifdef HAVE_GMTOFF
  305. static
  306. struct tm *_get_gmtoff(int *tz)
  307. {
  308.   time_t tt = time(NULL);
  309.   struct tm *t;
  310.   t = localtime(&tt);
  311.   *tz = (int)(t->tm_gmtoff / 60)
  312.   return t;
  313. }
  314. #else
  315. static
  316. struct tm *_get_gmtoff(int *tz)
  317. {
  318.   time_t tt = time(NULL);
  319.   struct tm gmt;
  320.   struct tm *t;
  321.   int days,hours,minutes;
  322.   gmt = *gmtime(&tt);
  323.   t = localtime(&tt);
  324.   days = t->tm_yday - gmt.tm_yday;
  325.   hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24)
  326.           + t->tm_hour - gmt.tm_hour);
  327.   minutes = hours * 60 + t->tm_min - gmt.tm_min;
  328.   *tz = minutes;
  329.   return t;
  330. }
  331. #endif /* HAVE_GMTOFF */
  332. static
  333. char *get_next_meta(pool *p, cmd_rec *cmd, unsigned char **f)
  334. {
  335.   unsigned char *m;
  336.   char arg[256],*argp = NULL;
  337.   m = (*f) + 1;
  338.   switch(*m) {
  339.   case META_ARG:
  340.     m++; argp = arg;
  341.     while(*m != META_ARG_END)
  342.       *argp++ = (char)*m++;
  343.     *argp = 0; argp = arg;
  344.     m++;
  345.     break;
  346.   case META_BYTES_SENT:
  347.     argp = arg;
  348.     if(session.xfer.p)
  349.       snprintf(argp, sizeof(arg), "%lu",session.xfer.total_bytes);
  350.     else
  351.       sstrncpy(argp, "-", sizeof(arg));
  352.     m++;
  353.     break;
  354.   case META_CLASS:
  355.     argp = arg;
  356.     if(get_param_int(TOPLEVEL_CONF, "Classes", FALSE) > 0)
  357.       sstrncpy(argp, session.class->name, sizeof(arg));
  358.     else
  359.       sstrncpy(argp, "-", sizeof(arg));
  360.     m++;
  361.     break;
  362.   case META_FILENAME:
  363.     argp = arg;
  364.     if(session.xfer.p && session.xfer.path) {
  365.       char *fullpath;
  366.       fullpath = dir_abs_path(p,session.xfer.path,TRUE);
  367.       sstrncpy(argp, fullpath, sizeof(arg));
  368.     } else {
  369.       sstrncpy(argp, "-", sizeof(arg));
  370.     }
  371.     m++;
  372.     break;
  373.   case META_ENV_VAR:
  374.     argp = arg;
  375.     m++;
  376.     if(*m == META_START && *(m+1) == META_ARG) {
  377.       char *env;
  378.       env = getenv(get_next_meta(p,cmd,&m));
  379.       sstrncpy(argp, env, sizeof(arg));
  380.     }
  381.     break;
  382.   case META_REMOTE_HOST:
  383.     argp = arg;
  384.     sstrncpy(argp, session.c->remote_name, sizeof(arg));
  385.     m++;
  386.     break;
  387.   case META_REMOTE_IP:
  388.     argp = arg;
  389.     sstrncpy(argp, inet_ntoa(*session.c->remote_ipaddr), sizeof(arg));
  390.     m++;
  391.     break;
  392.   case META_IDENT_USER:
  393.     argp = arg;
  394.     sstrncpy(argp, session.ident_user, sizeof(arg));
  395.     m++;
  396.     break;
  397.   case META_SERVER_PORT:
  398.     argp = arg;
  399.     snprintf(argp, sizeof(arg), "%d", cmd->server->ServerPort);
  400.     m++;
  401.     break;
  402.   case META_PID:
  403.     argp = arg;
  404.     snprintf(argp, sizeof(arg), "%u",(unsigned int)getpid());
  405.     m++;
  406.     break;
  407.   case META_TIME:
  408.     {
  409.       char *time_fmt = "[%d/%b/%Y:%H:%M:%S ";
  410.       struct tm t;
  411.       int internal_fmt = 1;
  412.       int timz;
  413.       char sign;
  414.       argp = arg; m++;
  415.       if(*m == META_START && *(m+1) == META_ARG) {
  416.         time_fmt = get_next_meta(p,cmd,&m);
  417.         internal_fmt = 0;
  418.       }
  419.       t = *_get_gmtoff(&timz);
  420.       sign = (timz < 0 ? '-' : '+');
  421.       if(timz < 0)
  422.         timz = -timz;
  423.       strftime(argp,80,time_fmt,&t);
  424.       if(internal_fmt)
  425.         snprintf(argp + strlen(argp), sizeof(arg) - strlen(argp),
  426.                 "%c%.2d%.2d]", sign, timz/60, timz%60);
  427.     }
  428.     break;
  429.   case META_SECONDS:
  430.     argp = arg;
  431.     if(session.xfer.p) {
  432.       struct timeval end_time;
  433.       gettimeofday(&end_time,NULL);
  434.       end_time.tv_sec -= session.xfer.start_time.tv_sec;
  435.       if(end_time.tv_usec >= session.xfer.start_time.tv_usec)
  436.         end_time.tv_usec -= session.xfer.start_time.tv_usec;
  437.       else {
  438.         end_time.tv_usec = 1000000L - (session.xfer.start_time.tv_usec -
  439.                            end_time.tv_usec);
  440.         end_time.tv_sec--;
  441.       }
  442.       snprintf(argp, sizeof(arg), "%lu",(unsigned long)end_time.tv_sec);
  443.     } else {
  444.       sstrncpy(argp,"-",sizeof(arg));
  445.     }
  446.     m++;
  447.     break;
  448.   case META_COMMAND:
  449.     argp = arg;
  450.     if(!strcasecmp(cmd->argv[0],"PASS") && session.hide_password)
  451.       sstrncpy(argp, "PASS (hidden)", sizeof(arg));
  452.     else
  453.       sstrncpy(argp, get_full_cmd(cmd), sizeof(arg));
  454.     m++;
  455.     break;
  456.   case META_SERVERNAME:
  457.     argp = arg;
  458.     sstrncpy(argp,cmd->server->ServerName,sizeof(arg));
  459.     m++;
  460.     break;
  461.   case META_USER:
  462.     argp = arg;
  463.     if(!session.user) {
  464.       char *u;
  465.       u = get_param_ptr(cmd->server->conf,"UserName",FALSE);
  466.       if(!u)
  467.         u = "root";
  468.     
  469.       sstrncpy(argp, u, sizeof(arg));
  470.     } else {
  471.       sstrncpy(argp, session.user, sizeof(arg));
  472.     }
  473.     m++;
  474.     break;
  475.   case META_RESPONSE_CODE:
  476.     {
  477.       response_t *r;
  478.       argp = arg;
  479.       r = (resp_list ? resp_list : resp_err_list);
  480.       for(; r && !r->num; r=r->next) ;
  481.       if(r && r->num)
  482.         sstrncpy(argp,r->num,sizeof(arg));
  483.       else
  484.         sstrncpy(argp,"-",sizeof(arg));
  485.     }
  486.     m++;
  487.     break;
  488.   }
  489.   *f = m;
  490.   if(argp)
  491.     return pstrdup(p, argp);
  492.   else
  493.     return NULL;
  494. }
  495. static
  496. void do_log(cmd_rec *cmd, logfile_t *lf)
  497. {
  498.   unsigned char *f;
  499.   size_t size = LOGBUF_SIZE-2;
  500.   char logbuf[LOGBUF_SIZE];
  501.   logformat_t *fmt;
  502.   char *s,*bp;
  503.   fmt = lf->lf_format;
  504.   f = fmt->lf_format;
  505.   bp = logbuf;
  506.   while(*f && size) {
  507.     if(*f == META_START) {
  508.       s = get_next_meta(cmd->tmp_pool,cmd,&f);
  509.       if(s) {
  510.         size_t tmp;
  511.         tmp = strlen(s);
  512.         if(tmp > size)
  513.           tmp = size;
  514.         bcopy(s,bp,tmp);
  515.         size -= tmp;
  516.         bp += tmp;
  517.       }
  518.     } else {
  519.       *bp++ = (char)*f++;
  520.       size--;
  521.     }
  522.   }
  523.   *bp++ = 'n';
  524.   *bp = '';
  525.   write(lf->lf_fd,logbuf,strlen(logbuf));
  526. }
  527. MODRET log_command(cmd_rec *cmd)
  528. {
  529.   logfile_t *lf;
  530.   /* If not in anon mode, only handle logs for main servers */
  531.   for(lf = logs; lf; lf=lf->next)
  532.     if(lf->lf_fd != -1 && (cmd->class & lf->lf_classes)) {
  533.       if(!session.anon_config && lf->lf_conf && lf->lf_conf->config_type == CONF_ANON)
  534.         continue;
  535.       do_log(cmd,lf);
  536.     }
  537.   return DECLINED(cmd);
  538. }
  539. /* log_rehash is called whenever the master server rehashes it's
  540.  * config (in response to SIGHUP).
  541.  */
  542. static
  543. void log_rehash(void *d)
  544. {
  545.   destroy_pool(log_pool);
  546.   formats = NULL;
  547.   format_set = NULL;
  548.   logs = NULL;
  549.   log_set = NULL;
  550.   log_pool = make_sub_pool(permanent_pool);
  551.   logformat("","%h %l %u %t "%r" %s %b");
  552. }
  553.   
  554. static 
  555. int log_init()
  556. {
  557.   log_pool = make_sub_pool(permanent_pool);
  558.   /* add the "default" extendedlog format */
  559.   logformat("","%h %l %u %t "%r" %s %b");
  560.   register_rehash(NULL,log_rehash);
  561.   return 0;
  562. }
  563. static
  564. void get_extendedlogs()
  565. {
  566.   config_rec *c;
  567.   char *logfname;
  568.   int logclasses = CL_ALL;
  569.   logformat_t *logfmt;
  570.   char *logfmt_s = NULL;
  571.   logfile_t *logf;
  572.   c = find_config(main_server->conf,CONF_PARAM,"ExtendedLog",TRUE);
  573.   while(c) {
  574.     logfname = c->argv[0];
  575.     if(c->argc > 1) {
  576.       logclasses = _parse_classes(c->argv[1]);
  577.       if(c->argc > 2)
  578.         logfmt_s = c->argv[2];
  579.     }
  580.     if(logfmt_s) {
  581.       /* search for the format-nickname */
  582.       for(logfmt = formats; logfmt; logfmt=logfmt->next)
  583.         if(!strcmp(logfmt->lf_nickname,logfmt_s))
  584.           break;
  585.       if(!logfmt) {
  586.         log_pri(LOG_NOTICE,"format-nickname '%s' is not defined.",
  587.                            logfmt_s);
  588.         goto loop_extendedlogs;
  589.       }
  590.     } else
  591.       logfmt = formats;
  592.     logf = (logfile_t*)pcalloc(permanent_pool,sizeof(logfile_t));
  593.     logf->lf_fd = -1;
  594.     logf->lf_classes = logclasses;
  595.     logf->lf_filename = pstrdup(permanent_pool,logfname);
  596.     logf->lf_format = logfmt;
  597.     logf->lf_conf = c->parent;
  598.     if(!log_set)
  599.       log_set = xaset_create(permanent_pool,NULL);
  600.     xaset_insert(log_set,(xasetmember_t*)logf);
  601.     logs = (logfile_t*)log_set->xas_list;
  602. loop_extendedlogs:
  603.     c = find_config_next(c,c->next,CONF_PARAM,"ExtendedLog",TRUE);
  604.   }
  605. }
  606. MODRET log_auth_complete(cmd_rec *cmd)
  607. {
  608.   logfile_t *lf;
  609.   /* authentication is complete, if we aren't in anon-mode, close
  610.    * all extendedlogs opened inside <Anonymous> blocks.
  611.    */
  612.   if(!session.anon_config) {
  613.     for(lf = logs; lf; lf=lf->next)
  614.       if(lf->lf_fd != -1 && lf->lf_conf && lf->lf_conf->config_type == CONF_ANON) {
  615.         close(lf->lf_fd);
  616.         lf->lf_fd = -1;
  617.       }
  618.   } else {
  619.     /* close all logs which were opened inside a _different_ anonymous
  620.      * context.
  621.      */
  622.     for(lf = logs; lf; lf=lf->next)
  623.       if(lf->lf_fd != -1 && lf->lf_conf && lf->lf_conf != session.anon_config) {
  624.         close(lf->lf_fd);
  625.         lf->lf_fd = -1;
  626.       }
  627.     /* if any extendedlogs set inside our context match an outer log,
  628.      * close the outer (this allows overriding inside <Anonymous>).
  629.      */
  630.     for(lf = logs; lf; lf=lf->next)
  631.       if(lf->lf_conf && lf->lf_conf == session.anon_config) {
  632.         /* this should "override" any lower-level extendedlog with the
  633.          * same filename.
  634.          */
  635.         logfile_t *lf2;
  636.         for(lf2 = logs; lf2; lf2=lf2->next) {
  637.           if(lf2->lf_fd != -1 && !lf2->lf_conf &&
  638.              !strcmp(lf2->lf_filename,lf->lf_filename)) {
  639.             close(lf2->lf_fd);
  640.             lf2->lf_fd = -1;
  641.           }
  642.         }
  643.        
  644.         /* go ahead and close the log if it's CL_NONE */
  645.         if(lf->lf_fd != -1 && lf->lf_classes == CL_NONE) {
  646.           close(lf->lf_fd);
  647.           lf->lf_fd = -1;
  648.         }
  649.       }
  650.   }
  651.   return DECLINED(cmd);
  652. }
  653. static
  654. int log_child_init()
  655. {
  656.   /* open all log files */
  657.   logfile_t *lf;
  658.   int xerrno;
  659.   get_extendedlogs();
  660.   for(lf = logs; lf; lf=lf->next)
  661.     if(lf->lf_fd == -1) {
  662.       block_signals();
  663.       PRIVS_ROOT
  664.       lf->lf_fd = 
  665.          open(lf->lf_filename,O_CREAT|O_APPEND|O_WRONLY,0644);
  666.       xerrno = errno;
  667.       PRIVS_RELINQUISH
  668.       unblock_signals();
  669.       if(lf->lf_fd == -1)
  670.         log_pri(LOG_NOTICE,"unable to open ExtendedLog '%s': %s",
  671.                 lf->lf_filename,strerror(errno));
  672.     }
  673.   return 0;
  674. }
  675. static conftable log_config[] = {
  676.   { "LogFormat", add_logformat, NULL },
  677.   { "ExtendedLog", add_extendedlog, NULL },
  678.   { "SystemLog", set_systemlog, NULL },
  679.   { NULL, NULL, NULL }
  680. };
  681. cmdtable log_commands[] = {
  682.   { LOG_CMD, "*", G_NONE, log_command, FALSE, FALSE },
  683.   { LOG_CMD_ERR,"*", G_NONE, log_command, FALSE, FALSE },
  684.   { POST_CMD, "PASS", G_NONE, log_auth_complete,FALSE,FALSE },
  685.   { 0, NULL }
  686. };
  687. module log_module = {
  688.   NULL,NULL, /* Always NULL */
  689.   0x20, /* API version */
  690.   "log", /* Module name */
  691.   log_config,
  692.   log_commands,
  693.   NULL,
  694.   log_init,log_child_init
  695. };