1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 /** \file wzd_log.c
28  * @brief Contains routines to log files.
29  */
30 
31 #include "wzd_all.h"
32 
33 #ifndef WZD_USE_PCH
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <time.h>
40 
41 #ifdef WIN32
42 #include <winsock2.h>
43 #include <io.h>
44 #else
45 #include <unistd.h>
46 
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 
52 #include <netdb.h>
53 
54 #include <syslog.h>
55 #endif
56 
57 #include <fcntl.h> /* O_WRONLY */
58 
59 #include "wzd_structs.h"
60 #include "wzd_log.h"
61 #include "wzd_misc.h"
62 #include "wzd_user.h"
63 
64 #include "wzd_debug.h"
65 
66 #endif /* WZD_USE_PCH */
67 
68 static struct memory_log_t _static_log;
69 
70 static void _buffer_push(const char *);
71 
72 struct wzd_log_entry_t {
73   int fd;
74   int syslog;
75 };
76 
77 static struct wzd_log_entry_t _log_channels[MAX_LOG_CHANNELS];
78 
log_init(void)79 int log_init(void)
80 {
81   int i;
82 
83   for (i=0; i<MAX_LOG_CHANNELS; i++) {
84     _log_channels[i].fd = -1;
85     _log_channels[i].syslog = 0;
86   }
87 
88   _static_log.size = 100; /* last 100 messages */
89   _static_log.data = malloc(_static_log.size * sizeof(char*));
90   memset(_static_log.data,0,_static_log.size * sizeof(char*));
91 
92   return 0;
93 }
94 
log_open(const char * filename,int filemode)95 int log_open(const char * filename, int filemode)
96 {
97   int fd;
98 
99   fd = open(filename, filemode, 0640);
100   if (fd)
101     FD_REGISTER(fd,"Log");
102 
103   return fd;
104 }
105 
log_close(int fd)106 void log_close(int fd)
107 {
108 #ifdef DEBUG
109   if (fd == 1 /* stdout */ || fd == 2 /* stderr */) return;
110 #endif
111   if (fd == -1) return;
112 
113   FD_UNREGISTER(fd,"Log");
114   close(fd);
115 }
116 
log_fini(void)117 void log_fini(void)
118 {
119   int i,j,fd;
120 
121   for (i=0; i<MAX_LOG_CHANNELS; i++)
122     if (_log_channels[i].fd != -1) {
123       fd = _log_channels[i].fd;
124       for (j=i; j<MAX_LOG_CHANNELS; j++)
125         if (_log_channels[j].fd == fd) _log_channels[j].fd = -1;
126 #ifdef DEBUG
127       if (fd == 1 /* stdout */ || fd == 2 /* stderr */) continue;
128 #endif
129       FD_UNREGISTER(fd,"Log");
130       close(fd);
131     }
132 
133   for (i=0; i<_static_log.size; i++) {
134     free(_static_log.data[i]);
135   }
136   free(_static_log.data);
137   _static_log.size = 0;
138 }
139 
140 /* NOTE we are forced to open log in lib, because of win32
141  * memory management
142  */
log_open_old(const char * filename,int filemode)143 int log_open_old(const char *filename, int filemode)
144 {
145   int fd;
146 
147   fd = open(filename, filemode, 0640);
148   if (fd < 0)
149     return -1;
150   mainConfig->logfile = fdopen(fd,"a");
151   if (!mainConfig->logfile) return 1;
152   return 0;
153 }
154 
log_close_old(void)155 void log_close_old(void)
156 {
157   if (mainConfig->logfile)
158     fclose(mainConfig->logfile);
159   mainConfig->logfile = NULL;
160 }
161 
162 /** \brief Get file descriptor asociated to log level
163  *
164  * Get file decriptor corresponding to all messages sent to the
165  * log level.
166  */
log_get(unsigned int level)167 int log_get(unsigned int level)
168 {
169   if (level >= MAX_LOG_CHANNELS) return -1;
170 
171   return _log_channels[level].fd;
172 }
173 
log_set(unsigned int level,int fd)174 int log_set(unsigned int level, int fd)
175 {
176   unsigned int i, count;
177 
178   if (level >= MAX_LOG_CHANNELS) return -1;
179 
180   if (_log_channels[level].fd > 0) {
181     count = 0;
182 
183     for (i=0; i<MAX_LOG_CHANNELS; i++) {
184       if (_log_channels[i].fd == _log_channels[level].fd)
185         count++;
186     }
187     if (count == 1) {
188       log_close(_log_channels[level].fd);
189     }
190   }
191 
192   _log_channels[level].fd = fd;
193 
194   return 0;
195 }
196 
log_set_syslog(unsigned int level,int syslog_value)197 int log_set_syslog(unsigned int level, int syslog_value)
198 {
199   if (level >= MAX_LOG_CHANNELS) return -1;
200 
201   _log_channels[level].syslog = syslog_value;
202 
203   return 0;
204 }
205 
out_log(int level,const char * fmt,...)206 void out_log(int level,const char *fmt,...)
207 {
208   int prior=0;
209   va_list argptr;
210   char buffer[4096];
211   char datestr[128];
212   time_t timeval;
213   struct tm * ntime;
214 
215   /* new logging code */
216   if (level >= MAX_LOG_CHANNELS) return;
217 
218   /* don't log events when log level is lower than threshold */
219   if (mainConfig && level < mainConfig->loglevel) return;
220 
221   /* create timestamp */
222   timeval = time(NULL);
223   ntime = localtime( &timeval );
224   (void)strftime(datestr,sizeof(datestr),"%b %d %H:%M:%S ",ntime);
225 
226   if (_log_channels[level].fd > 0 || _log_channels[level].syslog)
227   {
228     va_start(argptr,fmt); /* note: ansi compatible version of va_start */
229     vsnprintf(buffer,sizeof(buffer)-1,fmt,argptr);
230     va_end (argptr);
231 
232     if (_log_channels[level].fd > 0) {
233       write(_log_channels[level].fd, datestr, strlen(datestr));
234       write(_log_channels[level].fd, buffer, strlen(buffer));
235     }
236 
237     _buffer_push(buffer);
238 
239 #ifndef _WIN32
240     if (_log_channels[level].syslog)
241     {
242       /* default priority: informational */
243       prior = LOG_INFO;
244       switch (level) {
245         case LEVEL_CRITICAL:
246           prior = LOG_ERR;
247           break;
248         case LEVEL_HIGH:
249           prior = LOG_ERR;
250           break;
251         case LEVEL_NORMAL:
252           prior = LOG_NOTICE;
253           break;
254         case LEVEL_INFO:
255           prior = LOG_INFO;
256           break;
257         case LEVEL_FLOOD:
258           prior = LOG_DEBUG;
259           break;
260         default:
261           break;
262       }
263 
264       syslog(prior,"%s",buffer);
265     }
266 #endif
267   }
268 
269 #ifdef DEBUG
270   {
271     char new_format[1024];
272     char msg_begin[120];
273     char msg_end[20];
274 
275     msg_begin[0] = '\0';
276     msg_end[0] = '\0';
277 
278     switch (level) {
279       case LEVEL_CRITICAL:
280         strcpy(msg_begin,CLR_BOLD);
281         (void)strlcat(msg_begin,CLR_RED,sizeof(msg_begin));
282         strcpy(msg_end,CLR_NOCOLOR);
283         break;
284       case LEVEL_HIGH:
285         strcpy(msg_begin,CLR_RED);
286         strcpy(msg_end,CLR_NOCOLOR);
287         break;
288       case LEVEL_NORMAL:
289         strcpy(msg_begin,CLR_GREEN);
290         strcpy(msg_end,CLR_NOCOLOR);
291         break;
292       case LEVEL_INFO:
293         strcpy(msg_begin,CLR_BLUE);
294         strcpy(msg_end,CLR_NOCOLOR);
295         break;
296       case LEVEL_FLOOD:
297         strcpy(msg_begin,CLR_CYAN);
298         strcpy(msg_end,CLR_NOCOLOR);
299         break;
300       default:
301         break;
302     }
303 
304     /* add timestamp */
305     strlcat(msg_begin,datestr,sizeof(msg_begin));
306 
307     va_start(argptr,fmt); /* note: ansi compatible version of va_start */
308     snprintf(new_format,sizeof(new_format)-1,"%s%s%s",msg_begin,fmt,msg_end);
309     vsnprintf(buffer,sizeof(buffer)-1,new_format,argptr);
310     va_end (argptr);
311 
312     write(1 /* stderr */, buffer, strlen(buffer));
313   }
314 #endif
315 }
316 
317 #ifdef DEBUG
out_err(int level,const char * fmt,...)318 void out_err(int level, const char *fmt,...)
319 {
320   va_list argptr;
321   char msg_begin[120];
322   char msg_end[20];
323   char new_format[1024];
324   char datestr[128];
325   time_t timeval;
326   struct tm * ntime;
327 
328   msg_begin[0] = '\0';
329   msg_end[0] = '\0';
330 
331   if (!mainConfig || level >= mainConfig->loglevel) {
332 
333     { /* ! syslog */
334 
335 
336       switch (level) {
337         case LEVEL_CRITICAL:
338           strcpy(msg_begin,CLR_BOLD);
339           (void)strlcat(msg_begin,CLR_RED,sizeof(msg_begin));
340           strcpy(msg_end,CLR_NOCOLOR);
341           break;
342         case LEVEL_HIGH:
343           strcpy(msg_begin,CLR_RED);
344           strcpy(msg_end,CLR_NOCOLOR);
345           break;
346         case LEVEL_NORMAL:
347           strcpy(msg_begin,CLR_GREEN);
348           strcpy(msg_end,CLR_NOCOLOR);
349           break;
350         case LEVEL_INFO:
351           strcpy(msg_begin,CLR_BLUE);
352           strcpy(msg_end,CLR_NOCOLOR);
353           break;
354         case LEVEL_FLOOD:
355           strcpy(msg_begin,CLR_CYAN);
356           strcpy(msg_end,CLR_NOCOLOR);
357           break;
358         default:
359           break;
360       }
361 
362       /* add timestamp */
363       timeval = time(NULL);
364       ntime = localtime( &timeval );
365       (void)strftime(datestr,sizeof(datestr),"%b %d %H:%M:%S ",ntime);
366       strlcat(msg_begin,datestr,sizeof(msg_begin));
367 
368       snprintf(new_format,1023,"%s%s%s",msg_begin,fmt,msg_end);
369 
370       /* XXX we can't use mainConfig, because it could be broken here */
371       /*  if (level >= mainConfig->loglevel) {*/
372       va_start(argptr,fmt); /* note: ansi compatible version of va_start */
373       vfprintf(stderr,new_format,argptr);
374       fflush(stderr);
375       va_end (argptr);
376       /*  }*/
377     } /* syslog */
378   } /* > loglevel ? */
379 }
380 #else
out_err(int level,const char * fmt,...)381 void out_err(int level, const char *fmt,...)
382 {
383 }
384 #endif
385 
xferlog_open(const char * filename,unsigned int filemode)386 int xferlog_open(const char *filename, unsigned int filemode)
387 {
388   int fd;
389 #if (defined (__FreeBSD__) && (__FreeBSD__ < 5)) || defined(WIN32) || defined(__APPLE__)
390   fd = open(filename,O_WRONLY | O_CREAT | O_APPEND, filemode);
391 #else /* ! BSD */
392   fd = open(filename,O_WRONLY | O_CREAT | O_APPEND | O_SYNC, filemode);
393 #endif /* BSD */
394   return fd;
395 }
396 
xferlog_close(int fd)397 void xferlog_close(int fd)
398 {
399   close(fd);
400 }
401 
out_xferlog(wzd_context_t * context,int is_complete)402 void out_xferlog(wzd_context_t * context, int is_complete)
403 {
404   char buffer[2048];
405   char datestr[128];
406   time_t timeval;
407   struct tm * ntime;
408   const char * remote_host;
409   struct hostent *h;
410   char * username;
411 
412   if (mainConfig->xferlog_fd == -1) return;
413 
414   h = gethostbyaddr((char*)&context->hostip,sizeof(context->hostip),AF_INET);
415   if (h==NULL)
416     remote_host = inet_ntoa( *((struct in_addr*)context->hostip) );
417   else
418     remote_host = h->h_name;
419   username = GetUserByID(context->userid)->username;
420   timeval = time(NULL);
421   ntime = localtime( &timeval );
422   (void)strftime(datestr,sizeof(datestr),"%a %b %d %H:%M:%S %Y",ntime);
423   snprintf(buffer,2047,
424       "%s %lu %s %" PRIu64 " %s %c %c %c %c %s ftp 1 * %c\n",
425       datestr,
426       (unsigned long)(time(NULL)-context->current_action.tm_start), /* transfer time */
427       remote_host?remote_host:"(null)", /* remote-host */
428       context->current_action.bytesnow, /* file-size */
429       context->current_action.arg, /* filename */
430       'b', /* transfer type: b(inary) / a(scii) */
431       '_', /* special action flag: C(ompressed), U(ncompressed),
432               T(ar'ed) _ (no action) */
433       (context->current_action.token==TOK_RETR)?'o':'i',
434         /* direction: o (outgoing) i (incoming) */
435       'r', /* access-mode: a (anonymous) g (guest) r (real-user) */
436       username,
437       is_complete?'c':'i' /* c (complete) i (incomplete) */
438       );
439   (void)write(mainConfig->xferlog_fd,buffer,strlen(buffer));
440 }
441 
log_message(const char * event,const char * fmt,...)442 void log_message(const char *event, const char *fmt, ...)
443 {
444   va_list argptr;
445   char buffer[2048];
446   char datestr[128];
447   time_t timeval;
448   struct tm * ntime;
449 
450   va_start(argptr,fmt); /* note: ansi compatible version of va_start */
451   vsnprintf(buffer,2047,fmt,argptr);
452   va_end (argptr);
453 
454   timeval = time(NULL);
455   ntime = localtime( &timeval );
456   (void)strftime(datestr,sizeof(datestr),"%a %b %d %H:%M:%S %Y",ntime);
457   out_log(LEVEL_NORMAL,"%s %s: %s\n",
458       datestr,
459       event,
460       buffer
461       );
462 }
463 
str2loglevel(const char * s)464 int str2loglevel(const char *s)
465 {
466   if (strcasecmp(s,"lowest")==0) return LEVEL_LOWEST;
467   else if (strcasecmp(s,"flood")==0) return LEVEL_FLOOD;
468   else if (strcasecmp(s,"info")==0) return LEVEL_INFO;
469   else if (strcasecmp(s,"normal")==0) return LEVEL_NORMAL;
470   else if (strcasecmp(s,"high")==0) return LEVEL_HIGH;
471   else if (strcasecmp(s,"critical")==0) return LEVEL_CRITICAL;
472   return -1;
473 }
474 
loglevel2str(int l)475 const char * loglevel2str(int l)
476 {
477   switch (l) {
478   case LEVEL_LOWEST: return "lowest";
479   case LEVEL_FLOOD: return "flood";
480   case LEVEL_INFO: return "info";
481   case LEVEL_NORMAL: return "normal";
482   case LEVEL_HIGH: return "high";
483   case LEVEL_CRITICAL: return "critical";
484   }
485   return "";
486 }
487 
_buffer_push(const char * str)488 static void _buffer_push(const char *str)
489 {
490   int i;
491   char * old;
492 
493   for (i=0; i<_static_log.size; i++) {
494     if (_static_log.data[i] == NULL) {
495       _static_log.data[i] = strdup(str);
496       return;
497     }
498   }
499 
500   /* circular buffer is full. Clear last entry and rotate buffer */
501   old = _static_log.data[0];
502   memmove(_static_log.data, _static_log.data + 1, (_static_log.size-1)*sizeof(char*));
503   _static_log.data[_static_log.size - 1] = strdup(str);
504   free(old);
505 }
506 
507 /** \brief Return a pointer to the log buffer (last log messages, stored in memory)
508  *
509  * The structure must not be changed or freed
510  */
get_log_buffer(void)511 struct memory_log_t * get_log_buffer(void)
512 {
513   return (&_static_log);
514 }
515 
516