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