forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
3.2 KiB
161 lines
3.2 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Author: Alexey Gladkov <[email protected]> |
|
*/ |
|
#define _GNU_SOURCE |
|
#include <sys/types.h> |
|
#include <sys/wait.h> |
|
#include <sys/time.h> |
|
#include <sys/resource.h> |
|
#include <sys/prctl.h> |
|
#include <sys/stat.h> |
|
|
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <sched.h> |
|
#include <signal.h> |
|
#include <limits.h> |
|
#include <fcntl.h> |
|
#include <errno.h> |
|
#include <err.h> |
|
|
|
#define NR_CHILDS 2 |
|
|
|
static char *service_prog; |
|
static uid_t user = 60000; |
|
static uid_t group = 60000; |
|
|
|
static void setrlimit_nproc(rlim_t n) |
|
{ |
|
pid_t pid = getpid(); |
|
struct rlimit limit = { |
|
.rlim_cur = n, |
|
.rlim_max = n |
|
}; |
|
|
|
warnx("(pid=%d): Setting RLIMIT_NPROC=%ld", pid, n); |
|
|
|
if (setrlimit(RLIMIT_NPROC, &limit) < 0) |
|
err(EXIT_FAILURE, "(pid=%d): setrlimit(RLIMIT_NPROC)", pid); |
|
} |
|
|
|
static pid_t fork_child(void) |
|
{ |
|
pid_t pid = fork(); |
|
|
|
if (pid < 0) |
|
err(EXIT_FAILURE, "fork"); |
|
|
|
if (pid > 0) |
|
return pid; |
|
|
|
pid = getpid(); |
|
|
|
warnx("(pid=%d): New process starting ...", pid); |
|
|
|
if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) |
|
err(EXIT_FAILURE, "(pid=%d): prctl(PR_SET_PDEATHSIG)", pid); |
|
|
|
signal(SIGUSR1, SIG_DFL); |
|
|
|
warnx("(pid=%d): Changing to uid=%d, gid=%d", pid, user, group); |
|
|
|
if (setgid(group) < 0) |
|
err(EXIT_FAILURE, "(pid=%d): setgid(%d)", pid, group); |
|
if (setuid(user) < 0) |
|
err(EXIT_FAILURE, "(pid=%d): setuid(%d)", pid, user); |
|
|
|
warnx("(pid=%d): Service running ...", pid); |
|
|
|
warnx("(pid=%d): Unshare user namespace", pid); |
|
if (unshare(CLONE_NEWUSER) < 0) |
|
err(EXIT_FAILURE, "unshare(CLONE_NEWUSER)"); |
|
|
|
char *const argv[] = { "service", NULL }; |
|
char *const envp[] = { "I_AM_SERVICE=1", NULL }; |
|
|
|
warnx("(pid=%d): Executing real service ...", pid); |
|
|
|
execve(service_prog, argv, envp); |
|
err(EXIT_FAILURE, "(pid=%d): execve", pid); |
|
} |
|
|
|
int main(int argc, char **argv) |
|
{ |
|
size_t i; |
|
pid_t child[NR_CHILDS]; |
|
int wstatus[NR_CHILDS]; |
|
int childs = NR_CHILDS; |
|
pid_t pid; |
|
|
|
if (getenv("I_AM_SERVICE")) { |
|
pause(); |
|
exit(EXIT_SUCCESS); |
|
} |
|
|
|
service_prog = argv[0]; |
|
pid = getpid(); |
|
|
|
warnx("(pid=%d) Starting testcase", pid); |
|
|
|
/* |
|
* This rlimit is not a problem for root because it can be exceeded. |
|
*/ |
|
setrlimit_nproc(1); |
|
|
|
for (i = 0; i < NR_CHILDS; i++) { |
|
child[i] = fork_child(); |
|
wstatus[i] = 0; |
|
usleep(250000); |
|
} |
|
|
|
while (1) { |
|
for (i = 0; i < NR_CHILDS; i++) { |
|
if (child[i] <= 0) |
|
continue; |
|
|
|
errno = 0; |
|
pid_t ret = waitpid(child[i], &wstatus[i], WNOHANG); |
|
|
|
if (!ret || (!WIFEXITED(wstatus[i]) && !WIFSIGNALED(wstatus[i]))) |
|
continue; |
|
|
|
if (ret < 0 && errno != ECHILD) |
|
warn("(pid=%d): waitpid(%d)", pid, child[i]); |
|
|
|
child[i] *= -1; |
|
childs -= 1; |
|
} |
|
|
|
if (!childs) |
|
break; |
|
|
|
usleep(250000); |
|
|
|
for (i = 0; i < NR_CHILDS; i++) { |
|
if (child[i] <= 0) |
|
continue; |
|
kill(child[i], SIGUSR1); |
|
} |
|
} |
|
|
|
for (i = 0; i < NR_CHILDS; i++) { |
|
if (WIFEXITED(wstatus[i])) |
|
warnx("(pid=%d): pid %d exited, status=%d", |
|
pid, -child[i], WEXITSTATUS(wstatus[i])); |
|
else if (WIFSIGNALED(wstatus[i])) |
|
warnx("(pid=%d): pid %d killed by signal %d", |
|
pid, -child[i], WTERMSIG(wstatus[i])); |
|
|
|
if (WIFSIGNALED(wstatus[i]) && WTERMSIG(wstatus[i]) == SIGUSR1) |
|
continue; |
|
|
|
warnx("(pid=%d): Test failed", pid); |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
warnx("(pid=%d): Test passed", pid); |
|
exit(EXIT_SUCCESS); |
|
}
|
|
|