1 /* $OpenBSD: dev-limit.c,v 1.3 2023/07/12 18:21:39 anton Exp $ */
2
3 /*
4 * Copyright (c) 2023 Alexandr Nedvedicky <sashan@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <signal.h>
25 #include <err.h>
26 #include <sys/wait.h>
27
28 static int sigchild;
29
30 static void
usage(const char * progname)31 usage(const char *progname)
32 {
33 fprintf(stderr,
34 "%s [-d] [-s success_count] [-c child_count] [-t timeout]\n"
35 "if no options are specified program opens '/dev/pf'\n"
36 "and waits for 5s before it exits\n"
37 "\t-s how many children should successfully open /dev/pf\n"
38 "\t-c children to fork, each child opens /dev/pf\n"
39 "\t-t timeout in seconds each child should wait\n"
40 "after successfully opening /dev/pf. Child exits immediately\n"
41 "if /dev/pf can not be opened\n", progname);
42 exit(1);
43 }
44
45 static void
handle_sigchild(int signum)46 handle_sigchild(int signum)
47 {
48 if (signum == SIGCHLD)
49 sigchild = 1;
50 }
51
52 static void
open_pf_and_exit(unsigned int sleep_time)53 open_pf_and_exit(unsigned int sleep_time)
54 {
55 if (open("/dev/pf", O_RDONLY) == -1)
56 exit(1);
57
58 sleep(sleep_time);
59 exit(0);
60 }
61
62 int
main(int argc,char * const argv[])63 main(int argc, char *const argv[])
64 {
65 pid_t *pids;
66 unsigned int chld_count = 0;
67 unsigned int sleep_time = 5;
68 unsigned int expect_success = 0;
69 unsigned int success, errors, i;
70 const char *errstr, *sleep_arg;
71 int status;
72 int c;
73
74 while ((c = getopt(argc, argv, "t:c:s:")) != -1) {
75 switch (c) {
76 case 't':
77 sleep_arg = (char *const)optarg;
78 sleep_time = strtonum(optarg, 1, 60, &errstr);
79 if (errstr != NULL) {
80 fprintf(stderr,
81 "%s invalid sleep time %s: %s, must be in "
82 "range <1, 60>\n", argv[0], errstr, optarg);
83 usage(argv[0]);
84 }
85 break;
86 case 'c':
87 chld_count = strtonum(optarg, 1, 32768, &errstr);
88 if (errstr != NULL) {
89 fprintf(stderr,
90 "%s invalid children count %s: %s, must be "
91 "in range <1, 32768>\n", argv[0], optarg,
92 errstr);
93 usage(argv[0]);
94 }
95 break;
96 case 's':
97 expect_success = strtonum(optarg, 0, 32768, &errstr);
98 if (errstr != NULL) {
99 fprintf(stderr,
100 "%s invalid expect success count %s: %s "
101 "must be in range <1, 32768>\n", argv[0],
102 optarg, errstr);
103 usage(argv[0]);
104 }
105 break;
106 default:
107 usage(argv[0]);
108 }
109 }
110
111 if (chld_count == 0)
112 open_pf_and_exit(sleep_time);
113
114 signal(SIGCHLD, handle_sigchild);
115 pids = (pid_t *)malloc(sizeof(pid_t) * chld_count);
116 if (pids == 0)
117 err(1, NULL);
118
119 i = 0;
120 while ((sigchild == 0) && (i < chld_count)) {
121 pid_t pid;
122
123 pid = fork();
124 pids[i++] = pid;
125 if (pid == -1)
126 warn("fork");
127 else if (pid == 0)
128 execl(argv[0], argv[0], "-t", sleep_arg, NULL);
129 }
130 chld_count = i;
131
132 success = 0;
133 errors = 0;
134 for (i = 0; i < chld_count; i++) {
135 waitpid(pids[i], &status, 0);
136 if (status == 0)
137 success++;
138 else
139 errors++;
140 }
141
142 free(pids);
143
144 if (success != expect_success) {
145 printf("Successful opens: %u\n", success);
146 printf("Failures: %u\n", errors);
147 printf("Expected opens: %u\n", expect_success);
148 printf("%u vs %u = %u + %u\n",
149 chld_count, errors + success, errors, success);
150 return (1);
151 }
152
153 return (0);
154 }
155