1/*- 2 * Copyright (c) 2012 David O'Brien 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29#include <sys/types.h> 30#include <sys/resource.h> 31#include <sys/sysctl.h> 32#include <sys/wait.h> 33 34#include <unistd.h> 35#include <stdlib.h> 36#include <signal.h> 37#include <errno.h> 38#include <stdio.h> 39#include <string.h> 40#include <time.h> 41#include <err.h> 42#include <assert.h> 43 44#ifndef SLEEP 45#define SLEEP 20 /* seconds */ 46#endif 47 48int verbose; 49 50static void 51usage(void) 52{ 53 fprintf(stderr, "usage: %s\n", getprogname()); 54 fprintf(stderr, "\t\t-n : length of fork(2) chain\n"); 55 fprintf(stderr, "\t\t-t : limit run-time seconds\n"); 56 exit(1); 57 /* NOTREACHED */ 58} 59 60void term(int); 61void 62term(int signum) 63{ 64 65 if (getpid() == getpgrp() || verbose) { 66 fprintf(stderr, 67 "pid %d pgroup %d (ppid %d): Received SIGTERM(%d), exiting...\n", 68 getpid(), getpgrp(), getppid(), signum); 69 } 70 exit(1); 71} 72 73void angel_of_mercy(int); 74void 75angel_of_mercy(int sig __unused) 76{ 77 78 signal(SIGALRM, SIG_IGN); /* ignore this signal */ 79 printf("Master process: alarmed waking up\n"); 80 killpg(0, SIGTERM); 81 return; 82} 83 84int bombing_run(unsigned, int); 85int 86bombing_run(unsigned chainlen, int stime) 87{ 88 struct rusage ru; 89 pid_t pid, cpid; 90 int status; 91 92 if (chainlen) { 93 switch (pid = fork()) { 94 case -1: 95 errx(1, "%s: can't fork", __func__); 96 97 case 0: 98 /* This is the code the child runs. */ 99 bombing_run(--chainlen, stime); 100 break; 101 102 default: 103 /* This is the code the parent runs. */ 104 if (getpid() == getpgrp()) { 105 signal(SIGALRM, angel_of_mercy); 106 alarm(stime); // time for bombing run... 107 cpid = wait4(pid, &status, 0, &ru); 108 alarm(0); 109 printf( 110 "Cleanly shutting down - pid %d pgroup %d (ppid %d)\n", 111 getpid(), getpgrp(), getppid()); 112 } else { 113 cpid = wait4(pid, &status, 0, &ru); 114 } 115 } 116 } 117 118 return 0; 119} 120 121int 122main(int argc, char *argv[]) 123{ 124 time_t start /*,tvec*/; 125 char *endptr, *ctm; 126 size_t len; 127 int nflag, tflag; 128 int ch, k, maxprocperuid; 129 130 (void)signal(SIGTERM, term); 131 132 nflag = 0; 133 tflag = SLEEP; 134 135 start = time(NULL); 136 ctm = ctime(&start); 137 ctm[24] = '\0'; // see: man 3 ctime 138 fprintf(stderr, "*** fork() generation started on \"%s\" ***\n", ctm); 139 140 while ((ch = getopt(argc, argv, "n:t:v")) != -1) 141 switch (ch) { 142 case 'n': 143 nflag = strtol(optarg, &endptr, 10); 144 if (nflag <= 0 || *endptr != '\0') 145 errx(1, "illegal number, -n argument -- %s", 146 optarg); 147 break; 148 case 't': 149 tflag = strtol(optarg, &endptr, 10); 150 if (tflag <= 0 || *endptr != '\0') 151 errx(1, "illegal number, -t argument -- %s", 152 optarg); 153 break; 154 case 'v': 155 ++verbose; 156 break; 157 default: 158 usage(); 159 } 160 argv += optind; 161 162 if (!nflag) { 163 len = sizeof(maxprocperuid); 164 k = sysctlbyname("kern.maxprocperuid", &maxprocperuid, &len, 165 NULL, 0); 166 assert(k != ENOMEM); 167 /* Try to allow a shell to still be started. */ 168 nflag = maxprocperuid - 10; 169 } 170 171 // Ensure a unique process group to make killing all children easier. 172 setpgrp(0,0); 173 printf(" pid %d pgroup %d (ppid %d), %d fork chain over %d sec\n", 174 getpid(), getpgrp(), getppid(), nflag - 1, tflag); 175 176 return bombing_run(nflag, tflag); 177} 178