1 /* log.c
2    Routines to add entries to the log files.
3 
4    Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor
5 
6    This file is part of the Taylor UUCP package.
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21 
22    The author of the program may be contacted at ian@airs.com.
23    */
24 
25 #include "uucp.h"
26 
27 #if USE_RCS_ID
28 const char log_rcsid[] = "$FreeBSD$";
29 #endif
30 
31 #include <ctype.h>
32 #include <errno.h>
33 
34 #if HAVE_STDARG_H
35 #include <stdarg.h>
36 #endif
37 
38 #if TM_IN_SYS_TIME
39 #include <sys/time.h>
40 #else
41 #include <time.h>
42 #endif
43 
44 #include "uudefs.h"
45 #include "uuconf.h"
46 #include "system.h"
47 
48 /* Local functions.  */
49 
50 __inline__ static char *zstpcpy P((char *zto, const char *zfrom));
51 static const char *zldate_and_time P((void));
52 
53 /* Program name.  Set by main function.  */
54 const char *zProgram;
55 
56 /* Log file name.  */
57 static const char *zLogfile;
58 
59 /* The function to call when a LOG_FATAL error occurs.  */
60 static void (*pfLfatal) P((void));
61 
62 /* Whether to go to a file.  */
63 static boolean fLfile;
64 
65 /* ID number.  */
66 static int iLid;
67 
68 /* The current user name.  */
69 static char *zLuser;
70 
71 /* The current system name.  */
72 static char *zLsystem;
73 
74 /* The current device name.  */
75 char *zLdevice;
76 
77 /* The open log file.  */
78 static FILE *eLlog;
79 
80 /* Whether we have tried to open the log file.  We need this because
81    we don't want to keep trying to open the log file if we failed the
82    first time.  It can't be static because under HAVE_HDB_LOGGING we
83    may have to write to various different log files.  */
84 static boolean fLlog_tried;
85 
86 #if DEBUG > 1
87 /* Debugging file name.  */
88 static const char *zLdebugfile;
89 
90 /* The open debugging file.  */
91 static FILE *eLdebug;
92 
93 /* Whether we've tried to open the debugging file.  */
94 static boolean fLdebug_tried;
95 #endif
96 
97 /* Statistics file name.  */
98 static const char *zLstatsfile;
99 
100 /* The open statistics file.  */
101 static FILE *eLstats;
102 
103 /* Whether we've tried to open the statistics file.  */
104 static boolean fLstats_tried;
105 
106 /* The array of signals.  The elements are only set to TRUE by the
107    default signal handler.  They are only set to FALSE if we don't
108    care whether we got the signal or not.  */
109 volatile sig_atomic_t afSignal[INDEXSIG_COUNT];
110 
111 /* The array of signals to log.  The elements are only set to TRUE by
112    the default signal handler.  They are set to FALSE when the signal
113    is logged in ulog.  This means that if a signal comes in at just
114    the right time we won't log it (or, rather, we'll log it once
115    instead of twice), but that is not a catatrophe.  */
116 volatile sig_atomic_t afLog_signal[INDEXSIG_COUNT];
117 
118 /* Flag that indicates SIGHUP is worth logging.  */
119 boolean fLog_sighup = TRUE;
120 
121 /* Signal names to use when logging signals.  */
122 static const char * const azSignal_names[INDEXSIG_COUNT] = INDEXSIG_NAMES;
123 
124 /* If not NULL, ulog calls this function before outputting anything.
125    This is used to support cu.  */
126 void (*pfLstart) P((void));
127 
128 /* If not NULL, ulog calls this function after outputting everything.
129    This is used to support cu.  */
130 void (*pfLend) P((void));
131 
132 /* Set the function to call on a LOG_FATAL error.  */
133 
134 void
135 ulog_fatal_fn (pfn)
136      void (*pfn) P((void));
137 {
138   pfLfatal = pfn;
139 }
140 
141 /* Decide whether to send log message to the file or not.  */
142 
143 void
ulog_to_file(puuconf,ffile)144 ulog_to_file (puuconf, ffile)
145      pointer puuconf;
146      boolean ffile;
147 {
148   int iuuconf;
149 
150   iuuconf = uuconf_logfile (puuconf, &zLogfile);
151   if (iuuconf != UUCONF_SUCCESS)
152     ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
153 
154 #if DEBUG > 1
155   iuuconf = uuconf_debugfile (puuconf, &zLdebugfile);
156   if (iuuconf != UUCONF_SUCCESS)
157     ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
158 #endif
159 
160   iuuconf = uuconf_statsfile (puuconf, &zLstatsfile);
161   if (iuuconf != UUCONF_SUCCESS)
162     ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
163 
164   fLfile = ffile;
165 }
166 
167 /* Set the ID number.  This will be called by the usysdep_initialize
168    if there is something sensible to set it to.  */
169 
170 void
ulog_id(i)171 ulog_id (i)
172      int i;
173 {
174   iLid = i;
175 }
176 
177 /* Set the user we are making log entries for.  The arguments will be
178    copied into memory.  */
179 
180 void
ulog_user(zuser)181 ulog_user (zuser)
182      const char *zuser;
183 {
184   ubuffree (zLuser);
185   zLuser = zbufcpy (zuser);
186 }
187 
188 /* Set the system name we are making log entries for.  The name is copied
189    into memory.  */
190 
191 void
ulog_system(zsystem)192 ulog_system (zsystem)
193   const char *zsystem;
194 {
195   if (zsystem == NULL
196       || zLsystem == NULL
197       || strcmp (zsystem, zLsystem) != 0)
198     {
199       ubuffree (zLsystem);
200       zLsystem = zbufcpy (zsystem);
201 #if HAVE_HDB_LOGGING
202       /* Under HDB logging we now must write to a different log file.  */
203       ulog_close ();
204 #endif /* HAVE_HDB_LOGGING */
205     }
206 }
207 
208 /* Set the device name.  This is copied into memory.  */
209 
210 void
ulog_device(zdevice)211 ulog_device (zdevice)
212      const char *zdevice;
213 {
214   ubuffree (zLdevice);
215   zLdevice = zbufcpy (zdevice);
216 }
217 
218 /* A helper function for ulog.  */
219 
220 __inline__ static char *
zstpcpy(zto,zfrom)221 zstpcpy (zto, zfrom)
222      char *zto;
223      const char *zfrom;
224 {
225   while ((*zto++ = *zfrom++) != '\0')
226     ;
227   return zto - 1;
228 }
229 
230 /* Make a log entry.  We make a token concession to non ANSI_C systems,
231    but it clearly won't always work.  */
232 
233 #if ! HAVE_PROTOTYPES || ! HAVE_STDARG_H
234 #undef HAVE_VFPRINTF
235 #define HAVE_VFPRINTF 0
236 #endif
237 
238 /*VARARGS2*/
239 #if HAVE_VFPRINTF
240 void
ulog(enum tlog ttype,const char * zmsg,...)241 ulog (enum tlog ttype, const char *zmsg, ...)
242 #else
243 void
244 ulog (ttype, zmsg, a, b, c, d, f, g, h, i, j)
245      enum tlog ttype;
246      const char *zmsg;
247 #endif
248 {
249 #if HAVE_VFPRINTF
250   va_list parg;
251 #endif
252   FILE *e, *edebug;
253   boolean fstart, fend;
254   const char *zhdr;
255   char *zprefix;
256   register char *zset;
257   char *zformat;
258   char *zfrom;
259 
260   /* Log any received signal.  We do it this way to avoid calling ulog
261      from the signal handler.  A few routines call ulog to get this
262      message out with zmsg == NULL.  */
263   {
264     static boolean fdoing_sigs;
265 
266     if (! fdoing_sigs)
267       {
268 	int isig;
269 
270 	fdoing_sigs = TRUE;
271 	for (isig = 0; isig < INDEXSIG_COUNT; isig++)
272 	  {
273 	    if (afLog_signal[isig])
274 	      {
275 		afLog_signal[isig] = FALSE;
276 
277 		/* Apparently SunOS sends SIGINT rather than SIGHUP
278 		   when hanging up, so we don't log either signal if
279 		   fLog_sighup is FALSE.  */
280 		if ((isig != INDEXSIG_SIGHUP && isig != INDEXSIG_SIGINT)
281 		    || fLog_sighup)
282 		  ulog (LOG_ERROR, "Got %s signal", azSignal_names[isig]);
283 	      }
284 	  }
285 	fdoing_sigs = FALSE;
286       }
287   }
288 
289 #if DEBUG > 1
290   /* If we've had a debugging file open in the past, then we want to
291      write all log file entries to the debugging file even if it's
292      currently closed.  */
293   if (fLfile
294       && eLdebug == NULL
295       && ! fLdebug_tried
296       && iDebug != 0)
297     {
298       fLdebug_tried = TRUE;
299       eLdebug = esysdep_fopen (zLdebugfile, FALSE, TRUE, TRUE);
300     }
301 #endif /* DEBUG > 1 */
302 
303   if (! fLfile)
304     e = stderr;
305 #if DEBUG > 1
306   else if ((int) ttype >= (int) LOG_DEBUG)
307     {
308       e = eLdebug;
309 
310       /* If we can't open the debugging file, don't output any
311 	 debugging messages.  */
312       if (e == NULL)
313 	return;
314     }
315 #endif /* DEBUG > 1 */
316   else
317     {
318       if (eLlog == NULL && ! fLlog_tried)
319 	{
320 	  const char *zprint = NULL;
321 
322 	  fLlog_tried = TRUE;
323 #if ! HAVE_HDB_LOGGING
324 	  eLlog = esysdep_fopen (zLogfile, TRUE, TRUE, TRUE);
325 	  zprint = zLogfile;
326 #else /* HAVE_HDB_LOGGING */
327 	  {
328 	    const char *zcheck;
329 	    int cfmt;
330 	    char *zfile;
331 
332 	    /* Only run sprintf if there are no more than two
333                unadorned %s.  If we see any other formatting
334                character, just use zLogfile as is.  This is to protect
335                the UUCP administrator against foolishness.  Note that
336                this has been reported as a security vulnerability, but
337                it is not.  */
338 	    cfmt = 0;
339 	    for (zcheck = zLogfile; *zcheck != '\0'; ++zcheck)
340 	      {
341 		if (*zcheck == '%')
342 		  {
343 		    if (zcheck[1] == 's')
344 		      ++cfmt;
345 		    else
346 		      {
347 			cfmt = 3;
348 			break;
349 		      }
350 		  }
351 	      }
352 
353 	    if (cfmt > 2)
354 	      zfile = zbufcpy (zLogfile);
355 	    else
356 	      {
357 		const char *zsys;
358 		char *zbase;
359 		char *zlower;
360 
361 		/* We want to write to .Log/program/system, e.g.
362 		   .Log/uucico/uunet.  The system name may not be set.  */
363 		if (zLsystem == NULL)
364 		  zsys = "ANY";
365 		else
366 		  zsys = zLsystem;
367 
368 		zbase = zsysdep_base_name (zProgram);
369 		if (zbase == NULL)
370 		  zbase = zbufcpy (zProgram);
371 
372 		/* On some systems the native uusched will invoke
373 		   uucico with an upper case argv[0].  We work around
374 		   that by forcing the filename to lower case here.  */
375 		for (zlower = zbase; *zlower != '\0'; zlower++)
376 		  if (isupper (*zlower))
377 		    *zlower = tolower (*zlower);
378 
379 		zfile = zbufalc (strlen (zLogfile)
380 				 + strlen (zbase)
381 				 + strlen (zsys)
382 				 + 1);
383 		sprintf (zfile, zLogfile, zbase, zsys);
384 		ubuffree (zbase);
385 	      }
386 
387 	    eLlog = esysdep_fopen (zfile, TRUE, TRUE, TRUE);
388 	    if (eLlog != NULL)
389 	      ubuffree (zfile);
390 	    else
391 	      zprint = zfile;
392 	  }
393 #endif /* HAVE_HDB_LOGGING */
394 
395 	  if (eLlog == NULL)
396 	    {
397 	      /* We can't open the log file.  We report the problem to
398 		 stderr.  This is not ideal, since if this is uucico
399 		 running on an inbound call stderr is actually
400 		 connected to a remote system, but is better than
401 		 doing nothing.  */
402 	      fprintf (stderr, "%s: %s: can not open log file: %s\n",
403 		       zProgram, zprint, strerror (errno));
404 	      if (pfLfatal != NULL)
405 		(*pfLfatal) ();
406 	      usysdep_exit (FALSE);
407 	    }
408 	}
409 
410       e = eLlog;
411 
412       /* eLlog might be NULL here because we might try to open the log
413 	 file recursively via esysdep_fopen.  */
414       if (e == NULL)
415 	return;
416     }
417 
418   if (zmsg == NULL)
419     return;
420 
421   if (pfLstart != NULL)
422     (*pfLstart) ();
423 
424   edebug = NULL;
425 #if DEBUG > 1
426   if ((int) ttype < (int) LOG_DEBUG)
427     edebug = eLdebug;
428 #endif
429 
430   fstart = TRUE;
431   fend = TRUE;
432 
433   switch (ttype)
434     {
435     case LOG_NORMAL:
436       zhdr = "";
437       break;
438     case LOG_ERROR:
439       zhdr = "ERROR: ";
440       break;
441     case LOG_FATAL:
442       zhdr = "FATAL: ";
443       break;
444 #if DEBUG > 1
445     case LOG_DEBUG:
446       zhdr = "DEBUG: ";
447       break;
448     case LOG_DEBUG_START:
449       zhdr = "DEBUG: ";
450       fend = FALSE;
451       break;
452     case LOG_DEBUG_CONTINUE:
453       zhdr = NULL;
454       fstart = FALSE;
455       fend = FALSE;
456       break;
457     case LOG_DEBUG_END:
458       zhdr = NULL;
459       fstart = FALSE;
460       break;
461 #endif
462     default:
463       zhdr = "???: ";
464       break;
465     }
466 
467   if (! fstart)
468     zprefix = zbufcpy ("");
469   else
470     {
471       if (! fLfile)
472 	{
473 	  zprefix = zbufalc (strlen (zProgram) + 3);
474 	  sprintf (zprefix, "%s: ", zProgram);
475 	}
476       else
477 	{
478 	  zprefix = zbufalc (strlen (zProgram)
479 			     + (zLsystem == NULL ? 1 : strlen (zLsystem))
480 			     + (zLuser == NULL ? 4 : strlen (zLuser))
481 			     + sizeof "1991-12-31 12:00:00.00"
482 			     + strlen (zhdr)
483 			     + 100);
484 	  zset = zprefix;
485 #if HAVE_TAYLOR_LOGGING
486 	  {
487 	    char *zbase;
488 
489 	    zbase = zsysdep_base_name (zProgram);
490 	    if (zbase == NULL)
491 	      zbase = zbufcpy (zProgram);
492 	    zset = zstpcpy (zset, zbase);
493 	    *zset++ = ' ';
494 	    ubuffree (zbase);
495 	  }
496 #else /* ! HAVE_TAYLOR_LOGGING */
497 	  zset = zstpcpy (zset, zLuser == NULL ? "uucp" : zLuser);
498 	  *zset++ = ' ';
499 #endif /* HAVE_TAYLOR_LOGGING */
500 
501 	  zset = zstpcpy (zset, zLsystem == NULL ? "-" : zLsystem);
502 	  *zset++ = ' ';
503 
504 #if HAVE_TAYLOR_LOGGING
505 	  zset = zstpcpy (zset, zLuser == NULL ? "-" : zLuser);
506 	  *zset++ = ' ';
507 #endif /* HAVE_TAYLOR_LOGGING */
508 
509 	  *zset++ = '(';
510 	  zset = zstpcpy (zset, zldate_and_time ());
511 
512 	  if (iLid != 0)
513 	    {
514 #if ! HAVE_HDB_LOGGING
515 #if HAVE_TAYLOR_LOGGING
516 	      sprintf (zset, " %d", iLid);
517 #else /* ! HAVE_TAYLOR_LOGGING */
518 	      sprintf (zset, "-%d", iLid);
519 #endif /* ! HAVE_TAYLOR_LOGGING */
520 #else /* HAVE_HDB_LOGGING */
521 	      /* I assume that the second number here is meant to be
522 		 some sort of file sequence number, and that it should
523 		 correspond to the sequence number in the statistics
524 		 file.  I don't have any really convenient way to do
525 		 this, so I won't unless somebody thinks it's very
526 		 important.  */
527 	      sprintf (zset, ",%d,%d", iLid, 0);
528 #endif /* HAVE_HDB_LOGGING */
529 
530 	      zset += strlen (zset);
531 	    }
532 
533 #if QNX_LOG_NODE_ID
534 	  sprintf (zset, " %ld", (long) getnid ());
535 	  zset += strlen (zset);
536 #endif
537 
538 	  *zset++ = ')';
539 	  *zset++ = ' ';
540 
541 	  strcpy (zset, zhdr);
542 	}
543     }
544 
545   zformat = zbufalc (2 * strlen (zprefix) + strlen (zmsg) + 2);
546 
547   zset = zformat;
548   zfrom = zprefix;
549   while (*zfrom != '\0')
550     {
551       if (*zfrom == '%')
552 	*zset++ = '%';
553       *zset++ = *zfrom++;
554     }
555 
556   ubuffree (zprefix);
557 
558   zset = zstpcpy (zset, zmsg);
559 
560   if (fend)
561     {
562       *zset++ = '\n';
563       *zset = '\0';
564     }
565 
566 #if HAVE_VFPRINTF
567   va_start (parg, zmsg);
568   vfprintf (e, zformat, parg);
569   va_end (parg);
570   if (edebug != NULL)
571     {
572       va_start (parg, zmsg);
573       vfprintf (edebug, zformat, parg);
574       va_end (parg);
575     }
576 #else /* ! HAVE_VFPRINTF */
577   fprintf (e, zformat, a, b, c, d, f, g, h, i, j);
578   if (edebug != NULL)
579     fprintf (edebug, zformat, a, b, c, d, f, g, h, i, j);
580 #endif /* ! HAVE_VFPRINTF */
581 
582   ubuffree (zformat);
583 
584   (void) fflush (e);
585   if (edebug != NULL)
586     (void) fflush (edebug);
587 
588   if (pfLend != NULL)
589     (*pfLend) ();
590 
591   if (ttype == LOG_FATAL)
592     {
593       if (pfLfatal != NULL)
594 	(*pfLfatal) ();
595       usysdep_exit (FALSE);
596     }
597 
598 #if CLOSE_LOGFILES
599   ulog_close ();
600 #endif
601 }
602 
603 /* Log a uuconf error.  */
604 
605 void
ulog_uuconf(ttype,puuconf,iuuconf)606 ulog_uuconf (ttype, puuconf, iuuconf)
607      enum tlog ttype;
608      pointer puuconf;
609      int iuuconf;
610 {
611   char ab[512];
612 
613   (void) uuconf_error_string (puuconf, iuuconf, ab, sizeof ab);
614   ulog (ttype, "%s", ab);
615 }
616 
617 /* Close the log file.  There's nothing useful we can do with errors,
618    so we don't check for them.  */
619 
620 void
ulog_close()621 ulog_close ()
622 {
623   /* Make sure we logged any signal we received.  */
624   ulog (LOG_ERROR, (const char *) NULL);
625 
626   if (eLlog != NULL)
627     {
628       (void) fclose (eLlog);
629       eLlog = NULL;
630       fLlog_tried = FALSE;
631     }
632 
633 #if DEBUG > 1
634   if (eLdebug != NULL)
635     {
636       (void) fclose (eLdebug);
637       eLdebug = NULL;
638       fLdebug_tried = FALSE;
639     }
640 #endif
641 }
642 
643 /* Add an entry to the statistics file.  We may eventually want to put
644    failed file transfers in here, but we currently do not.  */
645 
646 /*ARGSUSED*/
647 void
ustats(fsucceeded,zuser,zsystem,fsent,cbytes,csecs,cmicros,fcaller)648 ustats (fsucceeded, zuser, zsystem, fsent, cbytes, csecs, cmicros, fcaller)
649      boolean fsucceeded;
650      const char *zuser;
651      const char *zsystem;
652      boolean fsent;
653      long cbytes;
654      long csecs;
655      long cmicros;
656      boolean fcaller ATTRIBUTE_UNUSED;
657 {
658   long cbps;
659 
660   /* The seconds and microseconds are now counted independently, so
661      they may be out of synch.  */
662   if (cmicros < 0)
663     {
664       csecs -= ((- cmicros) / 1000000L) + 1;
665       cmicros = 1000000L - ((- cmicros) % 1000000L);
666     }
667   if (cmicros >= 1000000L)
668     {
669       csecs += cmicros / 10000000L;
670       cmicros = cmicros % 1000000L;
671     }
672 
673   /* On a system which can determine microseconds we might very well
674      have both csecs == 0 and cmicros == 0.  */
675   if (csecs == 0 && cmicros < 1000)
676     cbps = 0;
677   else
678     {
679       long cmillis, cdiv, crem;
680 
681       /* Compute ((csecs * 1000) / cmillis) using integer division.
682 	 Where DIV is integer division, we know
683 	     a = (a DIV b) * b + a % b
684 	 so
685 	     a / b = (a DIV b) + (a % b) / b
686 	 We compute the latter with a as csecs and b as cmillis,
687 	 mixing the multiplication by 1000.  */
688       cmillis = csecs * 1000 + cmicros / 1000;
689       cdiv = (cbytes / cmillis) * 1000;
690       crem = (cbytes % cmillis) * 1000;
691       cbps = cdiv + (crem / cmillis);
692       if (cmillis < 0 || cdiv < 0 || crem < 0 || cbps < 0)
693 	{
694 	  /* We overflowed using milliseconds, so use seconds.  */
695 	  cbps = cbytes / (csecs + ((cmicros > 500000L) ? 1 : 0));
696 	}
697     }
698 
699   if (eLstats == NULL)
700     {
701       if (fLstats_tried)
702 	return;
703       fLstats_tried = TRUE;
704       eLstats = esysdep_fopen (zLstatsfile, TRUE, TRUE, TRUE);
705       if (eLstats == NULL)
706 	return;
707     }
708 
709 #if HAVE_TAYLOR_LOGGING
710   fprintf (eLstats,
711 	   "%s %s (%s) %s%s %ld bytes in %ld.%03ld seconds (%ld bytes/sec) on port %s\n",
712 	   zuser, zsystem, zldate_and_time (),
713 	   fsucceeded ? "" : "failed after ",
714 	   fsent ? "sent" : "received",
715 	   cbytes, csecs, cmicros / 1000, cbps,
716 	   zLdevice == NULL ? "unknown" : zLdevice);
717 #endif /* HAVE_TAYLOR_LOGGING */
718 #if HAVE_V2_LOGGING
719   fprintf (eLstats,
720 	   "%s %s (%s) (%ld) %s %s %ld bytes %ld seconds\n",
721 	   zuser, zsystem, zldate_and_time (),
722 	   (long) time ((time_t *) NULL),
723 	   fsent ? "sent" : "received",
724 	   fsucceeded ? "data" : "failed after",
725 	   cbytes, csecs + cmicros / 500000);
726 #endif /* HAVE_V2_LOGGING */
727 #if HAVE_HDB_LOGGING
728   {
729     static int iseq;
730 
731     /* I don't know what the 'C' means.  The sequence number should
732        probably correspond to the sequence number in the log file, but
733        that is currently always 0; using this fake sequence number
734        will still at least reveal which transfers are from different
735        calls.  */
736     ++iseq;
737     fprintf (eLstats,
738 	     "%s!%s %c (%s) (C,%d,%d) [%s] %s %ld / %ld.%03ld secs, %ld%s%s\n",
739 	     zsystem, zuser, fcaller ? 'M' : 'S', zldate_and_time (),
740 	     iLid, iseq, zLdevice == NULL ? "unknown" : zLdevice,
741 	     fsent ? "->" : "<-",
742 	     cbytes, csecs, cmicros / 1000, cbps,
743 	     " bytes/sec",
744 	     fsucceeded ? "" : " [PARTIAL FILE]");
745   }
746 #endif /* HAVE_HDB_LOGGING */
747 
748   (void) fflush (eLstats);
749 
750 #if CLOSE_LOGFILES
751   ustats_close ();
752 #endif
753 }
754 
755 /* Close the statistics file.  */
756 
757 void
ustats_close()758 ustats_close ()
759 {
760   if (eLstats != NULL)
761     {
762       if (fclose (eLstats) != 0)
763 	ulog (LOG_ERROR, "fclose: %s", strerror (errno));
764       eLstats = NULL;
765       fLstats_tried = FALSE;
766     }
767 }
768 
769 /* Return the date and time in a form used for a log entry.  */
770 
771 static const char *
zldate_and_time()772 zldate_and_time ()
773 {
774   long isecs, imicros;
775   struct tm s;
776 #if HAVE_TAYLOR_LOGGING
777   static char ab[sizeof "1991-12-31 12:00:00.00"];
778 #endif
779 #if HAVE_V2_LOGGING
780   static char ab[sizeof "12/31-12:00"];
781 #endif
782 #if HAVE_HDB_LOGGING
783   static char ab[sizeof "12/31-12:00:00"];
784 #endif
785 
786   isecs = ixsysdep_time (&imicros);
787   usysdep_localtime (isecs, &s);
788 
789 #if HAVE_TAYLOR_LOGGING
790   sprintf (ab, "%04d-%02d-%02d %02d:%02d:%02d.%02d",
791 	   s.tm_year + 1900, s.tm_mon + 1, s.tm_mday, s.tm_hour,
792 	   s.tm_min, s.tm_sec, (int) (imicros / 10000));
793 #endif
794 #if HAVE_V2_LOGGING
795   sprintf (ab, "%d/%d-%02d:%02d", s.tm_mon + 1, s.tm_mday,
796 	   s.tm_hour, s.tm_min);
797 #endif
798 #if HAVE_HDB_LOGGING
799   sprintf (ab, "%d/%d-%d:%02d:%02d", s.tm_mon + 1, s.tm_mday,
800 	   s.tm_hour, s.tm_min, s.tm_sec);
801 #endif
802 
803   return ab;
804 }
805