1 #ident "$Id: logfile.c,v 4.11 2005/11/26 13:48:16 gert Exp $ Copyright (c) Gert Doering"
2
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <sys/types.h>
7 #include <time.h>
8 #include <errno.h>
9 #include <ctype.h>
10 #include <string.h>
11
12 #include "mgetty.h"
13 #include "policy.h"
14
15 /* this must be included after ugly.h (sets USE_VARARGS on non-ANSI cc's) */
16 #ifdef USE_VARARGS
17 # if !defined(NeXT) || defined(NEXTSGTTY)
18 # include <varargs.h>
19 # endif
20 #else
21 # include <stdarg.h>
22 #endif
23
24 #ifdef SYSLOG
25 #include <syslog.h>
26
27 #if !defined(linux) && !defined(BSD) && !defined(_SCO_DS) && \
28 !defined(SVR42) && !defined(solaris2) && !defined(_AIX) && !defined(__GLIBC__)
29 int openlog _PROTO(( char *, int, int ));
30 int syslog _PROTO(( int, char *, ... ));
31 #endif
32
33 #endif
34
35 /* on NeXTstep(POSIX), we have to use this *UGLY* way to cheat varargs/stdarg
36 */
37 #if defined(NeXT) && !defined(NEXTSGTTY)
38 # define va_alist a1,a2,a3,a4,a5,a6,a7,a8,a9
39 # define va_dcl long a1,a2,a3,a4,a5,a6,a7,a8,a9;
40 # define vsprintf(buf,fmt,v) sprintf((buf),(fmt),a1,a2,a3,a4,a5,a6,a7,a8,a9)
41 # define va_list int
42 # define va_start(v)
43 # define va_end(v)
44 #endif
45
46
47 static int log_level = LOG_LEVEL; /* set default log level threshold (jcp) */
48
49 static FILE * log_fp;
50 static boolean mail_logfile = FALSE;
51 static char log_path[ MAXPATH ];
52
53 static char log_infix[10]; /* printed between time stamp and text */
54 static char * log_program = "mgetty";
55
56 extern int atexit _PROTO(( void (*)(void) ));
57
58 /* Most systems have these variables but do not declare them. On many
59 of those systems that _do_ declare them, it won't hurt */
60
61 #if !defined(__NetBSD__) && !defined( __FreeBSD__ ) && !defined(__OpenBSD__) && !defined(__GLIBC__) && !defined(__MACH__)
62 extern int sys_nerr;
63 extern char *sys_errlist[];
64 #endif
65
66 /* Interactive Unix is a little bit braindead - does not have atexit(),
67 */
68 #if defined(ISC) || defined(SVR4) || defined(_3B1_) || \
69 defined(MEIBE) || defined(_SEQUENT_) || defined(_AIX) || \
70 defined(sysV68) || ( defined(M_XENIX) && !defined(M_UNIX) )
71 # define atexit(dummy)
72 #endif
73
74 /* on SunOS, we can do it with on_exit()
75 */
76 #ifdef sunos4
77 # define atexit(func) on_exit(func, NULL)
78 #endif
79
80 void log_init_paths _P3 ( (l_program, l_path, l_infix),
81 char * l_program, char * l_path, char * l_infix )
82 {
83 if ( l_program != NULL ) /* set program name */
84 {
85 char * p = strrchr( l_program, '/' );
86 log_program = ( p == NULL ) ? l_program : p+1;
87 }
88
89 if ( l_path != NULL ) /* set log file name+path */
90 {
91 if ( log_fp != NULL && /* logfile already open */
92 strcmp( l_path, log_path ) != 0 ) /* and path changed */
93 {
94 lprintf( L_MESG, "logging continues in file %s", l_path );
95 log_close(); /* -> reopen */
96 }
97
98 strncpy( log_path, l_path, sizeof(log_path)-1 );
99 log_path[sizeof(log_path)-1] = 0;
100 if ( strlen(l_path) >= sizeof(log_path) )
101 {
102 lprintf( L_FATAL, "internal error: log file path too long!" );
103 }
104 }
105
106 if ( l_infix != NULL ) /* usually tty id */
107 {
108 sprintf( log_infix, "%.*s ", (int) sizeof(log_infix)-2, l_infix );
109 }
110 }
111
112 void log_set_llevel _P1( (level), int level )
113 {
114 log_level = level;
115 }
116
117 /* close log file, to give programs like 'savelog' a chance to move it away
118 */
_P0(void)119 void log_close _P0(void)
120 {
121 if ( log_fp != NULL ) fclose( log_fp );
122 log_fp = NULL;
123 }
124
_P0(void)125 void logmail _P0( void )
126 {
127 char ws[MAXPATH+100];
128 char buf[512];
129 int l;
130 FILE * pipe_fp;
131 int log_fd;
132
133 if ( mail_logfile )
134 {
135 lprintf( L_MESG, "mailing logfile to %s...", ADMIN );
136
137 sprintf( ws, "%s %s", MAILER, ADMIN );
138 pipe_fp = popen( ws, "w" );
139 if ( pipe_fp == NULL )
140 {
141 lprintf( L_ERROR, "cannot open pipe to %s", MAILER );
142 /* FIXME: write to console - last resort */
143 fprintf( stderr, "cannot open pipe to %s", MAILER );
144 return;
145 }
146
147 fprintf( pipe_fp, "Subject: fatal error in logfile\n" );
148 fprintf( pipe_fp, "To: %s\n", ADMIN );
149 fprintf( pipe_fp, "From: root (Fax Getty)\n" );
150 fprintf( pipe_fp, "\nA fatal error has occured! The logfile follows\n" );
151 log_fd = open( log_path, O_RDONLY );
152 if ( log_fd == -1 )
153 {
154 fprintf( pipe_fp, "The logfile '%s' cannot be opened (errno=%d)\n",
155 log_path, errno );
156 }
157 else
158 {
159 do
160 {
161 l = read( log_fd, buf, sizeof( buf ) );
162 fwrite( buf, l, 1, pipe_fp );
163 }
164 while( l == sizeof( buf ) );
165 fprintf( pipe_fp, "\n------ logfile ends here -----\n" );
166 }
167 close( log_fd );
168 pclose( pipe_fp );
169 }
170
171 mail_logfile = FALSE;
172 }
173
174 int lputc _P2((level, ch), int level, char ch )
175 {
176 if ( log_fp != NULL && level <= log_level )
177 {
178 if ( isprint(ch) ) fputc( ch, log_fp );
179 else fprintf( log_fp, "[%02x]", (unsigned char) ch );
180 fflush( log_fp );
181 #ifdef LOG_CR_NEWLINE
182 if ( ch == '\n' ) fputc( ch, log_fp );
183 #endif
184 }
185 return 0;
186 }
187
188 int lputs _P2((level, s), int level, char * s )
189 {
190 int retcode = 0;
191 if ( log_fp != NULL && level <= log_level )
192 {
193 retcode = fputs( s!=NULL? s : "(NULL)", log_fp );
194 fflush( log_fp );
195 }
196 return retcode;
197 }
198
199 #ifdef USE_VARARGS
lprintf(level,format,va_alist)200 int lprintf( level, format, va_alist )
201 int level;
202 const char * format;
203 va_dcl
204 #else
205 int lprintf(int level, const char *format, ...)
206 #endif
207 {
208 char ws[2000];
209 time_t ti;
210 struct tm *tm;
211 va_list pvar;
212 int errnr;
213 char * p;
214 static int first_open = TRUE;
215
216 if ( level > log_level ) /* log level high enough? */
217 {
218 return 0; /* no -> return immediately */
219 }
220
221 #ifdef USE_VARARGS
222 va_start( pvar );
223 #else
224 va_start( pvar, format );
225 #endif
226
227 errnr = errno;
228
229 if ( log_fp == NULL ) /* open log file, if necessary */
230 {
231 if ( log_path[0] == 0 )
232 sprintf( log_path, LOG_PATH, "unknown" );
233 log_fp = fopen( log_path, "a" );
234
235 if ( log_fp == NULL ) /* opening log file failed */
236 {
237
238 sprintf(ws, "cannot open logfile %s", log_path);
239 perror(ws);
240
241 /* use /dev/console for logging, if possible */
242 if ( ( log_fp = fopen( CONSOLE, "w" ) ) != NULL )
243 {
244 fprintf( log_fp, "\n%s: resorting to logging to %s\n",
245 log_program, CONSOLE );
246 }
247 else /* give up, disable logging */
248 {
249 sprintf( ws, "cannot log to %s, disable logging", CONSOLE );
250 perror( ws );
251 log_level = -1;
252 return 0;
253 }
254 }
255
256 /* make sure that the logfile is not accidently stdin, -out or -err
257 */
258 if ( fileno( log_fp ) < 3 )
259 {
260 int fd;
261 if ( ( fd = fcntl( fileno( log_fp ), F_DUPFD, 3 ) ) > 2 )
262 {
263 fclose( log_fp );
264 log_fp = fdopen( fd, "a" );
265 }
266 }
267
268 /* the first time we open the logfile, write a separator line
269 * and initialize syslog logging (if desired)
270 */
271 if ( first_open )
272 {
273 first_open = FALSE;
274 fprintf( log_fp, "\n--" );
275 #ifdef SYSLOG
276 openlog( log_program, LOG_PID, SYSLOG_FC );
277 #endif
278 }
279
280 /* set close-on-exec bit (prevent user programs writing to logfile */
281 if ( fcntl( fileno( log_fp ), F_SETFD, 1 ) < 0 )
282 {
283 lprintf( L_ERROR, "open_log: can't set close-on-exec bit" );
284 }
285 }
286
287 /* Marc's hack to get different verbosity levels on different
288 * intendation levels
289 *!!!! ugly. Rewrite some day.
290 */
291 ws[0] = ' ';
292 ws[1] = ' ';
293
294 if (level == L_NOISE)
295 vsprintf( &ws[1], format, pvar );
296 else if (level == L_JUNK)
297 vsprintf( &ws[2], format, pvar );
298 else
299 vsprintf( &ws[0], format, pvar );
300
301 va_end( pvar );
302
303 /* convert non-printable characters "in-place" to "_" */
304 if ( level != L_AUDIT )
305 for( p=ws; *p!='\0'; p++ )
306 {
307 if ( ! isprint(*p) ) *p='_';
308 }
309
310 ti = time(NULL); tm = localtime(&ti);
311
312 if ( level == L_AUDIT ) /* some little auditing */
313 {
314 fprintf(log_fp, "\n%02d/%02d %02d:%02d:%02d ##### %s\n",
315 tm->tm_mon+1, tm->tm_mday,
316 tm->tm_hour, tm->tm_min, tm->tm_sec, ws );
317 #ifdef SYSLOG
318 syslog( LOG_NOTICE, "%s", ws );
319 #endif
320 }
321 else if ( level != L_ERROR && level != L_FATAL )
322 {
323 fprintf(log_fp, "\n%02d/%02d %02d:%02d:%02d %s %s",
324 tm->tm_mon+1, tm->tm_mday,
325 tm->tm_hour, tm->tm_min, tm->tm_sec,
326 log_infix, ws );
327 }
328 else /* ERROR or FATAL */
329 {
330 fprintf(log_fp, "\n%02d/%02d %02d:%02d:%02d %s %s: %s",
331 tm->tm_mon+1, tm->tm_mday,
332 tm->tm_hour, tm->tm_min, tm->tm_sec,
333 log_infix, ws,
334 ( errnr <= sys_nerr ) ? sys_errlist[errnr]:
335 "<error not in list>" );
336 #ifdef SYSLOG
337 syslog( level == L_FATAL? LOG_ALERT: LOG_ERR, "%s: %m", ws );
338 #endif
339
340 #ifndef SYSLOG
341 if ( level == L_FATAL ) /* write to console */
342 {
343 FILE * cons_fp;
344 if ( ( cons_fp = fopen( CONSOLE, "w" ) ) != NULL )
345 {
346 fprintf( cons_fp, "\n%s FATAL: %s %s\n",
347 log_program, log_infix, ws );
348 fclose( cons_fp );
349 }
350 else /* last resort */
351 if ( !mail_logfile )
352 {
353 atexit( logmail );
354 mail_logfile = TRUE;
355 }
356 }
357 #endif
358 } /* end if ( L_ERROR or L_FATAL ) */
359 fflush(log_fp);
360
361 return 0;
362 }
363
364