|
@@ -10805,27 +10805,42 @@ prepare_cgi_environment(struct mg_connection *conn,
|
|
|
}
|
|
|
|
|
|
|
|
|
+/* Data for CGI process control: PID and number of references */
|
|
|
+struct process_control_data {
|
|
|
+ pid_t pid;
|
|
|
+ int references;
|
|
|
+};
|
|
|
+
|
|
|
static int
|
|
|
abort_process(void *data)
|
|
|
{
|
|
|
/* Waitpid checks for child status and won't work for a pid that does not
|
|
|
* identify a child of the current process. Thus, if the pid is reused,
|
|
|
* we will not affect a different process. */
|
|
|
- pid_t pid = (pid_t)data;
|
|
|
+ struct process_control_data *proc = (struct process_control_data *)data;
|
|
|
int status = 0;
|
|
|
- pid_t rpid = waitpid(pid, &status, WNOHANG);
|
|
|
- if ((rpid != (pid_t)-1) && (status == 0)) {
|
|
|
+ int refs;
|
|
|
+ pid_t ret_pid;
|
|
|
+
|
|
|
+ ret_pid = waitpid(proc->pid, &status, WNOHANG);
|
|
|
+ if ((ret_pid != (pid_t)-1) && (status == 0)) {
|
|
|
/* Stop child process */
|
|
|
- DEBUG_TRACE("CGI timer: Stop child process %p\n", pid);
|
|
|
- kill(pid, SIGABRT);
|
|
|
+ DEBUG_TRACE("CGI timer: Stop child process %p\n", proc->pid);
|
|
|
+ kill(proc->pid, SIGABRT);
|
|
|
|
|
|
/* Wait until process is terminated (don't leave zombies) */
|
|
|
- while (waitpid(pid, &status, 0) != (pid_t)-1) /* nop */
|
|
|
+ while (waitpid(proc->pid, &status, 0) != (pid_t)-1) /* nop */
|
|
|
;
|
|
|
} else {
|
|
|
- DEBUG_TRACE("CGI timer: Child process %p already stopped in time\n",
|
|
|
- pid);
|
|
|
+ DEBUG_TRACE("CGI timer: Child process %p already stopped\n", proc->pid);
|
|
|
+ }
|
|
|
+ /* Dec reference counter */
|
|
|
+ refs = mg_atomic_dec(&proc->references);
|
|
|
+ if (refs == 0) {
|
|
|
+ /* no more references - free data */
|
|
|
+ mg_free(data);
|
|
|
}
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -10844,6 +10859,7 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
|
|
|
FILE *in = NULL, *out = NULL, *err = NULL;
|
|
|
struct mg_file fout = STRUCT_FILE_INITIALIZER;
|
|
|
pid_t pid = (pid_t)-1;
|
|
|
+ struct process_control_data *proc = NULL;
|
|
|
|
|
|
if (conn == NULL) {
|
|
|
return;
|
|
@@ -10891,6 +10907,14 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
+ proc = (struct process_control_data *)
|
|
|
+ mg_malloc_ctx(sizeof(struct process_control_data), conn->phys_ctx);
|
|
|
+ if (proc == NULL) {
|
|
|
+ mg_cry_internal(conn, "Error: CGI program \"%s\": Out or memory", prog);
|
|
|
+ mg_send_http_error(conn, 500, "Error: Out of memory [%s]", prog);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
DEBUG_TRACE("CGI: spawn %s %s\n", dir, p);
|
|
|
pid = spawn_process(conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir);
|
|
|
|
|
@@ -10906,17 +10930,26 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
|
|
|
"Error: Cannot spawn CGI process [%s]: %s",
|
|
|
prog,
|
|
|
status);
|
|
|
+ mg_free(proc);
|
|
|
+ proc = NULL;
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
+ /* Store data in shared process_control_data */
|
|
|
+ proc->pid = pid;
|
|
|
+
|
|
|
#if defined(USE_TIMERS)
|
|
|
- // TODO (#618): set a timeout
|
|
|
+ proc->references = 2;
|
|
|
+
|
|
|
+ // Start a timer for CGI
|
|
|
timer_add(conn->phys_ctx,
|
|
|
- /* one minute */ 60.0,
|
|
|
+ /* one minute. TODO: use config or define */ 60.0,
|
|
|
0.0,
|
|
|
1,
|
|
|
abort_process,
|
|
|
- (void *)pid);
|
|
|
+ (void *)proc);
|
|
|
+#else
|
|
|
+ proc->references = 1;
|
|
|
#endif
|
|
|
|
|
|
/* Make sure child closes all pipe descriptors. It must dup them to 0,1
|
|
@@ -11119,7 +11152,7 @@ done:
|
|
|
mg_free(blk.buf);
|
|
|
|
|
|
if (pid != (pid_t)-1) {
|
|
|
- abort_process((void *)pid);
|
|
|
+ abort_process((void *)proc);
|
|
|
}
|
|
|
|
|
|
if (fdin[0] != -1) {
|