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