浏览代码

Externalized logging function cry() as mg_cry()

Thomas Davis 12 年之前
父节点
当前提交
8085be6072
共有 3 个文件被更改,包括 49 次插入41 次删除
  1. 1 0
      RELEASE_NOTES.md
  2. 10 0
      include/civetweb.h
  3. 38 41
      src/civetweb.c

+ 1 - 0
RELEASE_NOTES.md

@@ -2,6 +2,7 @@ Release Notes v1.4 (UNDER DEVELOPMENT)
 ===
 ===
 ### Objectives: *???*
 ### Objectives: *???*
 
 
+- Externalized logging function cry() as mg_cry()
 - Added CivetServer::getCookie method (Hariprasad Kamath)
 - Added CivetServer::getCookie method (Hariprasad Kamath)
 - Added CivetServer::getHeader method (Hariprasad Kamath)
 - Added CivetServer::getHeader method (Hariprasad Kamath)
 
 

+ 10 - 0
include/civetweb.h

@@ -393,6 +393,16 @@ int mg_url_decode(const char *src, int src_len, char *dst,
 char *mg_md5(char buf[33], ...);
 char *mg_md5(char buf[33], ...);
 
 
 
 
+// Print error message to the opened error log stream.
+// This utilizes the provided logging configuration.
+//   conn: connection
+//   fmt: format string without the line return
+//   ...: variable argument list
+// Example:
+//   mg_cry(conn,"i like %s", "logging");
+void mg_cry(struct mg_connection *conn,
+                PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif // __cplusplus
 #endif // __cplusplus

+ 38 - 41
src/civetweb.c

@@ -618,11 +618,8 @@ static void sockaddr_to_string(char *buf, size_t len,
 #endif
 #endif
 }
 }
 
 
-static void cry(struct mg_connection *conn,
-                PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
-
 // Print error message to the opened error log stream.
 // Print error message to the opened error log stream.
-static void cry(struct mg_connection *conn, const char *fmt, ...) {
+void mg_cry(struct mg_connection *conn, const char *fmt, ...) {
   char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
   char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
   va_list ap;
   va_list ap;
   FILE *fp;
   FILE *fp;
@@ -749,10 +746,10 @@ static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
   n = vsnprintf(buf, buflen, fmt, ap);
   n = vsnprintf(buf, buflen, fmt, ap);
 
 
   if (n < 0) {
   if (n < 0) {
-    cry(conn, "vsnprintf error");
+    mg_cry(conn, "vsnprintf error");
     n = 0;
     n = 0;
   } else if (n >= (int) buflen) {
   } else if (n >= (int) buflen) {
-    cry(conn, "truncating vsnprintf buffer: [%.*s]",
+    mg_cry(conn, "truncating vsnprintf buffer: [%.*s]",
         n > 200 ? 200 : n, buf);
         n > 200 ? 200 : n, buf);
     n = (int) buflen - 1;
     n = (int) buflen - 1;
   }
   }
@@ -1360,7 +1357,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
   DEBUG_TRACE(("Running [%s]", cmdline));
   DEBUG_TRACE(("Running [%s]", cmdline));
   if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
   if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
         CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) {
         CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) {
-    cry(conn, "%s: CreateProcess(%s): %ld",
+    mg_cry(conn, "%s: CreateProcess(%s): %ld",
         __func__, cmdline, ERRNO);
         __func__, cmdline, ERRNO);
     pi.hProcess = (pid_t) -1;
     pi.hProcess = (pid_t) -1;
   }
   }
@@ -1432,11 +1429,11 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
   } else if (pid == 0) {
   } else if (pid == 0) {
     // Child
     // Child
     if (chdir(dir) != 0) {
     if (chdir(dir) != 0) {
-      cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
+      mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
     } else if (dup2(fdin, 0) == -1) {
     } else if (dup2(fdin, 0) == -1) {
-      cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
+      mg_cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
     } else if (dup2(fdout, 1) == -1) {
     } else if (dup2(fdout, 1) == -1) {
-      cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
+      mg_cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
     } else {
     } else {
       // Not redirecting stderr to stdout, to avoid output being littered
       // Not redirecting stderr to stdout, to avoid output being littered
       // with the error messages.
       // with the error messages.
@@ -1452,10 +1449,10 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
       interp = conn->ctx->config[CGI_INTERPRETER];
       interp = conn->ctx->config[CGI_INTERPRETER];
       if (interp == NULL) {
       if (interp == NULL) {
         (void) execle(prog, prog, NULL, envp);
         (void) execle(prog, prog, NULL, envp);
-        cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
+        mg_cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
       } else {
       } else {
         (void) execle(interp, interp, prog, NULL, envp);
         (void) execle(interp, interp, prog, NULL, envp);
-        cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
+        mg_cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
             strerror(ERRNO));
             strerror(ERRNO));
       }
       }
     }
     }
@@ -2138,7 +2135,7 @@ static void open_auth_file(struct mg_connection *conn, const char *path,
   if (gpass != NULL) {
   if (gpass != NULL) {
     // Use global passwords file
     // Use global passwords file
     if (!mg_fopen(conn, gpass, "r", filep)) {
     if (!mg_fopen(conn, gpass, "r", filep)) {
-      cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
+      mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
     }
     }
     // Important: using local struct file to test path for is_directory flag.
     // Important: using local struct file to test path for is_directory flag.
     // If filep is used, mg_stat() makes it appear as if auth file was opened.
     // If filep is used, mg_stat() makes it appear as if auth file was opened.
@@ -2290,7 +2287,7 @@ static int check_authorization(struct mg_connection *conn, const char *path) {
       mg_snprintf(conn, fname, sizeof(fname), "%.*s",
       mg_snprintf(conn, fname, sizeof(fname), "%.*s",
                   (int) filename_vec.len, filename_vec.ptr);
                   (int) filename_vec.len, filename_vec.ptr);
       if (!mg_fopen(conn, fname, "r", &file)) {
       if (!mg_fopen(conn, fname, "r", &file)) {
-        cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno));
+        mg_cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno));
       }
       }
       break;
       break;
     }
     }
@@ -3075,7 +3072,7 @@ static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
     // Bump up used length counter. Include \0 terminator
     // Bump up used length counter. Include \0 terminator
     block->len += n + 1;
     block->len += n + 1;
   } else {
   } else {
-    cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
+    mg_cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
   }
   }
 
 
   return added;
   return added;
@@ -3487,12 +3484,12 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi,
     (void) mg_snprintf(conn, path + strlen(path),
     (void) mg_snprintf(conn, path + strlen(path),
         sizeof(path) - strlen(path), "%s", file_name);
         sizeof(path) - strlen(path), "%s", file_name);
   } else {
   } else {
-    cry(conn, "Bad SSI #include: [%s]", tag);
+    mg_cry(conn, "Bad SSI #include: [%s]", tag);
     return;
     return;
   }
   }
 
 
   if (!mg_fopen(conn, path, "rb", &file)) {
   if (!mg_fopen(conn, path, "rb", &file)) {
-    cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
+    mg_cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
         tag, path, strerror(ERRNO));
         tag, path, strerror(ERRNO));
   } else {
   } else {
     fclose_on_exec(&file);
     fclose_on_exec(&file);
@@ -3512,9 +3509,9 @@ static void do_ssi_exec(struct mg_connection *conn, char *tag) {
   struct file file = STRUCT_FILE_INITIALIZER;
   struct file file = STRUCT_FILE_INITIALIZER;
 
 
   if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
   if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
-    cry(conn, "Bad SSI #exec: [%s]", tag);
+    mg_cry(conn, "Bad SSI #exec: [%s]", tag);
   } else if ((file.fp = popen(cmd, "r")) == NULL) {
   } else if ((file.fp = popen(cmd, "r")) == NULL) {
-    cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
+    mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
   } else {
   } else {
     send_file_data(conn, &file, 0, INT64_MAX);
     send_file_data(conn, &file, 0, INT64_MAX);
     pclose(file.fp);
     pclose(file.fp);
@@ -3538,7 +3535,7 @@ static void send_ssi_file(struct mg_connection *conn, const char *path,
   int ch, offset, len, in_ssi_tag;
   int ch, offset, len, in_ssi_tag;
 
 
   if (include_level > 10) {
   if (include_level > 10) {
-    cry(conn, "SSI #include level is too deep (%s)", path);
+    mg_cry(conn, "SSI #include level is too deep (%s)", path);
     return;
     return;
   }
   }
 
 
@@ -3560,7 +3557,7 @@ static void send_ssi_file(struct mg_connection *conn, const char *path,
           do_ssi_exec(conn, buf + 9);
           do_ssi_exec(conn, buf + 9);
 #endif // !NO_POPEN
 #endif // !NO_POPEN
         } else {
         } else {
-          cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
+          mg_cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
         }
         }
       }
       }
       len = 0;
       len = 0;
@@ -3569,7 +3566,7 @@ static void send_ssi_file(struct mg_connection *conn, const char *path,
         // Not an SSI tag
         // Not an SSI tag
         in_ssi_tag = 0;
         in_ssi_tag = 0;
       } else if (len == (int) sizeof(buf) - 2) {
       } else if (len == (int) sizeof(buf) - 2) {
-        cry(conn, "%s: SSI tag is too large", path);
+        mg_cry(conn, "%s: SSI tag is too large", path);
         len = 0;
         len = 0;
       }
       }
       buf[len++] = ch & 0xff;
       buf[len++] = ch & 0xff;
@@ -4428,11 +4425,11 @@ static int set_ports_option(struct mg_context *ctx) {
 
 
   while (success && (list = next_option(list, &vec, NULL)) != NULL) {
   while (success && (list = next_option(list, &vec, NULL)) != NULL) {
     if (!parse_port_string(&vec, &so)) {
     if (!parse_port_string(&vec, &so)) {
-      cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
+      mg_cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
           __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|r]");
           __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|r]");
       success = 0;
       success = 0;
     } else if (so.is_ssl && ctx->ssl_ctx == NULL) {
     } else if (so.is_ssl && ctx->ssl_ctx == NULL) {
-      cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
+      mg_cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
       success = 0;
       success = 0;
     } else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
     } else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
                INVALID_SOCKET ||
                INVALID_SOCKET ||
@@ -4448,7 +4445,7 @@ static int set_ports_option(struct mg_context *ctx) {
                bind(so.sock, &so.lsa.sa, so.lsa.sa.sa_family == AF_INET ?
                bind(so.sock, &so.lsa.sa, so.lsa.sa.sa_family == AF_INET ?
                     sizeof(so.lsa.sin) : sizeof(so.lsa)) != 0 ||
                     sizeof(so.lsa.sin) : sizeof(so.lsa)) != 0 ||
                listen(so.sock, SOMAXCONN) != 0) {
                listen(so.sock, SOMAXCONN) != 0) {
-      cry(fc(ctx), "%s: cannot bind to %.*s: %d (%s)", __func__,
+      mg_cry(fc(ctx), "%s: cannot bind to %.*s: %d (%s)", __func__,
           (int) vec.len, vec.ptr, ERRNO, strerror(errno));
           (int) vec.len, vec.ptr, ERRNO, strerror(errno));
       closesocket(so.sock);
       closesocket(so.sock);
       success = 0;
       success = 0;
@@ -4530,7 +4527,7 @@ static int check_acl(struct mg_context *ctx, uint32_t remote_ip) {
     flag = vec.ptr[0];
     flag = vec.ptr[0];
     if ((flag != '+' && flag != '-') ||
     if ((flag != '+' && flag != '-') ||
         parse_net(&vec.ptr[1], &net, &mask) == 0) {
         parse_net(&vec.ptr[1], &net, &mask) == 0) {
-      cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
+      mg_cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
       return -1;
       return -1;
     }
     }
 
 
@@ -4552,11 +4549,11 @@ static int set_uid_option(struct mg_context *ctx) {
     success = 1;
     success = 1;
   } else {
   } else {
     if ((pw = getpwnam(uid)) == NULL) {
     if ((pw = getpwnam(uid)) == NULL) {
-      cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
+      mg_cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
     } else if (setgid(pw->pw_gid) == -1) {
     } else if (setgid(pw->pw_gid) == -1) {
-      cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
+      mg_cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
     } else if (setuid(pw->pw_uid) == -1) {
     } else if (setuid(pw->pw_uid) == -1) {
-      cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
+      mg_cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
     } else {
     } else {
       success = 1;
       success = 1;
     }
     }
@@ -4606,7 +4603,7 @@ static int load_dll(struct mg_context *ctx, const char *dll_name,
   struct ssl_func *fp;
   struct ssl_func *fp;
 
 
   if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
   if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
-    cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
+    mg_cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
     return 0;
     return 0;
   }
   }
 
 
@@ -4620,7 +4617,7 @@ static int load_dll(struct mg_context *ctx, const char *dll_name,
     u.p = dlsym(dll_handle, fp->name);
     u.p = dlsym(dll_handle, fp->name);
 #endif // _WIN32
 #endif // _WIN32
     if (u.fp == NULL) {
     if (u.fp == NULL) {
-      cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
+      mg_cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
       return 0;
       return 0;
     } else {
     } else {
       fp->ptr = u.fp;
       fp->ptr = u.fp;
@@ -4655,7 +4652,7 @@ static int set_ssl_option(struct mg_context *ctx) {
   SSL_load_error_strings();
   SSL_load_error_strings();
 
 
   if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
   if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
-    cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
+    mg_cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
     return 0;
     return 0;
   }
   }
 
 
@@ -4665,7 +4662,7 @@ static int set_ssl_option(struct mg_context *ctx) {
        !ctx->callbacks.init_ssl(ctx->ssl_ctx, ctx->user_data)) &&
        !ctx->callbacks.init_ssl(ctx->ssl_ctx, ctx->user_data)) &&
       (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
       (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
        SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0)) {
        SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0)) {
-    cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
+    mg_cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
     return 0;
     return 0;
   }
   }
 
 
@@ -4677,7 +4674,7 @@ static int set_ssl_option(struct mg_context *ctx) {
   // http://www.openssl.org/support/faq.html#PROG1
   // http://www.openssl.org/support/faq.html#PROG1
   size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
   size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
   if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
   if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
-    cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
+    mg_cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
     return 0;
     return 0;
   }
   }
 
 
@@ -4708,7 +4705,7 @@ static int set_gpass_option(struct mg_context *ctx) {
   struct file file = STRUCT_FILE_INITIALIZER;
   struct file file = STRUCT_FILE_INITIALIZER;
   const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
   const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
   if (path != NULL && !mg_stat(fc(ctx), path, &file)) {
   if (path != NULL && !mg_stat(fc(ctx), path, &file)) {
-    cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO));
+    mg_cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO));
     return 0;
     return 0;
   }
   }
   return 1;
   return 1;
@@ -4982,7 +4979,7 @@ static void *worker_thread(void *thread_func_param) {
 
 
   conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
   conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
   if (conn == NULL) {
   if (conn == NULL) {
-    cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
+    mg_cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
   } else {
   } else {
     conn->buf_size = MAX_REQUEST_SIZE;
     conn->buf_size = MAX_REQUEST_SIZE;
     conn->buf = (char *) (conn + 1);
     conn->buf = (char *) (conn + 1);
@@ -5074,7 +5071,7 @@ static void accept_new_connection(const struct socket *listener,
   if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
   if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
   } else if (!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) {
   } else if (!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) {
     sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
     sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
-    cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
+    mg_cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
     closesocket(so.sock);
     closesocket(so.sock);
   } else {
   } else {
     // Put so socket structure into the queue
     // Put so socket structure into the queue
@@ -5225,16 +5222,16 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks,
 
 
   while (options && (name = *options++) != NULL) {
   while (options && (name = *options++) != NULL) {
     if ((i = get_option_index(name)) == -1) {
     if ((i = get_option_index(name)) == -1) {
-      cry(fc(ctx), "Invalid option: %s", name);
+      mg_cry(fc(ctx), "Invalid option: %s", name);
       free_context(ctx);
       free_context(ctx);
       return NULL;
       return NULL;
     } else if ((value = *options++) == NULL) {
     } else if ((value = *options++) == NULL) {
-      cry(fc(ctx), "%s: option value cannot be NULL", name);
+      mg_cry(fc(ctx), "%s: option value cannot be NULL", name);
       free_context(ctx);
       free_context(ctx);
       return NULL;
       return NULL;
     }
     }
     if (ctx->config[i] != NULL) {
     if (ctx->config[i] != NULL) {
-      cry(fc(ctx), "warning: %s: duplicate option", name);
+      mg_cry(fc(ctx), "warning: %s: duplicate option", name);
       free(ctx->config[i]);
       free(ctx->config[i]);
     }
     }
     ctx->config[i] = mg_strdup(value);
     ctx->config[i] = mg_strdup(value);
@@ -5281,7 +5278,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks,
   // Start worker threads
   // Start worker threads
   for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
   for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
     if (mg_start_thread(worker_thread, ctx) != 0) {
     if (mg_start_thread(worker_thread, ctx) != 0) {
-      cry(fc(ctx), "Cannot start worker thread: %ld", (long) ERRNO);
+      mg_cry(fc(ctx), "Cannot start worker thread: %ld", (long) ERRNO);
     } else {
     } else {
       ctx->num_threads++;
       ctx->num_threads++;
     }
     }