1 /*
2 * util.c - utility function implementation
3 *
4 * Copyright (C) 2011-2013 Thien-Thi Nguyen
5 * Copyright (C) 2000, 2001, 2002, 2003 Stefan Jahn <stefan@lkcc.org>
6 * Copyright (C) 2000 Raimund Jacob <raimi@lkcc.org>
7 * Copyright (C) 1999 Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
8 *
9 * This is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3, or (at your option)
12 * any later version.
13 *
14 * This software is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this package. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24
25 #ifdef _AIX
26 # undef _NO_PROTO
27 # ifndef _USE_IRS
28 # define _USE_IRS 1
29 # endif
30 # define _XOPEN_SOURCE_EXTENDED 1
31 #endif /* _AIX */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <time.h>
39 #include <errno.h>
40 #include <assert.h>
41
42 #if HAVE_SYS_TIME_H
43 # include <sys/time.h>
44 #endif
45 #if HAVE_SYS_RESOURCE_H && !defined (__MINGW32__)
46 # include <sys/resource.h>
47 #endif
48 #if HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif
51 #if HAVE_STRINGS_H
52 # include <strings.h>
53 #endif
54
55 #if HAVE_SYS_UTSNAME_H
56 # include <sys/utsname.h>
57 #endif
58
59 #include "networking-headers.h"
60 #include "unused.h"
61 #include "libserveez/alloc.h"
62 #include "libserveez/boot.h"
63 #include "libserveez/windoze.h"
64 #ifdef ENABLE_LOG_MUTEX
65 # include "libserveez/mutex.h"
66 #endif
67 #include "libserveez/util.h"
68
69 #ifdef __MINGW32__
70 /* definitions for Win95..WinME */
71 #define MaxSocketKey HKEY_LOCAL_MACHINE
72 #define MaxSocketSubKey "System\\CurrentControlSet\\Services\\VxD\\MSTCP"
73 #define MaxSocketSubSubKey "MaxConnections"
74 #endif /* defined __MINGW32__ */
75
76 /*
77 * Level of the logging interfaces verbosity:
78 * 0 - only fatal error messages
79 * 1 - error messages
80 * 2 - warnings
81 * 3 - informational messages
82 * 4 - debugging output
83 * Levels always imply numerically lesser levels.
84 */
85
86 static char log_level[][16] = {
87 "fatal",
88 "error",
89 "warning",
90 "notice",
91 "debug"
92 };
93
94 /*
95 * This is the file all log messages are written to. Change it with a
96 * call to @code{svz_log_setfile}. By default, all log messages are written
97 * to @code{stderr}.
98 */
99 static FILE *logfile = NULL;
100
101 /* The logging mutex is necessary only if stdio doesn't do locking. */
102 #ifdef ENABLE_LOG_MUTEX
103
104 static svz_mutex_t spew_mutex = SVZ_MUTEX_INITIALIZER;
105 static int spew_mutex_valid;
106
107 #define LOCK_LOG_MUTEX() \
108 if (spew_mutex_valid) svz_mutex_lock (&spew_mutex)
109 #define UNLOCK_LOG_MUTEX() \
110 if (spew_mutex_valid) svz_mutex_unlock (&spew_mutex)
111
112 #else /* !ENABLE_LOG_MUTEX */
113
114 #define LOCK_LOG_MUTEX()
115 #define UNLOCK_LOG_MUTEX()
116
117 #endif /* !ENABLE_LOG_MUTEX */
118
119 #define LOGBUFSIZE 512
120
121 #ifndef HAVE_FWRITE_UNLOCKED
122 #define SVZ_UNUSED_IF_HAVE_FWRITE_UNLOCKED
123 #else
124 #define SVZ_UNUSED_IF_HAVE_FWRITE_UNLOCKED UNUSED
125 #endif
126
127 void
svz__log_updn(SVZ_UNUSED_IF_HAVE_FWRITE_UNLOCKED int direction)128 svz__log_updn (SVZ_UNUSED_IF_HAVE_FWRITE_UNLOCKED int direction)
129 {
130 #ifndef HAVE_FWRITE_UNLOCKED
131 (direction
132 ? svz_mutex_create
133 : svz_mutex_destroy)
134 (&spew_mutex);
135 spew_mutex_valid = direction;
136 #endif
137 }
138
139 /**
140 * Print a message to the log system. @var{level} specifies the prefix.
141 */
142 void
svz_log(int level,const char * format,...)143 svz_log (int level, const char *format, ...)
144 {
145 char buf[LOGBUFSIZE];
146 size_t w = 0;
147 va_list args;
148 time_t tm;
149 struct tm *t;
150
151 if (level > SVZ_RUNPARM (VERBOSITY) || logfile == NULL ||
152 feof (logfile) || ferror (logfile))
153 return;
154
155 tm = time (NULL);
156 t = localtime (&tm);
157 w = strftime (buf, LOGBUFSIZE, "[%Y/%m/%d %H:%M:%S]", t);
158 w += snprintf (buf + w, LOGBUFSIZE - w, " %s: ", log_level[level]);
159 va_start (args, format);
160 w += vsnprintf (buf + w, LOGBUFSIZE - w, format, args);
161 va_end (args);
162
163 /* Ensure that an overlong message is properly truncated. */
164 if (LOGBUFSIZE > w)
165 assert ('\0' == buf[w]);
166 else
167 {
168 w = LOGBUFSIZE - 1;
169 buf[w - 1] = '\n';
170 buf[w] = '\0';
171 }
172
173 /* Write it out. */
174 LOCK_LOG_MUTEX ();
175 fwrite (buf, 1, w, logfile);
176 fflush (logfile);
177 UNLOCK_LOG_MUTEX ();
178 }
179
180 /**
181 * Set the file stream @var{file} to the log file all messages
182 * are printed to. Can also be @code{stdout} or @code{stderr}.
183 */
184 void
svz_log_setfile(FILE * file)185 svz_log_setfile (FILE * file)
186 {
187 logfile = file;
188 }
189
190 int
svz_pton(const char * str,void * dst)191 svz_pton (const char *str, void *dst)
192 {
193 int rv;
194
195 #if HAVE_INET_PTON
196
197 rv = (1 == inet_pton (AF_INET, str, dst))
198 ? 0
199 : -1;
200
201 #elif HAVE_INET_ATON
202
203 {
204 rv = (1 == inet_aton (str, dst))
205 ? 0
206 : -1;
207 }
208
209 #elif defined (__MINGW32__)
210
211 {
212 struct in_addr *a = dst;
213 struct sockaddr_in addr;
214 size_t len = sizeof (struct sockaddr_in);
215
216 rv = (0 == WSAStringToAddress (str, AF_INET, NULL,
217 (struct sockaddr *) addr, &len))
218 ? 0
219 : -1;
220 if (0 == rv)
221 *a = addr->sin_addr;
222 }
223
224 #else
225
226 {
227 struct in_addr *a = dst;
228
229 *a = inet_addr (str);
230 rv = 0;
231 }
232
233 #endif
234
235 return rv;
236 }
237
238 #define MAX_DUMP_LINE 16 /* bytes per line */
239
240 /**
241 * Dump @var{buffer} with the length @var{len} to the file stream @var{out}.
242 * Display description @var{action} along with origin and size info first,
243 * followed by the hexadecimal text representation.
244 * Stop output at either @var{max} or @var{len} (if @var{max} is zero) bytes.
245 * @var{from} is a numerical identifier of the buffers creator.
246 */
247 int
svz_hexdump(FILE * out,char * action,int from,char * buffer,int len,int max)248 svz_hexdump (FILE *out, char *action, int from,
249 char *buffer, int len, int max)
250 {
251 int row, col, x, max_col;
252
253 if (!max)
254 max = len;
255 if (max > len)
256 max = len;
257 max_col = max / MAX_DUMP_LINE;
258 if ((max % MAX_DUMP_LINE) != 0)
259 max_col++;
260
261 fprintf (out, "%s [ FROM:0x%08X SIZE:%d ]\n", action, (unsigned) from, len);
262
263 for (x = row = 0; row < max_col && x < max; row++)
264 {
265 /* print hexdump */
266 fprintf (out, "%04X ", x);
267 for (col = 0; col < MAX_DUMP_LINE; col++, x++)
268 {
269 if (x < max)
270 fprintf (out, "%02X ", (uint8_t) buffer[x]);
271 else
272 fprintf (out, " ");
273 }
274 /* print character representation */
275 x -= MAX_DUMP_LINE;
276 fprintf (out, " ");
277 for (col = 0; col < MAX_DUMP_LINE && x < max; col++, x++)
278 {
279 fprintf (out, "%c", buffer[x] >= ' ' ? buffer[x] : '.');
280 }
281 fprintf (out, "\n");
282 }
283
284 fflush (out);
285 return 0;
286 }
287
288 /**
289 * Transform the given binary data @var{t} (UTC time) to an ASCII time text
290 * representation without any trailing characters.
291 */
292 char *
svz_time(long t)293 svz_time (long t)
294 {
295 static char *asc;
296 char *p;
297
298 p = asc = ctime ((time_t *) &t);
299 while (*p)
300 p++;
301 while (*p < ' ')
302 *(p--) = '\0';
303
304 return asc;
305 }
306
307 /**
308 * Convert the given string @var{str} to lower case text representation.
309 */
310 char *
svz_tolower(char * str)311 svz_tolower (char *str)
312 {
313 char *p = str;
314
315 while (*p)
316 {
317 *p = (char) (isupper ((uint8_t) * p) ?
318 tolower ((uint8_t) * p) : *p);
319 p++;
320 }
321 return str;
322 }
323
324 #ifdef __MINGW32__
325 /*
326 * This variable contains the last system or network error occurred if
327 * it was detected and printed. Needed for the "Resource unavailable" error
328 * condition.
329 */
330 int svz_errno = 0;
331
332 #define MESSAGE_BUF_SIZE 256
333
334 /*
335 * There is no text representation of network (Winsock API) errors in
336 * Win32. That is why we translate it by hand.
337 */
338 static char *
neterror(int error)339 neterror (int error)
340 {
341 static char message[MESSAGE_BUF_SIZE];
342
343 switch (error)
344 {
345 case WSAEACCES:
346 return "Permission denied.";
347 case WSAEADDRINUSE:
348 return "Address already in use.";
349 case WSAEADDRNOTAVAIL:
350 return "Cannot assign requested address.";
351 case WSAEAFNOSUPPORT:
352 return "Address family not supported by protocol family.";
353 case WSAEALREADY:
354 return "Operation already in progress.";
355 case WSAECONNABORTED:
356 return "Software caused connection abort.";
357 case WSAECONNREFUSED:
358 return "Connection refused.";
359 case WSAECONNRESET:
360 return "Connection reset by peer.";
361 case WSAEDESTADDRREQ:
362 return "Destination address required.";
363 case WSAEFAULT:
364 return "Bad address.";
365 case WSAEHOSTDOWN:
366 return "Host is down.";
367 case WSAEHOSTUNREACH:
368 return "No route to host.";
369 case WSAEINPROGRESS:
370 return "Operation now in progress.";
371 case WSAEINTR:
372 return "Interrupted function call.";
373 case WSAEINVAL:
374 return "Invalid argument.";
375 case WSAEISCONN:
376 return "Socket is already connected.";
377 case WSAEMFILE:
378 return "Too many open files.";
379 case WSAEMSGSIZE:
380 return "Message too long.";
381 case WSAENETDOWN:
382 return "Network is down.";
383 case WSAENETRESET:
384 return "Network dropped connection on reset.";
385 case WSAENETUNREACH:
386 return "Network is unreachable.";
387 case WSAENOBUFS:
388 return "No buffer space available.";
389 case WSAENOPROTOOPT:
390 return "Bad protocol option.";
391 case WSAENOTCONN:
392 return "Socket is not connected.";
393 case WSAENOTSOCK:
394 return "Socket operation on non-socket.";
395 case WSAEOPNOTSUPP:
396 return "Operation not supported.";
397 case WSAEPFNOSUPPORT:
398 return "Protocol family not supported.";
399 case WSAEPROCLIM:
400 return "Too many processes.";
401 case WSAEPROTONOSUPPORT:
402 return "Protocol not supported.";
403 case WSAEPROTOTYPE:
404 return "Protocol wrong type for socket.";
405 case WSAESHUTDOWN:
406 return "Cannot send after socket shutdown.";
407 case WSAESOCKTNOSUPPORT:
408 return "Socket type not supported.";
409 case WSAETIMEDOUT:
410 return "Connection timed out.";
411 case WSAEWOULDBLOCK:
412 return "Resource temporarily unavailable.";
413 case WSAHOST_NOT_FOUND:
414 return "Host not found.";
415 case WSANOTINITIALISED:
416 return "Successful WSAStartup not yet performed.";
417 case WSANO_DATA:
418 return "Valid name, no data record of requested type.";
419 case WSANO_RECOVERY:
420 return "This is a non-recoverable error.";
421 case WSASYSNOTREADY:
422 return "Network subsystem is unavailable.";
423 case WSATRY_AGAIN:
424 return "Non-authoritative host not found.";
425 case WSAVERNOTSUPPORTED:
426 return "WINSOCK.DLL version out of range.";
427 case WSAEDISCON:
428 return "Graceful shutdown in progress.";
429 default:
430 sprintf (message, "Network error code %d.", error);
431 break;
432 }
433 return message;
434 }
435
436 /*
437 * Routine which forms a valid error message under Win32. It might either
438 * use the @code{GetLastError} or @code{WSAGetLastError} in order to
439 * get a valid error code.
440 */
441 static char *
syserror(int nr)442 syserror (int nr)
443 {
444 static char message[MESSAGE_BUF_SIZE];
445
446 /* save the last error */
447 svz_errno = nr;
448
449 /* return a net error if necessary */
450 if (nr >= WSABASEERR)
451 return neterror (nr);
452
453 /*
454 * if the error is not valid (GetLastError returned zero)
455 * fall back to the errno variable of the usual crtdll.
456 */
457 if (!nr)
458 nr = errno;
459
460 /* return a sys error */
461 if (0 == FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
462 FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, nr,
463 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
464 (char *) message, MESSAGE_BUF_SIZE, NULL))
465 {
466 sprintf (message, "FormatMessage (%d): error code %ld",
467 nr, GetLastError ());
468 return message;
469 }
470
471 message[strlen (message) - 2] = 0;
472 return message;
473 }
474
475 /*
476 * This variable contains the the runtime detected Win32 version. Its value
477 * is setup in @code{svz_sys_version} and can be @code{Win32s} for Windows 3.x,
478 * @code{Win95} for Windows 95, @code{Win98} for Windows 98, @code{WinNT3x}
479 * for Windows NT 3.x, @code{WinNT4x} for Windows NT 4.x, @code{Win2k} for
480 * Windows 2000, @code{WinXP} for Windows XP and @code{WinME} for Windows ME.
481 */
482 static int os_version;
483 #define Win32s 0
484 #define Win95 1
485 #define Win98 2
486 #define WinNT3x 3
487 #define WinNT4x 4
488 #define Win2k 5
489 #define WinXP 6
490 #define WinME 7
491
492 #endif /* __MINGW32__ */
493
494 /**
495 * Return a string describing the most recent system error.
496 */
497 const char *
svz_sys_strerror(void)498 svz_sys_strerror (void)
499 {
500 #ifdef __MINGW32__
501 return syserror (GetLastError ());
502 #else
503 return strerror (errno);
504 #endif
505 }
506
507 /*
508 * Return a string describing the most recent network error.
509 */
510 const char *
svz_net_strerror(void)511 svz_net_strerror (void)
512 {
513 #ifdef __MINGW32__
514 return syserror (WSAGetLastError ());
515 #else
516 return strerror (errno);
517 #endif
518 }
519
520 /**
521 * Return 1 if there was a "socket unavailable" error recently, 0
522 * otherwise. This checks @code{svz_errno} against @code{WSAEWOULDBLOCK}
523 * (woe32) or @code{EAGAIN} (Unix).
524 */
525 int
svz_socket_unavailable_error_p(void)526 svz_socket_unavailable_error_p (void)
527 {
528 #ifdef __MINGW32__
529 return WSAEWOULDBLOCK == svz_errno;
530 #else
531 return EAGAIN == svz_errno;
532 #endif
533 }
534
535 /**
536 * Return a statically-allocated string describing some operating system
537 * version details.
538 */
539 char *
svz_sys_version(void)540 svz_sys_version (void)
541 {
542 static char os[256] = ""; /* contains the os string */
543
544 #ifdef __MINGW32__
545 static char ver[][6] =
546 { " 32s", " 95", " 98", " NT", " NT", " 2000", " XP", " ME" };
547 OSVERSIONINFO osver;
548 #elif HAVE_SYS_UTSNAME_H
549 struct utsname buf;
550 #endif
551
552 /* detect only once */
553 if (os[0])
554 return os;
555
556 #ifdef __MINGW32__ /* Windows */
557 osver.dwOSVersionInfoSize = sizeof (osver);
558 if (!GetVersionEx (&osver))
559 {
560 svz_log_sys_error ("GetVersionEx");
561 sprintf (os, "unknown Windows");
562 }
563 else
564 {
565 switch (osver.dwPlatformId)
566 {
567 case VER_PLATFORM_WIN32_NT: /* NT, Windows 2000 or Windows XP */
568 if (osver.dwMajorVersion == 4)
569 os_version = WinNT4x;
570 else if (osver.dwMajorVersion <= 3)
571 os_version = WinNT3x;
572 else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion < 1)
573 os_version = Win2k;
574 else if (osver.dwMajorVersion >= 5)
575 os_version = WinXP;
576 break;
577
578 case VER_PLATFORM_WIN32_WINDOWS: /* Win95 or Win98 */
579 if ((osver.dwMajorVersion > 4) ||
580 ((osver.dwMajorVersion == 4) && (osver.dwMinorVersion > 0)))
581 {
582 if (osver.dwMinorVersion >= 90)
583 os_version = WinME;
584 else
585 os_version = Win98;
586 }
587 else
588 os_version = Win95;
589 break;
590
591 case VER_PLATFORM_WIN32s: /* Windows 3.x */
592 os_version = Win32s;
593 break;
594 }
595
596 sprintf (os, "Windows%s %ld.%02ld %s%s(Build %ld)",
597 ver[os_version],
598 osver.dwMajorVersion, osver.dwMinorVersion,
599 osver.szCSDVersion, osver.szCSDVersion[0] ? " " : "",
600 osver.dwBuildNumber & 0xFFFF);
601 }
602 #elif HAVE_UNAME /* !__MINGW32__ */
603 uname (&buf);
604 sprintf (os, "%s %s on %s", buf.sysname, buf.release, buf.machine);
605 #endif /* not HAVE_UNAME */
606
607 return os;
608 }
609
610 /**
611 * Return 1 if running MinGW (Windows) NT4x or later,
612 * otherwise 0.
613 */
614 int
svz_mingw_at_least_nt4_p(void)615 svz_mingw_at_least_nt4_p (void)
616 {
617 #ifdef __MINGW32__
618 return WinNT4x <= os_version;
619 #else
620 return 0;
621 #endif
622 }
623
624 /**
625 * Convert an unsigned integer to its decimal string representation,
626 * returning a pointer to an internal buffer. (You should copy the result.)
627 */
628 char *
svz_itoa(unsigned int i)629 svz_itoa (unsigned int i)
630 {
631 static char buffer[32];
632 char *p = buffer + sizeof (buffer) - 1;
633
634 *p = '\0';
635 do
636 {
637 p--;
638 *p = (char) ((i % 10) + '0');
639 }
640 while ((i /= 10) != 0);
641 return p;
642 }
643
644 /**
645 * Convert string @var{str} in decimal format to an unsigned integer.
646 * Stop conversion on any invalid characters.
647 */
648 unsigned int
svz_atoi(char * str)649 svz_atoi (char *str)
650 {
651 unsigned int i = 0;
652
653 while (*str >= '0' && *str <= '9')
654 {
655 i *= 10;
656 i += *str - '0';
657 str++;
658 }
659 return i;
660 }
661
662 #ifdef __MINGW32__
663 # define getcwd(buf, size) (GetCurrentDirectory (size, buf) ? buf : NULL)
664 #endif
665
666 /**
667 * Return the current working directory in a newly allocated string.
668 * (You should @code{svz_free} it when done.)
669 */
670 char *
svz_getcwd(void)671 svz_getcwd (void)
672 {
673 char *buf, *dir;
674 int len = 64;
675
676 buf = dir = NULL;
677 do
678 {
679 buf = svz_realloc (buf, len);
680 dir = getcwd (buf, len);
681 len *= 2;
682 }
683 while (dir == NULL);
684
685 return dir;
686 }
687
688 /**
689 * Check for the current and maximum limit of open files of the
690 * current process and try to set the limit to @var{max_sockets}.
691 */
692 int
svz_openfiles(int max_sockets)693 svz_openfiles (int max_sockets)
694 {
695 #if HAVE_GETRLIMIT
696 struct rlimit rlim;
697 #endif
698
699 #if HAVE_GETDTABLESIZE
700 int openfiles;
701
702 if ((openfiles = getdtablesize ()) == -1)
703 {
704 svz_log_sys_error ("getdtablesize");
705 return -1;
706 }
707 svz_log (SVZ_LOG_NOTICE, "file descriptor table size: %d\n", openfiles);
708 #endif /* HAVE_GETDTABLESIZE */
709
710 #if HAVE_GETRLIMIT
711
712 # ifndef RLIMIT_NOFILE
713 # define RLIMIT_NOFILE RLIMIT_OFILE
714 # endif
715
716 if (getrlimit (RLIMIT_NOFILE, &rlim) == -1)
717 {
718 svz_log_sys_error ("getrlimit");
719 return -1;
720 }
721 svz_log (SVZ_LOG_NOTICE, "current open file limit: %d/%d\n",
722 rlim.rlim_cur, rlim.rlim_max);
723
724 if ((int) rlim.rlim_max < (int) max_sockets ||
725 (int) rlim.rlim_cur < (int) max_sockets)
726 {
727 rlim.rlim_max = max_sockets;
728 rlim.rlim_cur = max_sockets;
729
730 if (setrlimit (RLIMIT_NOFILE, &rlim) == -1)
731 {
732 svz_log_sys_error ("setrlimit");
733 return -1;
734 }
735 getrlimit (RLIMIT_NOFILE, &rlim);
736 svz_log (SVZ_LOG_NOTICE, "open file limit set to: %d/%d\n",
737 rlim.rlim_cur, rlim.rlim_max);
738 }
739
740 #elif defined (__MINGW32__) /* HAVE_GETRLIMIT */
741
742 unsigned sockets = 100;
743
744 if (os_version == Win95 ||
745 os_version == Win98 || os_version == WinME)
746 {
747 if (os_version == Win95)
748 sockets = svz_windoze_get_reg_unsigned (MaxSocketKey,
749 MaxSocketSubKey,
750 MaxSocketSubSubKey, sockets);
751 else
752 sockets = svz_atoi (svz_windoze_get_reg_string (MaxSocketKey,
753 MaxSocketSubKey,
754 MaxSocketSubSubKey,
755 svz_itoa (sockets)));
756
757 svz_log (SVZ_LOG_NOTICE, "current open file limit: %u\n", sockets);
758
759 if (sockets < (unsigned) max_sockets)
760 {
761 sockets = max_sockets;
762
763 if (os_version == Win95)
764 svz_windoze_set_reg_unsigned (MaxSocketKey,
765 MaxSocketSubKey,
766 MaxSocketSubSubKey, sockets);
767 else
768 svz_windoze_set_reg_string (MaxSocketKey,
769 MaxSocketSubKey,
770 MaxSocketSubSubKey,
771 svz_itoa (sockets));
772
773 svz_log (SVZ_LOG_NOTICE, "open file limit set to: %u\n", sockets);
774 }
775 }
776 #endif /* MINGW32__ */
777
778 return 0;
779 }
780
781 #define PREFIX_SIZE 256 /* 255 + one for '\0' */
782 #define ERRMSG_SIZE 128
783
784 static void
save_errmsg(char * buf,char const * source)785 save_errmsg (char *buf, char const *source)
786 {
787 *buf = '\0';
788 strncat (buf, source, ERRMSG_SIZE - 1);
789 }
790
791 static void
log_error(char const * prefix,char const * errmsg)792 log_error (char const *prefix, char const *errmsg)
793 {
794 svz_log (SVZ_LOG_ERROR, "%s: %s\n", prefix, errmsg);
795 }
796
797 #define LOG_ERROR_FROM(SOURCE) do \
798 { \
799 char prefix[PREFIX_SIZE]; \
800 char errmsg[ERRMSG_SIZE]; \
801 va_list args; \
802 \
803 save_errmsg (errmsg, SOURCE); \
804 \
805 va_start (args, fmt); \
806 vsnprintf (prefix, PREFIX_SIZE, fmt, args); \
807 va_end (args); \
808 \
809 log_error (prefix, errmsg); \
810 } \
811 while (0)
812
813 /**
814 * Log the current @dfn{system error}.
815 */
816 void
svz_log_sys_error(char const * fmt,...)817 svz_log_sys_error (char const *fmt, ...)
818 {
819 LOG_ERROR_FROM (svz_sys_strerror ());
820 }
821
822 /**
823 * Log the current @dfn{network error}.
824 */
825 void
svz_log_net_error(char const * fmt,...)826 svz_log_net_error (char const *fmt, ...)
827 {
828 LOG_ERROR_FROM (svz_net_strerror ());
829 }
830