Annotation of embedaddon/curl/tests/server/util.c, revision 1.1

1.1     ! misho       1: /***************************************************************************
        !             2:  *                                  _   _ ____  _
        !             3:  *  Project                     ___| | | |  _ \| |
        !             4:  *                             / __| | | | |_) | |
        !             5:  *                            | (__| |_| |  _ <| |___
        !             6:  *                             \___|\___/|_| \_\_____|
        !             7:  *
        !             8:  * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
        !             9:  *
        !            10:  * This software is licensed as described in the file COPYING, which
        !            11:  * you should have received as part of this distribution. The terms
        !            12:  * are also available at https://curl.haxx.se/docs/copyright.html.
        !            13:  *
        !            14:  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
        !            15:  * copies of the Software, and permit persons to whom the Software is
        !            16:  * furnished to do so, under the terms of the COPYING file.
        !            17:  *
        !            18:  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
        !            19:  * KIND, either express or implied.
        !            20:  *
        !            21:  ***************************************************************************/
        !            22: #include "server_setup.h"
        !            23: 
        !            24: #ifdef HAVE_SIGNAL_H
        !            25: #include <signal.h>
        !            26: #endif
        !            27: #ifdef HAVE_NETINET_IN_H
        !            28: #include <netinet/in.h>
        !            29: #endif
        !            30: #ifdef _XOPEN_SOURCE_EXTENDED
        !            31: /* This define is "almost" required to build on HPUX 11 */
        !            32: #include <arpa/inet.h>
        !            33: #endif
        !            34: #ifdef HAVE_NETDB_H
        !            35: #include <netdb.h>
        !            36: #endif
        !            37: #ifdef HAVE_POLL_H
        !            38: #include <poll.h>
        !            39: #elif defined(HAVE_SYS_POLL_H)
        !            40: #include <sys/poll.h>
        !            41: #endif
        !            42: #ifdef __MINGW32__
        !            43: #include <w32api.h>
        !            44: #endif
        !            45: 
        !            46: #define ENABLE_CURLX_PRINTF
        !            47: /* make the curlx header define all printf() functions to use the curlx_*
        !            48:    versions instead */
        !            49: #include "curlx.h" /* from the private lib dir */
        !            50: #include "getpart.h"
        !            51: #include "util.h"
        !            52: #include "timeval.h"
        !            53: 
        !            54: #ifdef USE_WINSOCK
        !            55: #undef  EINTR
        !            56: #define EINTR    4 /* errno.h value */
        !            57: #undef  EINVAL
        !            58: #define EINVAL  22 /* errno.h value */
        !            59: #endif
        !            60: 
        !            61: /* MinGW with w32api version < 3.6 declared in6addr_any as extern,
        !            62:    but lacked the definition */
        !            63: #if defined(ENABLE_IPV6) && defined(__MINGW32__)
        !            64: #if (__W32API_MAJOR_VERSION < 3) || \
        !            65:     ((__W32API_MAJOR_VERSION == 3) && (__W32API_MINOR_VERSION < 6))
        !            66: const struct in6_addr in6addr_any = {{ IN6ADDR_ANY_INIT }};
        !            67: #endif /* w32api < 3.6 */
        !            68: #endif /* ENABLE_IPV6 && __MINGW32__*/
        !            69: 
        !            70: static struct timeval tvnow(void);
        !            71: 
        !            72: /* This function returns a pointer to STATIC memory. It converts the given
        !            73:  * binary lump to a hex formatted string usable for output in logs or
        !            74:  * whatever.
        !            75:  */
        !            76: char *data_to_hex(char *data, size_t len)
        !            77: {
        !            78:   static char buf[256*3];
        !            79:   size_t i;
        !            80:   char *optr = buf;
        !            81:   char *iptr = data;
        !            82: 
        !            83:   if(len > 255)
        !            84:     len = 255;
        !            85: 
        !            86:   for(i = 0; i < len; i++) {
        !            87:     if((data[i] >= 0x20) && (data[i] < 0x7f))
        !            88:       *optr++ = *iptr++;
        !            89:     else {
        !            90:       msnprintf(optr, 4, "%%%02x", *iptr++);
        !            91:       optr += 3;
        !            92:     }
        !            93:   }
        !            94:   *optr = 0; /* in case no sprintf was used */
        !            95: 
        !            96:   return buf;
        !            97: }
        !            98: 
        !            99: void logmsg(const char *msg, ...)
        !           100: {
        !           101:   va_list ap;
        !           102:   char buffer[2048 + 1];
        !           103:   FILE *logfp;
        !           104:   struct timeval tv;
        !           105:   time_t sec;
        !           106:   struct tm *now;
        !           107:   char timebuf[20];
        !           108:   static time_t epoch_offset;
        !           109:   static int    known_offset;
        !           110: 
        !           111:   if(!serverlogfile) {
        !           112:     fprintf(stderr, "Error: serverlogfile not set\n");
        !           113:     return;
        !           114:   }
        !           115: 
        !           116:   tv = tvnow();
        !           117:   if(!known_offset) {
        !           118:     epoch_offset = time(NULL) - tv.tv_sec;
        !           119:     known_offset = 1;
        !           120:   }
        !           121:   sec = epoch_offset + tv.tv_sec;
        !           122:   now = localtime(&sec); /* not thread safe but we don't care */
        !           123: 
        !           124:   msnprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld",
        !           125:             (int)now->tm_hour, (int)now->tm_min, (int)now->tm_sec,
        !           126:             (long)tv.tv_usec);
        !           127: 
        !           128:   va_start(ap, msg);
        !           129:   mvsnprintf(buffer, sizeof(buffer), msg, ap);
        !           130:   va_end(ap);
        !           131: 
        !           132:   logfp = fopen(serverlogfile, "ab");
        !           133:   if(logfp) {
        !           134:     fprintf(logfp, "%s %s\n", timebuf, buffer);
        !           135:     fclose(logfp);
        !           136:   }
        !           137:   else {
        !           138:     int error = errno;
        !           139:     fprintf(stderr, "fopen() failed with error: %d %s\n",
        !           140:             error, strerror(error));
        !           141:     fprintf(stderr, "Error opening file: %s\n", serverlogfile);
        !           142:     fprintf(stderr, "Msg not logged: %s %s\n", timebuf, buffer);
        !           143:   }
        !           144: }
        !           145: 
        !           146: #ifdef WIN32
        !           147: /* use instead of perror() on generic windows */
        !           148: void win32_perror(const char *msg)
        !           149: {
        !           150:   char buf[512];
        !           151:   DWORD err = SOCKERRNO;
        !           152: 
        !           153:   if(!FormatMessageA((FORMAT_MESSAGE_FROM_SYSTEM |
        !           154:                       FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err,
        !           155:                      LANG_NEUTRAL, buf, sizeof(buf), NULL))
        !           156:     msnprintf(buf, sizeof(buf), "Unknown error %lu (%#lx)", err, err);
        !           157:   if(msg)
        !           158:     fprintf(stderr, "%s: ", msg);
        !           159:   fprintf(stderr, "%s\n", buf);
        !           160: }
        !           161: #endif  /* WIN32 */
        !           162: 
        !           163: #ifdef USE_WINSOCK
        !           164: void win32_init(void)
        !           165: {
        !           166:   WORD wVersionRequested;
        !           167:   WSADATA wsaData;
        !           168:   int err;
        !           169:   wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
        !           170: 
        !           171:   err = WSAStartup(wVersionRequested, &wsaData);
        !           172: 
        !           173:   if(err != 0) {
        !           174:     perror("Winsock init failed");
        !           175:     logmsg("Error initialising winsock -- aborting");
        !           176:     exit(1);
        !           177:   }
        !           178: 
        !           179:   if(LOBYTE(wsaData.wVersion) != USE_WINSOCK ||
        !           180:      HIBYTE(wsaData.wVersion) != USE_WINSOCK) {
        !           181:     WSACleanup();
        !           182:     perror("Winsock init failed");
        !           183:     logmsg("No suitable winsock.dll found -- aborting");
        !           184:     exit(1);
        !           185:   }
        !           186: }
        !           187: 
        !           188: void win32_cleanup(void)
        !           189: {
        !           190:   WSACleanup();
        !           191: }
        !           192: #endif  /* USE_WINSOCK */
        !           193: 
        !           194: /* set by the main code to point to where the test dir is */
        !           195: const char *path = ".";
        !           196: 
        !           197: FILE *test2fopen(long testno)
        !           198: {
        !           199:   FILE *stream;
        !           200:   char filename[256];
        !           201:   /* first try the alternative, preprocessed, file */
        !           202:   msnprintf(filename, sizeof(filename), ALTTEST_DATA_PATH, path, testno);
        !           203:   stream = fopen(filename, "rb");
        !           204:   if(stream)
        !           205:     return stream;
        !           206: 
        !           207:   /* then try the source version */
        !           208:   msnprintf(filename, sizeof(filename), TEST_DATA_PATH, path, testno);
        !           209:   stream = fopen(filename, "rb");
        !           210: 
        !           211:   return stream;
        !           212: }
        !           213: 
        !           214: /*
        !           215:  * Portable function used for waiting a specific amount of ms.
        !           216:  * Waiting indefinitely with this function is not allowed, a
        !           217:  * zero or negative timeout value will return immediately.
        !           218:  *
        !           219:  * Return values:
        !           220:  *   -1 = system call error, or invalid timeout value
        !           221:  *    0 = specified timeout has elapsed
        !           222:  */
        !           223: int wait_ms(int timeout_ms)
        !           224: {
        !           225: #if !defined(MSDOS) && !defined(USE_WINSOCK)
        !           226: #ifndef HAVE_POLL_FINE
        !           227:   struct timeval pending_tv;
        !           228: #endif
        !           229:   struct timeval initial_tv;
        !           230:   int pending_ms;
        !           231: #endif
        !           232:   int r = 0;
        !           233: 
        !           234:   if(!timeout_ms)
        !           235:     return 0;
        !           236:   if(timeout_ms < 0) {
        !           237:     errno = EINVAL;
        !           238:     return -1;
        !           239:   }
        !           240: #if defined(MSDOS)
        !           241:   delay(timeout_ms);
        !           242: #elif defined(USE_WINSOCK)
        !           243:   Sleep(timeout_ms);
        !           244: #else
        !           245:   pending_ms = timeout_ms;
        !           246:   initial_tv = tvnow();
        !           247:   do {
        !           248:     int error;
        !           249: #if defined(HAVE_POLL_FINE)
        !           250:     r = poll(NULL, 0, pending_ms);
        !           251: #else
        !           252:     pending_tv.tv_sec = pending_ms / 1000;
        !           253:     pending_tv.tv_usec = (pending_ms % 1000) * 1000;
        !           254:     r = select(0, NULL, NULL, NULL, &pending_tv);
        !           255: #endif /* HAVE_POLL_FINE */
        !           256:     if(r != -1)
        !           257:       break;
        !           258:     error = errno;
        !           259:     if(error && (error != EINTR))
        !           260:       break;
        !           261:     pending_ms = timeout_ms - (int)timediff(tvnow(), initial_tv);
        !           262:     if(pending_ms <= 0)
        !           263:       break;
        !           264:   } while(r == -1);
        !           265: #endif /* USE_WINSOCK */
        !           266:   if(r)
        !           267:     r = -1;
        !           268:   return r;
        !           269: }
        !           270: 
        !           271: int write_pidfile(const char *filename)
        !           272: {
        !           273:   FILE *pidfile;
        !           274:   curl_off_t pid;
        !           275: 
        !           276:   pid = (curl_off_t)getpid();
        !           277:   pidfile = fopen(filename, "wb");
        !           278:   if(!pidfile) {
        !           279:     logmsg("Couldn't write pid file: %s %s", filename, strerror(errno));
        !           280:     return 0; /* fail */
        !           281:   }
        !           282: #if defined(WIN32) || defined(_WIN32)
        !           283:   /* store pid + 65536 to avoid conflict with Cygwin/msys PIDs, see also:
        !           284:    * - https://cygwin.com/git/?p=newlib-cygwin.git;a=commit;        !           285:    *   h=b5e1003722cb14235c4f166be72c09acdffc62ea
        !           286:    * - https://cygwin.com/git/?p=newlib-cygwin.git;a=commit;        !           287:    *   h=448cf5aa4b429d5a9cebf92a0da4ab4b5b6d23fe
        !           288:    */
        !           289:   pid += 65536;
        !           290: #endif
        !           291:   fprintf(pidfile, "%" CURL_FORMAT_CURL_OFF_T "\n", pid);
        !           292:   fclose(pidfile);
        !           293:   logmsg("Wrote pid %ld to %s", pid, filename);
        !           294:   return 1; /* success */
        !           295: }
        !           296: 
        !           297: /* store the used port number in a file */
        !           298: int write_portfile(const char *filename, int port)
        !           299: {
        !           300:   FILE *portfile = fopen(filename, "wb");
        !           301:   if(!portfile) {
        !           302:     logmsg("Couldn't write port file: %s %s", filename, strerror(errno));
        !           303:     return 0; /* fail */
        !           304:   }
        !           305:   fprintf(portfile, "%d\n", port);
        !           306:   fclose(portfile);
        !           307:   logmsg("Wrote port %d to %s", port, filename);
        !           308:   return 1; /* success */
        !           309: }
        !           310: 
        !           311: void set_advisor_read_lock(const char *filename)
        !           312: {
        !           313:   FILE *lockfile;
        !           314:   int error = 0;
        !           315:   int res;
        !           316: 
        !           317:   do {
        !           318:     lockfile = fopen(filename, "wb");
        !           319:   } while((lockfile == NULL) && ((error = errno) == EINTR));
        !           320:   if(lockfile == NULL) {
        !           321:     logmsg("Error creating lock file %s error: %d %s",
        !           322:            filename, error, strerror(error));
        !           323:     return;
        !           324:   }
        !           325: 
        !           326:   do {
        !           327:     res = fclose(lockfile);
        !           328:   } while(res && ((error = errno) == EINTR));
        !           329:   if(res)
        !           330:     logmsg("Error closing lock file %s error: %d %s",
        !           331:            filename, error, strerror(error));
        !           332: }
        !           333: 
        !           334: void clear_advisor_read_lock(const char *filename)
        !           335: {
        !           336:   int error = 0;
        !           337:   int res;
        !           338: 
        !           339:   /*
        !           340:   ** Log all removal failures. Even those due to file not existing.
        !           341:   ** This allows to detect if unexpectedly the file has already been
        !           342:   ** removed by a process different than the one that should do this.
        !           343:   */
        !           344: 
        !           345:   do {
        !           346:     res = unlink(filename);
        !           347:   } while(res && ((error = errno) == EINTR));
        !           348:   if(res)
        !           349:     logmsg("Error removing lock file %s error: %d %s",
        !           350:            filename, error, strerror(error));
        !           351: }
        !           352: 
        !           353: 
        !           354: /* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
        !           355:    its behavior is altered by the current locale. */
        !           356: static char raw_toupper(char in)
        !           357: {
        !           358: #if !defined(CURL_DOES_CONVERSIONS)
        !           359:   if(in >= 'a' && in <= 'z')
        !           360:     return (char)('A' + in - 'a');
        !           361: #else
        !           362:   switch(in) {
        !           363:   case 'a':
        !           364:     return 'A';
        !           365:   case 'b':
        !           366:     return 'B';
        !           367:   case 'c':
        !           368:     return 'C';
        !           369:   case 'd':
        !           370:     return 'D';
        !           371:   case 'e':
        !           372:     return 'E';
        !           373:   case 'f':
        !           374:     return 'F';
        !           375:   case 'g':
        !           376:     return 'G';
        !           377:   case 'h':
        !           378:     return 'H';
        !           379:   case 'i':
        !           380:     return 'I';
        !           381:   case 'j':
        !           382:     return 'J';
        !           383:   case 'k':
        !           384:     return 'K';
        !           385:   case 'l':
        !           386:     return 'L';
        !           387:   case 'm':
        !           388:     return 'M';
        !           389:   case 'n':
        !           390:     return 'N';
        !           391:   case 'o':
        !           392:     return 'O';
        !           393:   case 'p':
        !           394:     return 'P';
        !           395:   case 'q':
        !           396:     return 'Q';
        !           397:   case 'r':
        !           398:     return 'R';
        !           399:   case 's':
        !           400:     return 'S';
        !           401:   case 't':
        !           402:     return 'T';
        !           403:   case 'u':
        !           404:     return 'U';
        !           405:   case 'v':
        !           406:     return 'V';
        !           407:   case 'w':
        !           408:     return 'W';
        !           409:   case 'x':
        !           410:     return 'X';
        !           411:   case 'y':
        !           412:     return 'Y';
        !           413:   case 'z':
        !           414:     return 'Z';
        !           415:   }
        !           416: #endif
        !           417: 
        !           418:   return in;
        !           419: }
        !           420: 
        !           421: int strncasecompare(const char *first, const char *second, size_t max)
        !           422: {
        !           423:   while(*first && *second && max) {
        !           424:     if(raw_toupper(*first) != raw_toupper(*second)) {
        !           425:       break;
        !           426:     }
        !           427:     max--;
        !           428:     first++;
        !           429:     second++;
        !           430:   }
        !           431:   if(0 == max)
        !           432:     return 1; /* they are equal this far */
        !           433: 
        !           434:   return raw_toupper(*first) == raw_toupper(*second);
        !           435: }
        !           436: 
        !           437: #if defined(WIN32) && !defined(MSDOS)
        !           438: 
        !           439: static struct timeval tvnow(void)
        !           440: {
        !           441:   /*
        !           442:   ** GetTickCount() is available on _all_ Windows versions from W95 up
        !           443:   ** to nowadays. Returns milliseconds elapsed since last system boot,
        !           444:   ** increases monotonically and wraps once 49.7 days have elapsed.
        !           445:   **
        !           446:   ** GetTickCount64() is available on Windows version from Windows Vista
        !           447:   ** and Windows Server 2008 up to nowadays. The resolution of the
        !           448:   ** function is limited to the resolution of the system timer, which
        !           449:   ** is typically in the range of 10 milliseconds to 16 milliseconds.
        !           450:   */
        !           451:   struct timeval now;
        !           452: #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) && \
        !           453:     (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR))
        !           454:   ULONGLONG milliseconds = GetTickCount64();
        !           455: #else
        !           456:   DWORD milliseconds = GetTickCount();
        !           457: #endif
        !           458:   now.tv_sec = (long)(milliseconds / 1000);
        !           459:   now.tv_usec = (long)((milliseconds % 1000) * 1000);
        !           460:   return now;
        !           461: }
        !           462: 
        !           463: #elif defined(HAVE_CLOCK_GETTIME_MONOTONIC)
        !           464: 
        !           465: static struct timeval tvnow(void)
        !           466: {
        !           467:   /*
        !           468:   ** clock_gettime() is granted to be increased monotonically when the
        !           469:   ** monotonic clock is queried. Time starting point is unspecified, it
        !           470:   ** could be the system start-up time, the Epoch, or something else,
        !           471:   ** in any case the time starting point does not change once that the
        !           472:   ** system has started up.
        !           473:   */
        !           474:   struct timeval now;
        !           475:   struct timespec tsnow;
        !           476:   if(0 == clock_gettime(CLOCK_MONOTONIC, &tsnow)) {
        !           477:     now.tv_sec = tsnow.tv_sec;
        !           478:     now.tv_usec = tsnow.tv_nsec / 1000;
        !           479:   }
        !           480:   /*
        !           481:   ** Even when the configure process has truly detected monotonic clock
        !           482:   ** availability, it might happen that it is not actually available at
        !           483:   ** run-time. When this occurs simply fallback to other time source.
        !           484:   */
        !           485: #ifdef HAVE_GETTIMEOFDAY
        !           486:   else
        !           487:     (void)gettimeofday(&now, NULL);
        !           488: #else
        !           489:   else {
        !           490:     now.tv_sec = (long)time(NULL);
        !           491:     now.tv_usec = 0;
        !           492:   }
        !           493: #endif
        !           494:   return now;
        !           495: }
        !           496: 
        !           497: #elif defined(HAVE_GETTIMEOFDAY)
        !           498: 
        !           499: static struct timeval tvnow(void)
        !           500: {
        !           501:   /*
        !           502:   ** gettimeofday() is not granted to be increased monotonically, due to
        !           503:   ** clock drifting and external source time synchronization it can jump
        !           504:   ** forward or backward in time.
        !           505:   */
        !           506:   struct timeval now;
        !           507:   (void)gettimeofday(&now, NULL);
        !           508:   return now;
        !           509: }
        !           510: 
        !           511: #else
        !           512: 
        !           513: static struct timeval tvnow(void)
        !           514: {
        !           515:   /*
        !           516:   ** time() returns the value of time in seconds since the Epoch.
        !           517:   */
        !           518:   struct timeval now;
        !           519:   now.tv_sec = (long)time(NULL);
        !           520:   now.tv_usec = 0;
        !           521:   return now;
        !           522: }
        !           523: 
        !           524: #endif
        !           525: 
        !           526: long timediff(struct timeval newer, struct timeval older)
        !           527: {
        !           528:   timediff_t diff = newer.tv_sec-older.tv_sec;
        !           529:   if(diff >= (LONG_MAX/1000))
        !           530:     return LONG_MAX;
        !           531:   else if(diff <= (LONG_MIN/1000))
        !           532:     return LONG_MIN;
        !           533:   return (long)(newer.tv_sec-older.tv_sec)*1000+
        !           534:     (long)(newer.tv_usec-older.tv_usec)/1000;
        !           535: }
        !           536: 
        !           537: /* do-nothing macro replacement for systems which lack siginterrupt() */
        !           538: 
        !           539: #ifndef HAVE_SIGINTERRUPT
        !           540: #define siginterrupt(x,y) do {} while(0)
        !           541: #endif
        !           542: 
        !           543: /* vars used to keep around previous signal handlers */
        !           544: 
        !           545: typedef RETSIGTYPE (*SIGHANDLER_T)(int);
        !           546: 
        !           547: #ifdef SIGHUP
        !           548: static SIGHANDLER_T old_sighup_handler  = SIG_ERR;
        !           549: #endif
        !           550: 
        !           551: #ifdef SIGPIPE
        !           552: static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
        !           553: #endif
        !           554: 
        !           555: #ifdef SIGALRM
        !           556: static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
        !           557: #endif
        !           558: 
        !           559: #ifdef SIGINT
        !           560: static SIGHANDLER_T old_sigint_handler  = SIG_ERR;
        !           561: #endif
        !           562: 
        !           563: #ifdef SIGTERM
        !           564: static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
        !           565: #endif
        !           566: 
        !           567: #if defined(SIGBREAK) && defined(WIN32)
        !           568: static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
        !           569: #endif
        !           570: 
        !           571: #ifdef WIN32
        !           572: static DWORD thread_main_id = 0;
        !           573: static HANDLE thread_main_window = NULL;
        !           574: static HWND hidden_main_window = NULL;
        !           575: #endif
        !           576: 
        !           577: /* var which if set indicates that the program should finish execution */
        !           578: volatile int got_exit_signal = 0;
        !           579: 
        !           580: /* if next is set indicates the first signal handled in exit_signal_handler */
        !           581: volatile int exit_signal = 0;
        !           582: 
        !           583: /* signal handler that will be triggered to indicate that the program
        !           584:  * should finish its execution in a controlled manner as soon as possible.
        !           585:  * The first time this is called it will set got_exit_signal to one and
        !           586:  * store in exit_signal the signal that triggered its execution.
        !           587:  */
        !           588: static RETSIGTYPE exit_signal_handler(int signum)
        !           589: {
        !           590:   int old_errno = errno;
        !           591:   logmsg("exit_signal_handler: %d", signum);
        !           592:   if(got_exit_signal == 0) {
        !           593:     got_exit_signal = 1;
        !           594:     exit_signal = signum;
        !           595:   }
        !           596:   (void)signal(signum, exit_signal_handler);
        !           597:   errno = old_errno;
        !           598: }
        !           599: 
        !           600: #ifdef WIN32
        !           601: /* CTRL event handler for Windows Console applications to simulate
        !           602:  * SIGINT, SIGTERM and SIGBREAK on CTRL events and trigger signal handler.
        !           603:  *
        !           604:  * Background information from MSDN:
        !           605:  * SIGINT is not supported for any Win32 application. When a CTRL+C
        !           606:  * interrupt occurs, Win32 operating systems generate a new thread
        !           607:  * to specifically handle that interrupt. This can cause a single-thread
        !           608:  * application, such as one in UNIX, to become multithreaded and cause
        !           609:  * unexpected behavior.
        !           610:  * [...]
        !           611:  * The SIGILL and SIGTERM signals are not generated under Windows.
        !           612:  * They are included for ANSI compatibility. Therefore, you can set
        !           613:  * signal handlers for these signals by using signal, and you can also
        !           614:  * explicitly generate these signals by calling raise. Source:
        !           615:  * https://docs.microsoft.com/de-de/cpp/c-runtime-library/reference/signal
        !           616:  */
        !           617: static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType)
        !           618: {
        !           619:   int signum = 0;
        !           620:   logmsg("ctrl_event_handler: %d", dwCtrlType);
        !           621:   switch(dwCtrlType) {
        !           622: #ifdef SIGINT
        !           623:     case CTRL_C_EVENT: signum = SIGINT; break;
        !           624: #endif
        !           625: #ifdef SIGTERM
        !           626:     case CTRL_CLOSE_EVENT: signum = SIGTERM; break;
        !           627: #endif
        !           628: #ifdef SIGBREAK
        !           629:     case CTRL_BREAK_EVENT: signum = SIGBREAK; break;
        !           630: #endif
        !           631:     default: return FALSE;
        !           632:   }
        !           633:   if(signum) {
        !           634:     logmsg("ctrl_event_handler: %d -> %d", dwCtrlType, signum);
        !           635:     exit_signal_handler(signum);
        !           636:   }
        !           637:   return TRUE;
        !           638: }
        !           639: /* Window message handler for Windows applications to add support
        !           640:  * for graceful process termination via taskkill (without /f) which
        !           641:  * sends WM_CLOSE to all Windows of a process (even hidden ones).
        !           642:  *
        !           643:  * Therefore we create and run a hidden Window in a separate thread
        !           644:  * to receive and handle the WM_CLOSE message as SIGTERM signal.
        !           645:  */
        !           646: static LRESULT CALLBACK main_window_proc(HWND hwnd, UINT uMsg,
        !           647:                                          WPARAM wParam, LPARAM lParam)
        !           648: {
        !           649:   int signum = 0;
        !           650:   if(hwnd == hidden_main_window) {
        !           651:     switch(uMsg) {
        !           652: #ifdef SIGTERM
        !           653:       case WM_CLOSE: signum = SIGTERM; break;
        !           654: #endif
        !           655:       case WM_DESTROY: PostQuitMessage(0); break;
        !           656:     }
        !           657:     if(signum) {
        !           658:       logmsg("main_window_proc: %d -> %d", uMsg, signum);
        !           659:       exit_signal_handler(signum);
        !           660:     }
        !           661:   }
        !           662:   return DefWindowProc(hwnd, uMsg, wParam, lParam);
        !           663: }
        !           664: /* Window message queue loop for hidden main window, details see above.
        !           665:  */
        !           666: static DWORD WINAPI main_window_loop(LPVOID lpParameter)
        !           667: {
        !           668:   WNDCLASS wc;
        !           669:   BOOL ret;
        !           670:   MSG msg;
        !           671: 
        !           672:   ZeroMemory(&wc, sizeof(wc));
        !           673:   wc.lpfnWndProc = (WNDPROC)main_window_proc;
        !           674:   wc.hInstance = (HINSTANCE)lpParameter;
        !           675:   wc.lpszClassName = "MainWClass";
        !           676:   if(!RegisterClass(&wc)) {
        !           677:     perror("RegisterClass failed");
        !           678:     return (DWORD)-1;
        !           679:   }
        !           680: 
        !           681:   hidden_main_window = CreateWindowEx(0, "MainWClass", "Recv WM_CLOSE msg",
        !           682:                                       WS_OVERLAPPEDWINDOW,
        !           683:                                       CW_USEDEFAULT, CW_USEDEFAULT,
        !           684:                                       CW_USEDEFAULT, CW_USEDEFAULT,
        !           685:                                       (HWND)NULL, (HMENU)NULL,
        !           686:                                       wc.hInstance, (LPVOID)NULL);
        !           687:   if(!hidden_main_window) {
        !           688:     perror("CreateWindowEx failed");
        !           689:     return (DWORD)-1;
        !           690:   }
        !           691: 
        !           692:   do {
        !           693:     ret = GetMessage(&msg, NULL, 0, 0);
        !           694:     if(ret == -1) {
        !           695:       perror("GetMessage failed");
        !           696:       return (DWORD)-1;
        !           697:     }
        !           698:     else if(ret) {
        !           699:       if(msg.message == WM_APP) {
        !           700:         DestroyWindow(hidden_main_window);
        !           701:       }
        !           702:       else if(msg.hwnd && !TranslateMessage(&msg)) {
        !           703:         DispatchMessage(&msg);
        !           704:       }
        !           705:     }
        !           706:   } while(ret);
        !           707: 
        !           708:   hidden_main_window = NULL;
        !           709:   return (DWORD)msg.wParam;
        !           710: }
        !           711: #endif
        !           712: 
        !           713: void install_signal_handlers(bool keep_sigalrm)
        !           714: {
        !           715: #ifdef SIGHUP
        !           716:   /* ignore SIGHUP signal */
        !           717:   old_sighup_handler = signal(SIGHUP, SIG_IGN);
        !           718:   if(old_sighup_handler == SIG_ERR)
        !           719:     logmsg("cannot install SIGHUP handler: %s", strerror(errno));
        !           720: #endif
        !           721: #ifdef SIGPIPE
        !           722:   /* ignore SIGPIPE signal */
        !           723:   old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
        !           724:   if(old_sigpipe_handler == SIG_ERR)
        !           725:     logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
        !           726: #endif
        !           727: #ifdef SIGALRM
        !           728:   if(!keep_sigalrm) {
        !           729:     /* ignore SIGALRM signal */
        !           730:     old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
        !           731:     if(old_sigalrm_handler == SIG_ERR)
        !           732:       logmsg("cannot install SIGALRM handler: %s", strerror(errno));
        !           733:   }
        !           734: #else
        !           735:   (void)keep_sigalrm;
        !           736: #endif
        !           737: #ifdef SIGINT
        !           738:   /* handle SIGINT signal with our exit_signal_handler */
        !           739:   old_sigint_handler = signal(SIGINT, exit_signal_handler);
        !           740:   if(old_sigint_handler == SIG_ERR)
        !           741:     logmsg("cannot install SIGINT handler: %s", strerror(errno));
        !           742:   else
        !           743:     siginterrupt(SIGINT, 1);
        !           744: #endif
        !           745: #ifdef SIGTERM
        !           746:   /* handle SIGTERM signal with our exit_signal_handler */
        !           747:   old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
        !           748:   if(old_sigterm_handler == SIG_ERR)
        !           749:     logmsg("cannot install SIGTERM handler: %s", strerror(errno));
        !           750:   else
        !           751:     siginterrupt(SIGTERM, 1);
        !           752: #endif
        !           753: #if defined(SIGBREAK) && defined(WIN32)
        !           754:   /* handle SIGBREAK signal with our exit_signal_handler */
        !           755:   old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
        !           756:   if(old_sigbreak_handler == SIG_ERR)
        !           757:     logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
        !           758:   else
        !           759:     siginterrupt(SIGBREAK, 1);
        !           760: #endif
        !           761: #ifdef WIN32
        !           762:   if(!SetConsoleCtrlHandler(ctrl_event_handler, TRUE))
        !           763:     logmsg("cannot install CTRL event handler");
        !           764:   thread_main_window = CreateThread(NULL, 0,
        !           765:                                     &main_window_loop,
        !           766:                                     (LPVOID)GetModuleHandle(NULL),
        !           767:                                     0, &thread_main_id);
        !           768:   if(!thread_main_window || !thread_main_id)
        !           769:     logmsg("cannot start main window loop");
        !           770: #endif
        !           771: }
        !           772: 
        !           773: void restore_signal_handlers(bool keep_sigalrm)
        !           774: {
        !           775: #ifdef SIGHUP
        !           776:   if(SIG_ERR != old_sighup_handler)
        !           777:     (void)signal(SIGHUP, old_sighup_handler);
        !           778: #endif
        !           779: #ifdef SIGPIPE
        !           780:   if(SIG_ERR != old_sigpipe_handler)
        !           781:     (void)signal(SIGPIPE, old_sigpipe_handler);
        !           782: #endif
        !           783: #ifdef SIGALRM
        !           784:   if(!keep_sigalrm) {
        !           785:     if(SIG_ERR != old_sigalrm_handler)
        !           786:       (void)signal(SIGALRM, old_sigalrm_handler);
        !           787:   }
        !           788: #else
        !           789:   (void)keep_sigalrm;
        !           790: #endif
        !           791: #ifdef SIGINT
        !           792:   if(SIG_ERR != old_sigint_handler)
        !           793:     (void)signal(SIGINT, old_sigint_handler);
        !           794: #endif
        !           795: #ifdef SIGTERM
        !           796:   if(SIG_ERR != old_sigterm_handler)
        !           797:     (void)signal(SIGTERM, old_sigterm_handler);
        !           798: #endif
        !           799: #if defined(SIGBREAK) && defined(WIN32)
        !           800:   if(SIG_ERR != old_sigbreak_handler)
        !           801:     (void)signal(SIGBREAK, old_sigbreak_handler);
        !           802: #endif
        !           803: #ifdef WIN32
        !           804:   (void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE);
        !           805:   if(thread_main_window && thread_main_id) {
        !           806:     if(PostThreadMessage(thread_main_id, WM_APP, 0, 0))
        !           807:       (void)WaitForSingleObjectEx(thread_main_window, INFINITE, TRUE);
        !           808:   }
        !           809: #endif
        !           810: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>