int cb_cbref;
gboolean replied;
gboolean is_error;
+ gboolean dead; /* Set by SIGCHLD handler when child exits */
pid_t cpid;
lua_State *L;
uint64_t sz;
lua_settop(L, err_idx - 1);
}
+/* Helper to free cbdata resources */
+static void
+rspamd_lua_cbdata_free(struct rspamd_lua_process_cbdata *cbdata)
+{
+ struct rspamd_srv_command srv_cmd;
+ lua_State *L = cbdata->L;
+
+ close(cbdata->sp[0]);
+ luaL_unref(L, LUA_REGISTRYINDEX, cbdata->func_cbref);
+ luaL_unref(L, LUA_REGISTRYINDEX, cbdata->cb_cbref);
+ g_string_free(cbdata->io_buf, TRUE);
+
+ if (cbdata->out_buf) {
+ g_string_free(cbdata->out_buf, TRUE);
+ }
+
+ /* Notify main */
+ memset(&srv_cmd, 0, sizeof(srv_cmd));
+ srv_cmd.type = RSPAMD_SRV_ON_FORK;
+ srv_cmd.cmd.on_fork.state = child_dead;
+ srv_cmd.cmd.on_fork.cpid = cbdata->cpid;
+ srv_cmd.cmd.on_fork.ppid = getpid();
+ rspamd_srv_send_command(cbdata->wrk, cbdata->event_loop, &srv_cmd, -1,
+ NULL, NULL);
+ g_free(cbdata);
+}
+
static gboolean
rspamd_lua_cld_handler(struct rspamd_worker_signal_handler *sigh, void *ud)
{
struct rspamd_lua_process_cbdata *cbdata = ud;
- struct rspamd_srv_command srv_cmd;
- lua_State *L;
pid_t died;
int res = 0;
return TRUE;
}
- L = cbdata->L;
msg_info("handled SIGCHLD from %P", cbdata->cpid);
+ cbdata->dead = TRUE;
if (!cbdata->replied) {
- /* We still need to call on_complete callback */
+ /* Child died before sending reply - call callback with error and cleanup */
ev_io_stop(cbdata->event_loop, &cbdata->ev);
rspamd_lua_call_on_complete(cbdata->L, cbdata,
"Worker has died without reply", NULL, 0);
+ rspamd_lua_cbdata_free(cbdata);
}
-
- /* Free structures */
- close(cbdata->sp[0]);
- luaL_unref(L, LUA_REGISTRYINDEX, cbdata->func_cbref);
- luaL_unref(L, LUA_REGISTRYINDEX, cbdata->cb_cbref);
- g_string_free(cbdata->io_buf, TRUE);
-
- if (cbdata->out_buf) {
- g_string_free(cbdata->out_buf, TRUE);
- }
-
- /* Notify main */
- memset(&srv_cmd, 0, sizeof(srv_cmd));
- srv_cmd.type = RSPAMD_SRV_ON_FORK;
- srv_cmd.cmd.on_fork.state = child_dead;
- srv_cmd.cmd.on_fork.cpid = cbdata->cpid;
- srv_cmd.cmd.on_fork.ppid = getpid();
- rspamd_srv_send_command(cbdata->wrk, cbdata->event_loop, &srv_cmd, -1,
- NULL, NULL);
- g_free(cbdata);
+ /* If replied is TRUE, the I/O handler is processing the callback.
+ * It will call rspamd_lua_cbdata_free() when done. */
/* We are done with this SIGCHLD */
return FALSE;
if (r == 0) {
ev_io_stop(cbdata->event_loop, &cbdata->ev);
+ cbdata->replied = TRUE;
rspamd_lua_call_on_complete(cbdata->L, cbdata,
"Unexpected EOF", NULL, 0);
- cbdata->replied = TRUE;
kill(cbdata->cpid, SIGTERM);
+ if (cbdata->dead) {
+ rspamd_lua_cbdata_free(cbdata);
+ }
return;
}
else if (r == -1) {
}
else {
ev_io_stop(cbdata->event_loop, &cbdata->ev);
+ cbdata->replied = TRUE;
rspamd_lua_call_on_complete(cbdata->L, cbdata,
strerror(errno), NULL, 0);
- cbdata->replied = TRUE;
kill(cbdata->cpid, SIGTERM);
+ if (cbdata->dead) {
+ rspamd_lua_cbdata_free(cbdata);
+ }
return;
}
}
if (r == 0) {
ev_io_stop(cbdata->event_loop, &cbdata->ev);
+ cbdata->replied = TRUE;
rspamd_lua_call_on_complete(cbdata->L, cbdata,
"Unexpected EOF", NULL, 0);
- cbdata->replied = TRUE;
kill(cbdata->cpid, SIGTERM);
+ if (cbdata->dead) {
+ rspamd_lua_cbdata_free(cbdata);
+ }
return;
}
else if (r == -1) {
}
else {
ev_io_stop(cbdata->event_loop, &cbdata->ev);
+ cbdata->replied = TRUE;
rspamd_lua_call_on_complete(cbdata->L, cbdata,
strerror(errno), NULL, 0);
- cbdata->replied = TRUE;
kill(cbdata->cpid, SIGTERM);
+ if (cbdata->dead) {
+ rspamd_lua_cbdata_free(cbdata);
+ }
return;
}
}
char rep[4];
ev_io_stop(cbdata->event_loop, &cbdata->ev);
+ /* Mark as replied BEFORE calling callback to prevent SIGCHLD handler
+ * from calling the callback again. The SIGCHLD handler will see
+ * replied=TRUE and skip cleanup, leaving it for us to do. */
+ cbdata->replied = TRUE;
/* Finished reading data */
if (cbdata->is_error) {
cbdata->io_buf->str[cbdata->io_buf->len] = '\0';
NULL, cbdata->io_buf->str, cbdata->io_buf->len);
}
- cbdata->replied = TRUE;
-
/* Write reply to the child */
rspamd_socket_blocking(cbdata->sp[0]);
memset(rep, 0, sizeof(rep));
(void) !write(cbdata->sp[0], rep, sizeof(rep));
+
+ /* If SIGCHLD already ran while we were in the callback,
+ * the child is dead and we need to cleanup now */
+ if (cbdata->dead) {
+ rspamd_lua_cbdata_free(cbdata);
+ }
}
}
}