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 28const char log_rcsid[] = "$Id: log.c,v 1.65 2002/03/05 19:10:41 ian Rel $"; 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)); 51static const char *zldate_and_time P((void)); 52 53/* Program name. Set by main function. */ 54const char *zProgram; 55 56/* Log file name. */ 57static const char *zLogfile; 58 59/* The function to call when a LOG_FATAL error occurs. */ 60static void (*pfLfatal) P((void)); 61 62/* Whether to go to a file. */ 63static boolean fLfile; 64 65/* ID number. */ 66static int iLid; 67 68/* The current user name. */ 69static char *zLuser; 70 71/* The current system name. */ 72static char *zLsystem; 73 74/* The current device name. */ 75char *zLdevice; 76 77/* The open log file. */ 78static 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. */ 84static boolean fLlog_tried; 85 86#if DEBUG > 1 87/* Debugging file name. */ 88static const char *zLdebugfile; 89 90/* The open debugging file. */ 91static FILE *eLdebug; 92 93/* Whether we've tried to open the debugging file. */ 94static boolean fLdebug_tried; 95#endif 96 97/* Statistics file name. */ 98static const char *zLstatsfile; 99 100/* The open statistics file. */ 101static FILE *eLstats; 102 103/* Whether we've tried to open the statistics file. */ 104static 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. */ 109volatile 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. */ 116volatile sig_atomic_t afLog_signal[INDEXSIG_COUNT]; 117 118/* Flag that indicates SIGHUP is worth logging. */ 119boolean fLog_sighup = TRUE; 120 121/* Signal names to use when logging signals. */ 122static 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. */ 126void (*pfLstart) P((void)); 127 128/* If not NULL, ulog calls this function after outputting everything. 129 This is used to support cu. */ 130void (*pfLend) P((void)); 131 132/* Set the function to call on a LOG_FATAL error. */ 133 134void 135ulog_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 143void 144ulog_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 170void 171ulog_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 180void 181ulog_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 191void 192ulog_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 210void 211ulog_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 * 221zstpcpy (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 240void 241ulog (enum tlog ttype, const char *zmsg, ...) 242#else 243void 244ulog (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 605void 606ulog_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 620void 621ulog_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 fchmod(fileno(eLlog), 0666); 629 (void) fclose (eLlog); 630 eLlog = NULL; 631 fLlog_tried = FALSE; 632 } 633 634#if DEBUG > 1 635 if (eLdebug != NULL) 636 { 637 fchmod(fileno(eLdebug), 0666); 638 (void) fclose (eLdebug); 639 eLdebug = NULL; 640 fLdebug_tried = FALSE; 641 } 642#endif 643} 644 645/* Add an entry to the statistics file. We may eventually want to put 646 failed file transfers in here, but we currently do not. */ 647 648/*ARGSUSED*/ 649void 650ustats (fsucceeded, zuser, zsystem, fsent, cbytes, csecs, cmicros, fcaller) 651 boolean fsucceeded; 652 const char *zuser; 653 const char *zsystem; 654 boolean fsent; 655 long cbytes; 656 long csecs; 657 long cmicros; 658 boolean fcaller ATTRIBUTE_UNUSED; 659{ 660 long cbps; 661 662 /* The seconds and microseconds are now counted independently, so 663 they may be out of synch. */ 664 if (cmicros < 0) 665 { 666 csecs -= ((- cmicros) / 1000000L) + 1; 667 cmicros = 1000000L - ((- cmicros) % 1000000L); 668 } 669 if (cmicros >= 1000000L) 670 { 671 csecs += cmicros / 10000000L; 672 cmicros = cmicros % 1000000L; 673 } 674 675 /* On a system which can determine microseconds we might very well 676 have both csecs == 0 and cmicros == 0. */ 677 if (csecs == 0 && cmicros < 1000) 678 cbps = 0; 679 else 680 { 681 long cmillis, cdiv, crem; 682 683 /* Compute ((csecs * 1000) / cmillis) using integer division. 684 Where DIV is integer division, we know 685 a = (a DIV b) * b + a % b 686 so 687 a / b = (a DIV b) + (a % b) / b 688 We compute the latter with a as csecs and b as cmillis, 689 mixing the multiplication by 1000. */ 690 cmillis = csecs * 1000 + cmicros / 1000; 691 cdiv = (cbytes / cmillis) * 1000; 692 crem = (cbytes % cmillis) * 1000; 693 cbps = cdiv + (crem / cmillis); 694 if (cmillis < 0 || cdiv < 0 || crem < 0 || cbps < 0) 695 { 696 /* We overflowed using milliseconds, so use seconds. */ 697 cbps = cbytes / (csecs + ((cmicros > 500000L) ? 1 : 0)); 698 } 699 } 700 701 if (eLstats == NULL) 702 { 703 if (fLstats_tried) 704 return; 705 fLstats_tried = TRUE; 706 eLstats = esysdep_fopen (zLstatsfile, TRUE, TRUE, TRUE); 707 if (eLstats == NULL) 708 return; 709 } 710 711#if HAVE_TAYLOR_LOGGING 712 fprintf (eLstats, 713 "%s %s (%s) %s%s %ld bytes in %ld.%03ld seconds (%ld bytes/sec) on port %s\n", 714 zuser, zsystem, zldate_and_time (), 715 fsucceeded ? "" : "failed after ", 716 fsent ? "sent" : "received", 717 cbytes, csecs, cmicros / 1000, cbps, 718 zLdevice == NULL ? "unknown" : zLdevice); 719#endif /* HAVE_TAYLOR_LOGGING */ 720#if HAVE_V2_LOGGING 721 fprintf (eLstats, 722 "%s %s (%s) (%ld) %s %s %ld bytes %ld seconds\n", 723 zuser, zsystem, zldate_and_time (), 724 (long) time ((time_t *) NULL), 725 fsent ? "sent" : "received", 726 fsucceeded ? "data" : "failed after", 727 cbytes, csecs + cmicros / 500000); 728#endif /* HAVE_V2_LOGGING */ 729#if HAVE_HDB_LOGGING 730 { 731 static int iseq; 732 733 /* I don't know what the 'C' means. The sequence number should 734 probably correspond to the sequence number in the log file, but 735 that is currently always 0; using this fake sequence number 736 will still at least reveal which transfers are from different 737 calls. */ 738 ++iseq; 739 fprintf (eLstats, 740 "%s!%s %c (%s) (C,%d,%d) [%s] %s %ld / %ld.%03ld secs, %ld%s%s\n", 741 zsystem, zuser, fcaller ? 'M' : 'S', zldate_and_time (), 742 iLid, iseq, zLdevice == NULL ? "unknown" : zLdevice, 743 fsent ? "->" : "<-", 744 cbytes, csecs, cmicros / 1000, cbps, 745 " bytes/sec", 746 fsucceeded ? "" : " [PARTIAL FILE]"); 747 } 748#endif /* HAVE_HDB_LOGGING */ 749 750 (void) fflush (eLstats); 751 752#if CLOSE_LOGFILES 753 ustats_close (); 754#endif 755} 756 757/* Close the statistics file. */ 758 759void 760ustats_close () 761{ 762 if (eLstats != NULL) 763 { 764 if (fclose (eLstats) != 0) 765 ulog (LOG_ERROR, "fclose: %s", strerror (errno)); 766 eLstats = NULL; 767 fLstats_tried = FALSE; 768 } 769} 770 771/* Return the date and time in a form used for a log entry. */ 772 773static const char * 774zldate_and_time () 775{ 776 long isecs, imicros; 777 struct tm s; 778#if HAVE_TAYLOR_LOGGING 779 static char ab[sizeof "1991-12-31 12:00:00.00"]; 780#endif 781#if HAVE_V2_LOGGING 782 static char ab[sizeof "12/31-12:00"]; 783#endif 784#if HAVE_HDB_LOGGING 785 static char ab[sizeof "12/31-12:00:00"]; 786#endif 787 788 isecs = ixsysdep_time (&imicros); 789 usysdep_localtime (isecs, &s); 790 791#if HAVE_TAYLOR_LOGGING 792 sprintf (ab, "%04d-%02d-%02d %02d:%02d:%02d.%02d", 793 s.tm_year + 1900, s.tm_mon + 1, s.tm_mday, s.tm_hour, 794 s.tm_min, s.tm_sec, (int) (imicros / 10000)); 795#endif 796#if HAVE_V2_LOGGING 797 sprintf (ab, "%d/%d-%02d:%02d", s.tm_mon + 1, s.tm_mday, 798 s.tm_hour, s.tm_min); 799#endif 800#if HAVE_HDB_LOGGING 801 sprintf (ab, "%d/%d-%d:%02d:%02d", s.tm_mon + 1, s.tm_mday, 802 s.tm_hour, s.tm_min, s.tm_sec); 803#endif 804 805 return ab; 806} 807