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 28static int sigchild; 29 30static void 31usage(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 45static void 46handle_sigchild(int signum) 47{ 48 if (signum == SIGCHLD) 49 sigchild = 1; 50} 51 52static void 53open_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 62int 63main(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