-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
183 lines (144 loc) · 4.16 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#define _GNU_SOURCE
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <unistd.h>
static char *prog_name;
static _Noreturn void exit_error(int line, char *msg) {
fprintf(stderr, "[ERROR %s] line %d: %s\n", prog_name, line, msg);
_exit(1);
}
static _Noreturn void exit_errno(int line) { exit_error(line, strerror(errno)); }
static int get_wait_second(void) {
char *s = getenv("PID1_WAIT_SECOND");
if (s) {
int i = atoi(s);
if (i > 0) return i;
}
return 5; // default
}
static _Noreturn void exec_child_or_exit_error(int line, char **argv) {
execvp(argv[0], argv);
exit_errno(line);
}
static void set_handler(int sig, void (*handler)(int)) {
sigaction(sig, &(struct sigaction){.sa_handler = handler}, NULL);
}
static volatile sig_atomic_t alarm_timeout = false;
static void alarm_handler(int sig) {
(void)sig;
alarm_timeout = true;
}
static int kill_all_and_wait_till_complete(void) {
kill(-1, SIGCONT);
kill(-1, SIGTERM);
set_handler(SIGALRM, alarm_handler);
alarm(get_wait_second());
int completed = false;
while (!alarm_timeout) {
if (wait(NULL) == -1 && errno == ECHILD) {
completed = true;
break;
}
}
alarm(0);
set_handler(SIGALRM, SIG_DFL);
return completed;
}
static volatile sig_atomic_t quit = false;
static void main_pause_quit_handler(int sig) {
(void)sig;
quit = true;
}
static void main_pause_sigchld_handler(int sig) {
(void)sig;
while (waitpid(-1, NULL, WNOHANG) > 0);
}
static _Noreturn void main_pause(void) {
set_handler(SIGINT, main_pause_quit_handler);
set_handler(SIGTERM, main_pause_quit_handler);
set_handler(SIGCHLD, main_pause_sigchld_handler);
while (!quit) pause();
set_handler(SIGINT, SIG_DFL);
set_handler(SIGTERM, SIG_DFL);
set_handler(SIGCHLD, SIG_DFL);
_exit(kill_all_and_wait_till_complete() ? 0 : 1);
}
static pid_t cpid;
static void forward_sig_handler(int sig) { kill(cpid, sig); }
static volatile int cpid_status;
static void main_with_child_sigchld_handler(int sig) {
(void)sig;
while (true) {
int status;
pid_t wpid = waitpid(-1, &status, WNOHANG);
if (wpid <= 0) break;
if (wpid == cpid && (WIFEXITED(status) || WIFSIGNALED(status))) {
cpid_status = status;
quit = true;
}
}
}
static _Noreturn void main_with_child(char **argv) {
for (int fd = 0; fd < 3; fd++) ioctl(fd, TIOCNOTTY);
cpid = fork();
if (cpid == -1) {
exit_errno(__LINE__);
} else if (!cpid) { // child process
setsid();
for (int fd = 0; fd < 3; fd++) ioctl(fd, TIOCSCTTY, 1);
exec_child_or_exit_error(__LINE__, argv);
exit_error(__LINE__, "DEAD CODE !!");
}
set_handler(SIGCHLD, main_with_child_sigchld_handler);
main_with_child_sigchld_handler(SIGCHLD); // to handle the case when child process is already dead
set_handler(SIGHUP, forward_sig_handler);
set_handler(SIGINT, forward_sig_handler);
set_handler(SIGQUIT, forward_sig_handler);
set_handler(SIGTERM, forward_sig_handler);
set_handler(SIGUSR1, forward_sig_handler);
set_handler(SIGUSR2, forward_sig_handler);
set_handler(SIGWINCH, forward_sig_handler);
while (!quit) pause();
int wstatus = cpid_status;
set_handler(SIGHUP, SIG_DFL);
set_handler(SIGINT, SIG_DFL);
set_handler(SIGQUIT, SIG_DFL);
set_handler(SIGTERM, SIG_DFL);
set_handler(SIGUSR1, SIG_DFL);
set_handler(SIGUSR2, SIG_DFL);
set_handler(SIGWINCH, SIG_DFL);
set_handler(SIGCHLD, SIG_DFL);
kill_all_and_wait_till_complete();
if (WIFEXITED(wstatus)) _exit(WEXITSTATUS(wstatus));
kill(getpid(), WTERMSIG(wstatus));
_exit(128 + WTERMSIG(wstatus));
}
int main(int argc, char **argv) {
prog_name = argv[0];
for (int i = 1; i < argc; i++) {
if (strcmp("--", argv[i]) == 0) {
argc -= i;
argv += i;
break;
}
}
argc--;
argv++;
if (getpid() != 1) {
fprintf(stderr, "[WARNING]: %s will not working unless running as pid 1\n", prog_name);
if (argc > 0) exec_child_or_exit_error(__LINE__, argv);
_exit(1);
}
if (argc == 0) {
main_pause();
} else {
main_with_child(argv);
}
exit_error(__LINE__, "DEAD CODE !!");
}