diff --git a/lix/libexec/kill-user.cc b/lix/libexec/kill-user.cc new file mode 100644 index 000000000..0fdc7e5c2 --- /dev/null +++ b/lix/libexec/kill-user.cc @@ -0,0 +1,60 @@ +#include "common.hh" +#include +#include +#include +#include +#include + +#if __APPLE__ +#include +#endif + +LIBEXEC_HELPER(1) + +int helperMain(const char * name, std::span args) noexcept +{ + std::string_view uidArg = args[0]; + uid_t uid; + + if (auto res = std::from_chars(uidArg.begin(), uidArg.end(), uid); + res.ptr != uidArg.end() || res.ec != std::errc()) + { + die("invalid uid argument"); + } + + /* The system call kill(-1, sig) sends the signal `sig' to all + users to which the current process can send signals. So we + switch to that uid and send a mass kill once we've done so. */ + + if (setuid(uid) == -1) { + die(std::format("setuid(): {}", strerror(errno))); + } + + while (true) { +#ifdef __APPLE__ + /* OSX's kill syscall takes a third parameter that, among + other things, determines if kill(-1, signo) affects the + calling process. In the OSX libc, it's set to true, + which means "follow POSIX", which we don't want here */ + if (syscall(SYS_kill, -1, SIGKILL, false) == 0) { + break; + } +#else + if (kill(-1, SIGKILL) == 0) { + break; + } +#endif + if (errno == ESRCH || errno == EPERM) { + break; /* no more processes */ + } + if (errno != EINTR) { + die(std::format("cannot kill processes for uid {}: {}", uid, strerror(errno))); + } + } + + /* !!! We should really do some check to make sure that there are + no processes left running under `uid', but there is no portable + way to do so (I think). The most reliable way may be `ps -eo + uid | grep -q $uid'. */ + return 0; +} diff --git a/lix/libexec/meson.build b/lix/libexec/meson.build index d61b44985..bc9651f9c 100644 --- a/lix/libexec/meson.build +++ b/lix/libexec/meson.build @@ -1,3 +1,10 @@ +kill_user = executable( + 'kill-user', + files('kill-user.cc'), + install : true, + install_dir : libexecdir / 'lix', +) + run_pager = executable( 'run-pager', files('run-pager.cc'), diff --git a/lix/libutil/processes.cc b/lix/libutil/processes.cc index 9b8a77e4f..b35ebfa10 100644 --- a/lix/libutil/processes.cc +++ b/lix/libutil/processes.cc @@ -130,42 +130,7 @@ void killUser(uid_t uid) assert(uid != 0); /* just to be safe... */ - /* The system call kill(-1, sig) sends the signal `sig' to all - users to which the current process can send signals. So we - fork a process, switch to uid, and send a mass kill. */ - - Pid pid{startProcess([&]() { - - if (setuid(uid) == -1) - throw SysError("setting uid"); - - while (true) { -#ifdef __APPLE__ - /* OSX's kill syscall takes a third parameter that, among - other things, determines if kill(-1, signo) affects the - calling process. In the OSX libc, it's set to true, - which means "follow POSIX", which we don't want here - */ - if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break; -#else - if (kill(-1, SIGKILL) == 0) break; -#endif - if (errno == ESRCH || errno == EPERM) break; /* no more processes */ - if (errno != EINTR) - throw SysError("cannot kill processes for uid '%1%'", uid); - } - - _exit(0); - })}; - - int status = pid.wait(); - if (status != 0) - throw Error("cannot kill processes for uid '%1%': %2%", uid, statusToString(status)); - - /* !!! We should really do some check to make sure that there are - no processes left running under `uid', but there is no portable - way to do so (I think). The most reliable way may be `ps -eo - uid | grep -q $uid'. */ + runHelper("kill-user", {.args = {std::to_string(uid)}}).waitAndCheck(); }