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