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