1242137Sobrien/*- 2242137Sobrien * Copyright (c) 2012 David O'Brien 3242137Sobrien * All rights reserved. 4242137Sobrien * 5242137Sobrien * Redistribution and use in source and binary forms, with or without 6242137Sobrien * modification, are permitted provided that the following conditions 7242137Sobrien * are met: 8242137Sobrien * 1. Redistributions of source code must retain the above copyright 9242137Sobrien * notice, this list of conditions and the following disclaimer. 10242137Sobrien * 2. Redistributions in binary form must reproduce the above copyright 11242137Sobrien * notice, this list of conditions and the following disclaimer in the 12242137Sobrien * documentation and/or other materials provided with the distribution. 13242137Sobrien * 14242137Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15242137Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16242137Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17242137Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18242137Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19242137Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20242137Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21242137Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22242137Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23242137Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24242137Sobrien * SUCH DAMAGE. 25242137Sobrien * 26242137Sobrien * $FreeBSD$ 27242137Sobrien */ 28242137Sobrien 29242137Sobrien#include <sys/types.h> 30242137Sobrien#include <sys/resource.h> 31242137Sobrien#include <sys/sysctl.h> 32242137Sobrien#include <sys/wait.h> 33242137Sobrien 34242137Sobrien#include <unistd.h> 35242137Sobrien#include <stdlib.h> 36242137Sobrien#include <signal.h> 37242137Sobrien#include <errno.h> 38242137Sobrien#include <stdio.h> 39242137Sobrien#include <string.h> 40242137Sobrien#include <time.h> 41242137Sobrien#include <err.h> 42242137Sobrien#include <assert.h> 43242137Sobrien 44242137Sobrien#ifndef SLEEP 45242137Sobrien#define SLEEP 20 /* seconds */ 46242137Sobrien#endif 47242137Sobrien 48251174Sobrienstatic int verbose; 49242137Sobrien 50242137Sobrienstatic void 51242137Sobrienusage(void) 52242137Sobrien{ 53242137Sobrien fprintf(stderr, "usage: %s\n", getprogname()); 54242137Sobrien fprintf(stderr, "\t\t-n : length of fork(2) chain\n"); 55242137Sobrien fprintf(stderr, "\t\t-t : limit run-time seconds\n"); 56242137Sobrien exit(1); 57242137Sobrien /* NOTREACHED */ 58242137Sobrien} 59242137Sobrien 60242137Sobrienvoid term(int); 61242137Sobrienvoid 62242137Sobrienterm(int signum) 63242137Sobrien{ 64242137Sobrien 65242137Sobrien if (getpid() == getpgrp() || verbose) { 66242137Sobrien fprintf(stderr, 67242137Sobrien "pid %d pgroup %d (ppid %d): Received SIGTERM(%d), exiting...\n", 68242137Sobrien getpid(), getpgrp(), getppid(), signum); 69242137Sobrien } 70242137Sobrien exit(1); 71242137Sobrien} 72242137Sobrien 73242137Sobrienvoid angel_of_mercy(int); 74242137Sobrienvoid 75242137Sobrienangel_of_mercy(int sig __unused) 76242137Sobrien{ 77242137Sobrien 78242137Sobrien signal(SIGALRM, SIG_IGN); /* ignore this signal */ 79242137Sobrien printf("Master process: alarmed waking up\n"); 80242137Sobrien killpg(0, SIGTERM); 81242137Sobrien return; 82242137Sobrien} 83242137Sobrien 84242137Sobrienint bombing_run(unsigned, int); 85242137Sobrienint 86242137Sobrienbombing_run(unsigned chainlen, int stime) 87242137Sobrien{ 88242137Sobrien struct rusage ru; 89242137Sobrien pid_t pid, cpid; 90242137Sobrien int status; 91242137Sobrien 92242137Sobrien if (chainlen) { 93242137Sobrien switch (pid = fork()) { 94242137Sobrien case -1: 95242137Sobrien errx(1, "%s: can't fork", __func__); 96242137Sobrien 97242137Sobrien case 0: 98242137Sobrien /* This is the code the child runs. */ 99242137Sobrien bombing_run(--chainlen, stime); 100242137Sobrien break; 101242137Sobrien 102242137Sobrien default: 103242137Sobrien /* This is the code the parent runs. */ 104242137Sobrien if (getpid() == getpgrp()) { 105242137Sobrien signal(SIGALRM, angel_of_mercy); 106242137Sobrien alarm(stime); // time for bombing run... 107242137Sobrien cpid = wait4(pid, &status, 0, &ru); 108242137Sobrien alarm(0); 109242137Sobrien printf( 110242137Sobrien "Cleanly shutting down - pid %d pgroup %d (ppid %d)\n", 111242137Sobrien getpid(), getpgrp(), getppid()); 112242137Sobrien } else { 113242137Sobrien cpid = wait4(pid, &status, 0, &ru); 114242137Sobrien } 115242137Sobrien } 116242137Sobrien } 117242137Sobrien 118242137Sobrien return 0; 119242137Sobrien} 120242137Sobrien 121242137Sobrienint 122242137Sobrienmain(int argc, char *argv[]) 123242137Sobrien{ 124242137Sobrien time_t start /*,tvec*/; 125242137Sobrien char *endptr, *ctm; 126242137Sobrien size_t len; 127242137Sobrien int nflag, tflag; 128242137Sobrien int ch, k, maxprocperuid; 129242137Sobrien 130242137Sobrien (void)signal(SIGTERM, term); 131242137Sobrien 132242137Sobrien nflag = 0; 133242137Sobrien tflag = SLEEP; 134242137Sobrien 135242137Sobrien start = time(NULL); 136242137Sobrien ctm = ctime(&start); 137242137Sobrien ctm[24] = '\0'; // see: man 3 ctime 138242137Sobrien fprintf(stderr, "*** fork() generation started on \"%s\" ***\n", ctm); 139242137Sobrien 140242137Sobrien while ((ch = getopt(argc, argv, "n:t:v")) != -1) 141242137Sobrien switch (ch) { 142242137Sobrien case 'n': 143242137Sobrien nflag = strtol(optarg, &endptr, 10); 144242137Sobrien if (nflag <= 0 || *endptr != '\0') 145242137Sobrien errx(1, "illegal number, -n argument -- %s", 146242137Sobrien optarg); 147242137Sobrien break; 148242137Sobrien case 't': 149242137Sobrien tflag = strtol(optarg, &endptr, 10); 150242137Sobrien if (tflag <= 0 || *endptr != '\0') 151242137Sobrien errx(1, "illegal number, -t argument -- %s", 152242137Sobrien optarg); 153242137Sobrien break; 154242137Sobrien case 'v': 155242137Sobrien ++verbose; 156242137Sobrien break; 157242137Sobrien default: 158242137Sobrien usage(); 159242137Sobrien } 160242137Sobrien argv += optind; 161242137Sobrien 162242137Sobrien if (!nflag) { 163242137Sobrien len = sizeof(maxprocperuid); 164242137Sobrien k = sysctlbyname("kern.maxprocperuid", &maxprocperuid, &len, 165242137Sobrien NULL, 0); 166242137Sobrien assert(k != ENOMEM); 167242137Sobrien /* Try to allow a shell to still be started. */ 168242137Sobrien nflag = maxprocperuid - 10; 169242137Sobrien } 170242137Sobrien 171242137Sobrien // Ensure a unique process group to make killing all children easier. 172242137Sobrien setpgrp(0,0); 173242137Sobrien printf(" pid %d pgroup %d (ppid %d), %d fork chain over %d sec\n", 174242137Sobrien getpid(), getpgrp(), getppid(), nflag - 1, tflag); 175242137Sobrien 176242137Sobrien return bombing_run(nflag, tflag); 177242137Sobrien} 178