1#!/bin/sh 2 3# 4# SPDX-License-Identifier: BSD-2-Clause 5# 6# Copyright (c) 2023 Peter Holm <pho@FreeBSD.org> 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions 10# are met: 11# 1. Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27# SUCH DAMAGE. 28# 29 30# killpg(2) version of reaper.sh. No problems seen. 31 32. ../default.cfg 33 34prog=$(basename "$0" .sh) 35cat > /tmp/$prog.c <<EOF 36#include <sys/param.h> 37#include <sys/mman.h> 38#include <sys/wait.h> 39 40#include <err.h> 41#include <errno.h> 42#include <pwd.h> 43#include <signal.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <time.h> 47#include <unistd.h> 48 49static volatile u_int *share; 50 51#define CONT 0 52#define GID 1 53#define SYNC 2 54#define MAXP 10000 55 56static void 57hand(int i __unused) { 58 _exit(0); 59} 60 61static void 62looper(void) 63{ 64 struct sigaction sa; 65 time_t start; 66 struct passwd *pw; 67 pid_t pids[MAXP]; 68 int i, parallel; 69 70 setproctitle("looper"); 71 sa.sa_handler = SIG_IGN; 72 sigemptyset(&sa.sa_mask); 73 sa.sa_flags = 0; 74 if (sigaction(SIGUSR1, &sa, NULL) == -1) 75 err(1, "sigaction"); 76 77 if ((pw = getpwnam("TUSER")) == NULL) 78 err(1, "no such user: TUSER"); 79 80 if (setgroups(1, &pw->pw_gid) || 81 setegid(pw->pw_gid) || setgid(pw->pw_gid) || 82 seteuid(pw->pw_uid) || setuid(pw->pw_uid)) 83 err(1, "Can't drop privileges to \"TUSER\""); 84 endpwent(); 85 setpgrp(0, 0); 86 share[GID] = getpgrp(); 87 share[SYNC] = 1; 88 start = time(NULL); 89 while (time(NULL) - start < 120) { 90 parallel = arc4random() % MAXP + 1; 91 for (i = 0; i < parallel; i++) { 92 if ((pids[i] = fork()) == 0) { 93 sa.sa_handler = hand; 94 sigemptyset(&sa.sa_mask); 95 sa.sa_flags = 0; 96 if (sigaction(SIGUSR1, &sa, NULL) == -1) 97 err(1, "sigaction"); 98 setproctitle("child"); 99 for (;;) 100 pause(); 101 _exit(0); /* never reached */ 102 } 103 if (pids[i] == -1) 104 err(1, "fork()"); 105 } 106 for (i = 0; i < parallel; i++) { 107 if (waitpid(pids[i], NULL, 0) != pids[i]) 108 err(1, "waitpid(%d) in looper", pids[i]); 109 } 110 } 111 _exit(0); 112} 113 114static void 115killer(void) 116{ 117 int e, gid; 118 119 while (share[SYNC] == 0) 120 ; 121 gid = share[GID]; 122 while (share[CONT] == 1) { 123 usleep(arc4random() % 50000); 124 gid = share[GID]; 125 if (gid != 0) { 126 e = 0; 127 while (e == 0) { 128 if (killpg(gid, SIGUSR1) == -1) { 129 e = 1; 130 if (errno != ESRCH) 131 err(1, "pgkill(%d)", gid); 132 } 133 usleep(5000 + arc4random() % 5000); 134 } 135 } 136 } 137 _exit(0); 138} 139 140int 141main(void) 142{ 143 size_t len; 144 time_t start; 145 int lpid, kpid, s1, s2; 146 147 len = PAGE_SIZE; 148 if ((share = mmap(NULL, len, PROT_READ | PROT_WRITE, 149 MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED) 150 err(1, "mmap"); 151 152 start = time(NULL); 153 while (time(NULL) - start < 120) { 154 share[CONT] = 1; 155 share[GID] = 0; 156 share[SYNC] = 0; 157 if ((lpid = fork()) == 0) 158 looper(); 159 usleep(arc4random() % 100000); 160 if ((kpid = fork()) == 0) 161 killer(); 162 163 if (waitpid(lpid, &s1, 0) != lpid) 164 err(1, "waitpid looper"); 165 usleep(arc4random() % 1000); 166 share[CONT] = 0; 167 waitpid(kpid, &s2, 0); 168 } 169 170 return (0); 171} 172EOF 173sed -i '' "s#TUSER#$testuser#" /tmp/$prog.c 174cc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c || exit 1 175rm /tmp/$prog.c 176 177n=1 178start=`date +%s` 179while true; do 180 /tmp/$prog 181 for i in `jot 50`; do 182 pgrep -q $prog || break 183 sleep .5 184 done 185 if pgrep -q $prog; then 186 e=$((`date +%s` - start)) 187 echo "Failed in loop #$n after $e seconds." 188 pgrep "$prog|timeout" | xargs ps -jp 189 pkill $prog 190 rm -f /tmp/$prog 191 exit 1 192 fi 193 [ $((`date +%s` - start)) -ge 600 ] && break 194 n=$((n + 1)) 195done 196rm /tmp/$prog 197exit 0 198