coreutils

General Software Utilities
git clone http://git.omkov.net/coreutils
Log | Tree | Refs | README | LICENCE | Download

coreutils/src/timeout.c (230 lines, 6.5 KiB) -rw-r--r-- file download

48e316b Jamozed 2022-03-01 03:04:16
0
// timeout.c
48e316b Jamozed 2022-03-01 03:04:16
1
// OMKOV coreutils timeout
48e316b Jamozed 2022-03-01 03:04:16
2
// Copyright (C) 2022, Jakob Wakeling
e2140ec Jamozed 2022-03-06 15:27:45
3
// MIT Licence
48e316b Jamozed 2022-03-01 03:04:16
4
48e316b Jamozed 2022-03-01 03:04:16
5
#define VERSION "0.1.0"
48e316b Jamozed 2022-03-01 03:04:16
6
48e316b Jamozed 2022-03-01 03:04:16
7
#include "util/error.h"
48e316b Jamozed 2022-03-01 03:04:16
8
#include "util/optget.h"
48e316b Jamozed 2022-03-01 03:04:16
9
#include "util/util.h"
48e316b Jamozed 2022-03-01 03:04:16
10
48e316b Jamozed 2022-03-01 03:04:16
11
#include <sys/wait.h>
48e316b Jamozed 2022-03-01 03:04:16
12
#include <unistd.h>
48e316b Jamozed 2022-03-01 03:04:16
13
48e316b Jamozed 2022-03-01 03:04:16
14
#include <errno.h>
48e316b Jamozed 2022-03-01 03:04:16
15
#include <signal.h>
48e316b Jamozed 2022-03-01 03:04:16
16
#include <stdio.h>
48e316b Jamozed 2022-03-01 03:04:16
17
#include <stdlib.h>
48e316b Jamozed 2022-03-01 03:04:16
18
#include <time.h>
48e316b Jamozed 2022-03-01 03:04:16
19
48e316b Jamozed 2022-03-01 03:04:16
20
static struct lop lops[] = {
48e316b Jamozed 2022-03-01 03:04:16
21
	{ "help",    ARG_NUL, 256 },
48e316b Jamozed 2022-03-01 03:04:16
22
	{ "version", ARG_NUL, 257 },
48e316b Jamozed 2022-03-01 03:04:16
23
	{ NULL, 0, 0 }
48e316b Jamozed 2022-03-01 03:04:16
24
};
48e316b Jamozed 2022-03-01 03:04:16
25
48e316b Jamozed 2022-03-01 03:04:16
26
static f64 itime = 0, ktime = 0;
48e316b Jamozed 2022-03-01 03:04:16
27
static int tosig = SIGTERM;
48e316b Jamozed 2022-03-01 03:04:16
28
48e316b Jamozed 2022-03-01 03:04:16
29
static pid_t pid = 0;
48e316b Jamozed 2022-03-01 03:04:16
30
static bool tout = false;
48e316b Jamozed 2022-03-01 03:04:16
31
48e316b Jamozed 2022-03-01 03:04:16
32
static f64 time_parse(const char *s);
48e316b Jamozed 2022-03-01 03:04:16
33
static int timeout(char *av[]);
48e316b Jamozed 2022-03-01 03:04:16
34
48e316b Jamozed 2022-03-01 03:04:16
35
static void signal_install(int sig);
48e316b Jamozed 2022-03-01 03:04:16
36
static void signal_block(int sig, sigset_t *oset);
48e316b Jamozed 2022-03-01 03:04:16
37
static void handle_sigalrm(int sig);
48e316b Jamozed 2022-03-01 03:04:16
38
static void handle_sigchld(int sig);
48e316b Jamozed 2022-03-01 03:04:16
39
static int signal_send(pid_t pid, int sig);
48e316b Jamozed 2022-03-01 03:04:16
40
48e316b Jamozed 2022-03-01 03:04:16
41
static void timer(f64 duration);
48e316b Jamozed 2022-03-01 03:04:16
42
static struct timespec f64totimespec(f64 seconds);
48e316b Jamozed 2022-03-01 03:04:16
43
48e316b Jamozed 2022-03-01 03:04:16
44
static void hlp(void);
48e316b Jamozed 2022-03-01 03:04:16
45
static void ver(void);
48e316b Jamozed 2022-03-01 03:04:16
46
48e316b Jamozed 2022-03-01 03:04:16
47
int main(int ac, char *av[]) { A0 = av[0];
48e316b Jamozed 2022-03-01 03:04:16
48
	struct opt opt = OPTGET_INIT; opt.str = "k:s:"; opt.lops = lops;
48e316b Jamozed 2022-03-01 03:04:16
49
	for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) {
48e316b Jamozed 2022-03-01 03:04:16
50
	case 'k': { ktime = time_parse(opt.arg); } break;
48e316b Jamozed 2022-03-01 03:04:16
51
	case 's': { /* TODO */ return 1; } break;
48e316b Jamozed 2022-03-01 03:04:16
52
	case 256: { hlp(); } return 0;
48e316b Jamozed 2022-03-01 03:04:16
53
	case 257: { ver(); } return 0;
48e316b Jamozed 2022-03-01 03:04:16
54
	default: {} return 125;
48e316b Jamozed 2022-03-01 03:04:16
55
	}
48e316b Jamozed 2022-03-01 03:04:16
56
	
48e316b Jamozed 2022-03-01 03:04:16
57
	if ((ac - opt.ind) < 2) { error(125, "missing operand(s)"); }
48e316b Jamozed 2022-03-01 03:04:16
58
	
48e316b Jamozed 2022-03-01 03:04:16
59
	itime = time_parse(av[opt.ind]);
48e316b Jamozed 2022-03-01 03:04:16
60
	return timeout(&av[opt.ind + 1]);
48e316b Jamozed 2022-03-01 03:04:16
61
}
48e316b Jamozed 2022-03-01 03:04:16
62
48e316b Jamozed 2022-03-01 03:04:16
63
/* Parse a duration string. */
48e316b Jamozed 2022-03-01 03:04:16
64
static inline f64 time_parse(const char *s) {
48e316b Jamozed 2022-03-01 03:04:16
65
	char *ep = NULL; errno = 0; f64 time = strtod(s, &ep);
48e316b Jamozed 2022-03-01 03:04:16
66
	if (errno != 0) { error(1, "%s: %s", s, SERR); }
48e316b Jamozed 2022-03-01 03:04:16
67
	if (time < 0) { error(1, "%s: duration cannot be negative", s); }
48e316b Jamozed 2022-03-01 03:04:16
68
	
48e316b Jamozed 2022-03-01 03:04:16
69
	/* Check for an apply any valid suffix */
48e316b Jamozed 2022-03-01 03:04:16
70
	if (ep != NULL && ep[0] != '\0') {
48e316b Jamozed 2022-03-01 03:04:16
71
		switch (ep[0]) {
48e316b Jamozed 2022-03-01 03:04:16
72
		case 'D': { time *= 86400; } break;
48e316b Jamozed 2022-03-01 03:04:16
73
		case 'h': { time *= 3600;  } break;
48e316b Jamozed 2022-03-01 03:04:16
74
		case 'm': { time *= 60;    } break;
48e316b Jamozed 2022-03-01 03:04:16
75
		case 's': { time *= 1;     } break;
48e316b Jamozed 2022-03-01 03:04:16
76
		default: { error(1, "%s: invalid suffix", s); } break;
48e316b Jamozed 2022-03-01 03:04:16
77
		}
48e316b Jamozed 2022-03-01 03:04:16
78
		
48e316b Jamozed 2022-03-01 03:04:16
79
		if (ep[1] != '\0') { error(1, "%s: invalid suffix", s); }
48e316b Jamozed 2022-03-01 03:04:16
80
	}
48e316b Jamozed 2022-03-01 03:04:16
81
	
48e316b Jamozed 2022-03-01 03:04:16
82
	return time;
48e316b Jamozed 2022-03-01 03:04:16
83
}
48e316b Jamozed 2022-03-01 03:04:16
84
48e316b Jamozed 2022-03-01 03:04:16
85
static int timeout(char *av[]) {
48e316b Jamozed 2022-03-01 03:04:16
86
	signal_install(tosig);
48e316b Jamozed 2022-03-01 03:04:16
87
	signal(SIGTTIN, SIG_IGN);
48e316b Jamozed 2022-03-01 03:04:16
88
	signal(SIGTTOU, SIG_IGN);
48e316b Jamozed 2022-03-01 03:04:16
89
	
48e316b Jamozed 2022-03-01 03:04:16
90
	switch ((pid = fork())) {
48e316b Jamozed 2022-03-01 03:04:16
91
	case -1: { error(125, "%s", SERR); }
48e316b Jamozed 2022-03-01 03:04:16
92
	case  0: { /* Child */
48e316b Jamozed 2022-03-01 03:04:16
93
		signal(SIGTTIN, SIG_DFL);
48e316b Jamozed 2022-03-01 03:04:16
94
		signal(SIGTTOU, SIG_DFL);
48e316b Jamozed 2022-03-01 03:04:16
95
		
48e316b Jamozed 2022-03-01 03:04:16
96
		execvp(av[0], av);
48e316b Jamozed 2022-03-01 03:04:16
97
		
48e316b Jamozed 2022-03-01 03:04:16
98
		error((errno = ENOENT) ? 127 : 126, "%s: %s", av[0], SERR);
48e316b Jamozed 2022-03-01 03:04:16
99
	} break;
48e316b Jamozed 2022-03-01 03:04:16
100
	default: { /* Parent */
48e316b Jamozed 2022-03-01 03:04:16
101
		int status; pid_t wpid; sigset_t sset;
48e316b Jamozed 2022-03-01 03:04:16
102
		
48e316b Jamozed 2022-03-01 03:04:16
103
		/* Unblock and start the timeout timer */
48e316b Jamozed 2022-03-01 03:04:16
104
		/* TODO unblock */ timer(itime);
48e316b Jamozed 2022-03-01 03:04:16
105
		
48e316b Jamozed 2022-03-01 03:04:16
106
		signal_block(tosig, &sset);
48e316b Jamozed 2022-03-01 03:04:16
107
		
48e316b Jamozed 2022-03-01 03:04:16
108
		/* Wait for the timer to expire, or the child to die */
48e316b Jamozed 2022-03-01 03:04:16
109
		for (; (wpid = waitpid(pid, &status, WNOHANG)) == 0;) {
48e316b Jamozed 2022-03-01 03:04:16
110
			sigsuspend(&sset);
48e316b Jamozed 2022-03-01 03:04:16
111
		}
48e316b Jamozed 2022-03-01 03:04:16
112
		
48e316b Jamozed 2022-03-01 03:04:16
113
		if (wpid == -1) { error(125, "%s", SERR); }
48e316b Jamozed 2022-03-01 03:04:16
114
		
48e316b Jamozed 2022-03-01 03:04:16
115
		/* If the child process exited normally */
48e316b Jamozed 2022-03-01 03:04:16
116
		if (WIFEXITED(status)) { status = WEXITSTATUS(status); }
48e316b Jamozed 2022-03-01 03:04:16
117
		/* If the child process exited abnormally */
48e316b Jamozed 2022-03-01 03:04:16
118
		else if (WIFSIGNALED(status)) {
48e316b Jamozed 2022-03-01 03:04:16
119
			int sig = WTERMSIG(status);
48e316b Jamozed 2022-03-01 03:04:16
120
			
48e316b Jamozed 2022-03-01 03:04:16
121
			status = 128 + sig;
48e316b Jamozed 2022-03-01 03:04:16
122
		}
48e316b Jamozed 2022-03-01 03:04:16
123
		else { error(125, "%s", SERR); }
48e316b Jamozed 2022-03-01 03:04:16
124
		
48e316b Jamozed 2022-03-01 03:04:16
125
		return tout ? 124 : status;
48e316b Jamozed 2022-03-01 03:04:16
126
	} break;
48e316b Jamozed 2022-03-01 03:04:16
127
	}
48e316b Jamozed 2022-03-01 03:04:16
128
}
48e316b Jamozed 2022-03-01 03:04:16
129
48e316b Jamozed 2022-03-01 03:04:16
130
/* Configure SIGALRM handling. */
48e316b Jamozed 2022-03-01 03:04:16
131
static void signal_install(int sig) {
48e316b Jamozed 2022-03-01 03:04:16
132
	struct sigaction sa0 = { .sa_handler = handle_sigalrm, .sa_flags = SA_RESTART };
48e316b Jamozed 2022-03-01 03:04:16
133
	struct sigaction sa1 = { .sa_handler = handle_sigchld, .sa_flags = SA_RESTART };
48e316b Jamozed 2022-03-01 03:04:16
134
	
48e316b Jamozed 2022-03-01 03:04:16
135
	sigemptyset(&sa0.sa_mask);
48e316b Jamozed 2022-03-01 03:04:16
136
	sigaction(SIGALRM, &sa0, NULL); /* Alarm signal, from the timeout timer */
48e316b Jamozed 2022-03-01 03:04:16
137
	sigaction(SIGHUP,  &sa0, NULL); /* Hangup signal, i.e. upon tty closing */
48e316b Jamozed 2022-03-01 03:04:16
138
	sigaction(SIGINT,  &sa0, NULL); /* Interrupt signal, i.e. from Ctrl+C */
48e316b Jamozed 2022-03-01 03:04:16
139
	sigaction(SIGQUIT, &sa0, NULL); /* Quit signal, i.e. from Ctrl+\ */
48e316b Jamozed 2022-03-01 03:04:16
140
	sigaction(SIGTERM, &sa0, NULL); /* Terminate signal, i.e. upon death */
48e316b Jamozed 2022-03-01 03:04:16
141
	sigaction(sig,     &sa0, NULL); /* User specified signal */
48e316b Jamozed 2022-03-01 03:04:16
142
	
48e316b Jamozed 2022-03-01 03:04:16
143
	sigemptyset(&sa1.sa_mask);
48e316b Jamozed 2022-03-01 03:04:16
144
	sigaction(SIGCHLD, &sa1, NULL); /* Child signal, i.e. upon child death */
48e316b Jamozed 2022-03-01 03:04:16
145
}
48e316b Jamozed 2022-03-01 03:04:16
146
48e316b Jamozed 2022-03-01 03:04:16
147
/* Block all appropriate signals, as per signal_install(). */
48e316b Jamozed 2022-03-01 03:04:16
148
static void signal_block(int sig, sigset_t *oset) {
48e316b Jamozed 2022-03-01 03:04:16
149
	sigset_t set; sigemptyset(&set);
48e316b Jamozed 2022-03-01 03:04:16
150
	sigaddset(&set, SIGALRM);
48e316b Jamozed 2022-03-01 03:04:16
151
	sigaddset(&set, SIGHUP);
48e316b Jamozed 2022-03-01 03:04:16
152
	sigaddset(&set, SIGINT);
48e316b Jamozed 2022-03-01 03:04:16
153
	sigaddset(&set, SIGQUIT);
48e316b Jamozed 2022-03-01 03:04:16
154
	sigaddset(&set, SIGTERM);
48e316b Jamozed 2022-03-01 03:04:16
155
	sigaddset(&set, sig);
48e316b Jamozed 2022-03-01 03:04:16
156
	
48e316b Jamozed 2022-03-01 03:04:16
157
	sigaddset(&set, SIGCHLD);
48e316b Jamozed 2022-03-01 03:04:16
158
	
48e316b Jamozed 2022-03-01 03:04:16
159
	if (sigprocmask(SIG_BLOCK, &set, oset) == -1) { warn("%s", SERR); }
48e316b Jamozed 2022-03-01 03:04:16
160
}
48e316b Jamozed 2022-03-01 03:04:16
161
48e316b Jamozed 2022-03-01 03:04:16
162
/* Handle SIGALRM. */
48e316b Jamozed 2022-03-01 03:04:16
163
static void handle_sigalrm(int sig) {
48e316b Jamozed 2022-03-01 03:04:16
164
	if (sig == SIGALRM) { tout = true; sig = tosig; }
48e316b Jamozed 2022-03-01 03:04:16
165
	
48e316b Jamozed 2022-03-01 03:04:16
166
	/* If we are the child, or the child is not yet born, then exit */
48e316b Jamozed 2022-03-01 03:04:16
167
	if (pid == 0) { exit(128 + sig); }
48e316b Jamozed 2022-03-01 03:04:16
168
	
48e316b Jamozed 2022-03-01 03:04:16
169
	if (ktime) {
48e316b Jamozed 2022-03-01 03:04:16
170
		/* Start a new timeout, after which SIGKILL will be sent */
48e316b Jamozed 2022-03-01 03:04:16
171
		int ern = errno; tosig = SIGKILL; timer(ktime); ktime = 0; errno = ern;
48e316b Jamozed 2022-03-01 03:04:16
172
	}
48e316b Jamozed 2022-03-01 03:04:16
173
	
48e316b Jamozed 2022-03-01 03:04:16
174
	signal_send(pid, sig);
48e316b Jamozed 2022-03-01 03:04:16
175
}
48e316b Jamozed 2022-03-01 03:04:16
176
48e316b Jamozed 2022-03-01 03:04:16
177
/* Handle SIGCHLD. */
48e316b Jamozed 2022-03-01 03:04:16
178
static void handle_sigchld(int sig) {}
48e316b Jamozed 2022-03-01 03:04:16
179
48e316b Jamozed 2022-03-01 03:04:16
180
/* Send a signal to a process, ignoring signals in the case of a group. */
48e316b Jamozed 2022-03-01 03:04:16
181
static int signal_send(pid_t pid, int sig) {
48e316b Jamozed 2022-03-01 03:04:16
182
	if (pid == 0) { signal(sig, SIG_IGN); }
48e316b Jamozed 2022-03-01 03:04:16
183
	return kill(pid, sig);
48e316b Jamozed 2022-03-01 03:04:16
184
}
48e316b Jamozed 2022-03-01 03:04:16
185
48e316b Jamozed 2022-03-01 03:04:16
186
/* Start the timeout timer, after which we'll recieve a SIGALRM. */
48e316b Jamozed 2022-03-01 03:04:16
187
static void timer(f64 duration) {
48e316b Jamozed 2022-03-01 03:04:16
188
	struct timespec ts = f64totimespec(duration);
48e316b Jamozed 2022-03-01 03:04:16
189
	struct itimerspec its = { { 0, 0 }, ts }; timer_t tid;
48e316b Jamozed 2022-03-01 03:04:16
190
	
48e316b Jamozed 2022-03-01 03:04:16
191
	/* Create and set a timer with timer_create and timer_settime */
48e316b Jamozed 2022-03-01 03:04:16
192
	if (timer_create(CLOCK_REALTIME, NULL, &tid) == 0) {
48e316b Jamozed 2022-03-01 03:04:16
193
		if (timer_settime(tid, 0, &its, NULL) == 0) { return; }
48e316b Jamozed 2022-03-01 03:04:16
194
		else { warn("timer_settime: %s", SERR); timer_delete(tid); }
48e316b Jamozed 2022-03-01 03:04:16
195
	}
48e316b Jamozed 2022-03-01 03:04:16
196
	else { warn("timer_create: %s", SERR); }
48e316b Jamozed 2022-03-01 03:04:16
197
	
48e316b Jamozed 2022-03-01 03:04:16
198
	/* Fallback to alarm() with single second precision upon failure */
48e316b Jamozed 2022-03-01 03:04:16
199
	if (duration >= (f64)U32_MAX) { alarm(U32_MAX); }
48e316b Jamozed 2022-03-01 03:04:16
200
	else { alarm((u32)duration + ((u32)duration < duration)); }
48e316b Jamozed 2022-03-01 03:04:16
201
}
48e316b Jamozed 2022-03-01 03:04:16
202
48e316b Jamozed 2022-03-01 03:04:16
203
/* Convert an f64 value to a timespec struct. */
48e316b Jamozed 2022-03-01 03:04:16
204
static struct timespec f64totimespec(f64 seconds) {
48e316b Jamozed 2022-03-01 03:04:16
205
	struct timespec ts; seconds += 0.5e-9;
48e316b Jamozed 2022-03-01 03:04:16
206
	ts.tv_sec = (time_t)seconds;
48e316b Jamozed 2022-03-01 03:04:16
207
	ts.tv_nsec = (seconds - ts.tv_sec) * 1.0e+9;
48e316b Jamozed 2022-03-01 03:04:16
208
	return ts;
48e316b Jamozed 2022-03-01 03:04:16
209
}
48e316b Jamozed 2022-03-01 03:04:16
210
48e316b Jamozed 2022-03-01 03:04:16
211
/* Print help information. */
48e316b Jamozed 2022-03-01 03:04:16
212
static void hlp(void) {
48e316b Jamozed 2022-03-01 03:04:16
213
	puts("timeout - Invoke a command with a timelimit\n");
48e316b Jamozed 2022-03-01 03:04:16
214
	puts("Usage:");
48e316b Jamozed 2022-03-01 03:04:16
215
	puts("  timeout [-ks] duration command [argument...]\n");
48e316b Jamozed 2022-03-01 03:04:16
216
	puts("Options:");
48e316b Jamozed 2022-03-01 03:04:16
217
	puts("  -k duration  Send a KILL signal after the specified duration");
48e316b Jamozed 2022-03-01 03:04:16
218
	puts("  -s signal    Send the specified signal instead of SIGINT");
48e316b Jamozed 2022-03-01 03:04:16
219
	puts("  --help       Display help information");
48e316b Jamozed 2022-03-01 03:04:16
220
	puts("  --version    Display version information");
48e316b Jamozed 2022-03-01 03:04:16
221
}
48e316b Jamozed 2022-03-01 03:04:16
222
48e316b Jamozed 2022-03-01 03:04:16
223
/* Print version information. */
48e316b Jamozed 2022-03-01 03:04:16
224
static void ver(void) {
48e316b Jamozed 2022-03-01 03:04:16
225
	puts("OMKOV cryptutils timeout, version " VERSION);
48e316b Jamozed 2022-03-01 03:04:16
226
	puts("Copyright (C) 2022, Jakob Wakeling");
e2140ec Jamozed 2022-03-06 15:27:45
227
	puts("MIT Licence (https://opensource.org/licenses/MIT)");
48e316b Jamozed 2022-03-01 03:04:16
228
}
229