opt: spawn use double fork

This commit is contained in:
DreamMaoMao 2026-02-20 18:56:13 +08:00
parent 722f9f6876
commit 7962dc04f2
4 changed files with 100 additions and 52 deletions

View file

@ -5,7 +5,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <signal.h>
#include <sys/resource.h>
#include <wlr/util/log.h>
#include "util.h" #include "util.h"
#define PCRE2_CODE_UNIT_WIDTH 8 #define PCRE2_CODE_UNIT_WIDTH 8
@ -92,3 +94,29 @@ uint32_t get_now_in_ms(void) {
uint32_t timespec_to_ms(struct timespec *ts) { uint32_t timespec_to_ms(struct timespec *ts) {
return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000; return (uint32_t)ts->tv_sec * 1000 + (uint32_t)ts->tv_nsec / 1000000;
} }
void restore_nofile_limit(void) {
struct rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
// Restore the soft limit to the hard limit
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &rl) != 0) {
wlr_log(WLR_DEBUG, "restore_nofile_limit: setrlimit failed");
}
} else {
wlr_log(WLR_DEBUG, "restore_nofile_limit: getrlimit failed");
}
}
void reset_child_environment(void) {
restore_nofile_limit();
// Reset signal handlers
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
// Reset signal handlers to default
signal(SIGPIPE, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
}

View file

@ -7,4 +7,5 @@ int32_t fd_set_nonblock(int32_t fd);
int32_t regex_match(const char *pattern_mb, const char *str_mb); int32_t regex_match(const char *pattern_mb, const char *str_mb);
void wl_list_append(struct wl_list *list, struct wl_list *object); void wl_list_append(struct wl_list *list, struct wl_list *object);
uint32_t get_now_in_ms(void); uint32_t get_now_in_ms(void);
uint32_t timespec_to_ms(struct timespec *ts); uint32_t timespec_to_ms(struct timespec *ts);
void reset_child_environment(void);

View file

@ -794,70 +794,88 @@ int32_t centerwin(const Arg *arg) {
} }
int32_t spawn_shell(const Arg *arg) { int32_t spawn_shell(const Arg *arg) {
if (!arg->v) if (!arg->v)
return 0; return 0;
if (fork() == 0) { pid_t child = fork();
// 1. 忽略可能导致 coredump 的信号 if (child == -1) {
signal(SIGSEGV, SIG_IGN); wlr_log_errno(WLR_ERROR, "spawn_shell: first fork failed");
signal(SIGABRT, SIG_IGN); return 0;
signal(SIGILL, SIG_IGN); }
dup2(STDERR_FILENO, STDOUT_FILENO); if (child == 0) {
setsid(); reset_child_environment();
execlp("sh", "sh", "-c", arg->v, (char *)NULL); pid_t grandchild = fork();
if (grandchild == -1) {
wlr_log_errno(WLR_ERROR, "spawn_shell: second fork failed");
_exit(EXIT_FAILURE);
}
// fallback to bash if (grandchild == 0) {
execlp("bash", "bash", "-c", arg->v, (char *)NULL);
execlp("sh", "sh", "-c", arg->v, (char *)NULL);
// fallback to bash
execlp("bash", "bash", "-c", arg->v, (char *)NULL);
wlr_log_errno(WLR_ERROR, "spawn_shell: execlp failed for '%s'", arg->v);
_exit(EXIT_FAILURE);
}
// if execlp fails, we should not reach here _exit(EXIT_SUCCESS);
wlr_log(WLR_ERROR, }
"mango: failed to execute command '%s' with shell: %s\n",
arg->v, strerror(errno)); waitpid(child, NULL, 0);
_exit(EXIT_FAILURE); return 0;
}
return 0;
} }
int32_t spawn(const Arg *arg) { int32_t spawn(const Arg *arg) {
if (!arg->v)
return 0;
if (!arg->v) pid_t child = fork();
return 0; if (child == -1) {
wlr_log_errno(WLR_ERROR, "spawn: first fork failed");
return 0;
}
if (fork() == 0) { if (child == 0) {
// 1. 忽略可能导致 coredump 的信号 // reset environment
signal(SIGSEGV, SIG_IGN); reset_child_environment();
signal(SIGABRT, SIG_IGN);
signal(SIGILL, SIG_IGN);
dup2(STDERR_FILENO, STDOUT_FILENO); // create second child
setsid(); pid_t grandchild = fork();
if (grandchild == -1) {
wlr_log_errno(WLR_ERROR, "spawn: second fork failed");
_exit(EXIT_FAILURE);
}
// 2. 解析参数 if (grandchild == 0) {
char *argv[64]; // execute actual command
int32_t argc = 0; char *argv[64];
char *token = strtok((char *)arg->v, " "); int32_t argc = 0;
while (token != NULL && argc < 63) { char *token = strtok((char *)arg->v, " ");
wordexp_t p; while (token != NULL && argc < 63) {
if (wordexp(token, &p, 0) == 0) { wordexp_t p;
argv[argc++] = p.we_wordv[0]; if (wordexp(token, &p, 0) == 0) {
} else { argv[argc++] = p.we_wordv[0];
argv[argc++] = token; } else {
} argv[argc++] = token;
token = strtok(NULL, " "); }
} token = strtok(NULL, " ");
argv[argc] = NULL; }
argv[argc] = NULL;
// 3. 执行命令 execvp(argv[0], argv);
execvp(argv[0], argv); wlr_log_errno(WLR_ERROR, "spawn: execvp '%s' failed", argv[0]);
_exit(EXIT_FAILURE);
}
// 4. execvp 失败时:打印错误并直接退出(避免 coredump
wlr_log(WLR_ERROR, "mango: execvp '%s' failed: %s\n", argv[0], _exit(EXIT_SUCCESS);
strerror(errno)); }
_exit(EXIT_FAILURE); // 使用 _exit 避免缓冲区刷新等操作
} waitpid(child, NULL, 0);
return 0; return 0;
} }
int32_t spawn_on_empty(const Arg *arg) { int32_t spawn_on_empty(const Arg *arg) {

View file

@ -86,6 +86,7 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <wlr/util/region.h> #include <wlr/util/region.h>
#include <wordexp.h> #include <wordexp.h>
#include <sys/resource.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#ifdef XWAYLAND #ifdef XWAYLAND
#include <X11/Xlib.h> #include <X11/Xlib.h>