##// END OF EJS Templates
chg: document why we send SIGHUP and SIGINT to process group...
Jun Wu -
r31229:68c94f28 default
parent child Browse files
Show More
@@ -1,227 +1,231
1 1 /*
2 2 * Utilities about process handling - signal and subprocess (ex. pager)
3 3 *
4 4 * Copyright (c) 2011 Yuya Nishihara <yuya@tcha.org>
5 5 *
6 6 * This software may be used and distributed according to the terms of the
7 7 * GNU General Public License version 2 or any later version.
8 8 */
9 9
10 10 #include <assert.h>
11 11 #include <errno.h>
12 12 #include <signal.h>
13 13 #include <stdio.h>
14 14 #include <string.h>
15 15 #include <sys/wait.h>
16 16 #include <unistd.h>
17 17
18 18 #include "procutil.h"
19 19 #include "util.h"
20 20
21 21 static pid_t pagerpid = 0;
22 22 static pid_t peerpgid = 0;
23 23 static pid_t peerpid = 0;
24 24
25 25 static void forwardsignal(int sig)
26 26 {
27 27 assert(peerpid > 0);
28 28 if (kill(peerpid, sig) < 0)
29 29 abortmsgerrno("cannot kill %d", peerpid);
30 30 debugmsg("forward signal %d", sig);
31 31 }
32 32
33 33 static void forwardsignaltogroup(int sig)
34 34 {
35 35 /* prefer kill(-pgid, sig), fallback to pid if pgid is invalid */
36 36 pid_t killpid = peerpgid > 1 ? -peerpgid : peerpid;
37 37 if (kill(killpid, sig) < 0)
38 38 abortmsgerrno("cannot kill %d", killpid);
39 39 debugmsg("forward signal %d to %d", sig, killpid);
40 40 }
41 41
42 42 static void handlestopsignal(int sig)
43 43 {
44 44 sigset_t unblockset, oldset;
45 45 struct sigaction sa, oldsa;
46 46 if (sigemptyset(&unblockset) < 0)
47 47 goto error;
48 48 if (sigaddset(&unblockset, sig) < 0)
49 49 goto error;
50 50 memset(&sa, 0, sizeof(sa));
51 51 sa.sa_handler = SIG_DFL;
52 52 sa.sa_flags = SA_RESTART;
53 53 if (sigemptyset(&sa.sa_mask) < 0)
54 54 goto error;
55 55
56 56 forwardsignal(sig);
57 57 if (raise(sig) < 0) /* resend to self */
58 58 goto error;
59 59 if (sigaction(sig, &sa, &oldsa) < 0)
60 60 goto error;
61 61 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
62 62 goto error;
63 63 /* resent signal will be handled before sigprocmask() returns */
64 64 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
65 65 goto error;
66 66 if (sigaction(sig, &oldsa, NULL) < 0)
67 67 goto error;
68 68 return;
69 69
70 70 error:
71 71 abortmsgerrno("failed to handle stop signal");
72 72 }
73 73
74 74 static void handlechildsignal(int sig UNUSED_)
75 75 {
76 76 if (peerpid == 0 || pagerpid == 0)
77 77 return;
78 78 /* if pager exits, notify the server with SIGPIPE immediately.
79 79 * otherwise the server won't get SIGPIPE if it does not write
80 80 * anything. (issue5278) */
81 81 if (waitpid(pagerpid, NULL, WNOHANG) == pagerpid)
82 82 kill(peerpid, SIGPIPE);
83 83 }
84 84
85 85 void setupsignalhandler(pid_t pid, pid_t pgid)
86 86 {
87 87 if (pid <= 0)
88 88 return;
89 89 peerpid = pid;
90 90 peerpgid = (pgid <= 1 ? 0 : pgid);
91 91
92 92 struct sigaction sa;
93 93 memset(&sa, 0, sizeof(sa));
94
95 /* deadly signals meant to be sent to a process group:
96 * - SIGHUP: usually generated by the kernel, when termination of a
97 * process causes that process group to become orphaned
98 * - SIGINT: usually generated by the terminal */
94 99 sa.sa_handler = forwardsignaltogroup;
95 100 sa.sa_flags = SA_RESTART;
96 101 if (sigemptyset(&sa.sa_mask) < 0)
97 102 goto error;
98
99 103 if (sigaction(SIGHUP, &sa, NULL) < 0)
100 104 goto error;
101 105 if (sigaction(SIGINT, &sa, NULL) < 0)
102 106 goto error;
103 107
104 108 /* terminate frontend by double SIGTERM in case of server freeze */
105 109 sa.sa_handler = forwardsignal;
106 110 sa.sa_flags |= SA_RESETHAND;
107 111 if (sigaction(SIGTERM, &sa, NULL) < 0)
108 112 goto error;
109 113
110 114 /* notify the worker about window resize events */
111 115 sa.sa_flags = SA_RESTART;
112 116 if (sigaction(SIGWINCH, &sa, NULL) < 0)
113 117 goto error;
114 118 /* propagate job control requests to worker */
115 119 sa.sa_handler = forwardsignal;
116 120 sa.sa_flags = SA_RESTART;
117 121 if (sigaction(SIGCONT, &sa, NULL) < 0)
118 122 goto error;
119 123 sa.sa_handler = handlestopsignal;
120 124 sa.sa_flags = SA_RESTART;
121 125 if (sigaction(SIGTSTP, &sa, NULL) < 0)
122 126 goto error;
123 127 /* get notified when pager exits */
124 128 sa.sa_handler = handlechildsignal;
125 129 sa.sa_flags = SA_RESTART;
126 130 if (sigaction(SIGCHLD, &sa, NULL) < 0)
127 131 goto error;
128 132
129 133 return;
130 134
131 135 error:
132 136 abortmsgerrno("failed to set up signal handlers");
133 137 }
134 138
135 139 void restoresignalhandler(void)
136 140 {
137 141 struct sigaction sa;
138 142 memset(&sa, 0, sizeof(sa));
139 143 sa.sa_handler = SIG_DFL;
140 144 sa.sa_flags = SA_RESTART;
141 145 if (sigemptyset(&sa.sa_mask) < 0)
142 146 goto error;
143 147
144 148 if (sigaction(SIGHUP, &sa, NULL) < 0)
145 149 goto error;
146 150 if (sigaction(SIGTERM, &sa, NULL) < 0)
147 151 goto error;
148 152 if (sigaction(SIGWINCH, &sa, NULL) < 0)
149 153 goto error;
150 154 if (sigaction(SIGCONT, &sa, NULL) < 0)
151 155 goto error;
152 156 if (sigaction(SIGTSTP, &sa, NULL) < 0)
153 157 goto error;
154 158 if (sigaction(SIGCHLD, &sa, NULL) < 0)
155 159 goto error;
156 160
157 161 /* ignore Ctrl+C while shutting down to make pager exits cleanly */
158 162 sa.sa_handler = SIG_IGN;
159 163 if (sigaction(SIGINT, &sa, NULL) < 0)
160 164 goto error;
161 165
162 166 peerpid = 0;
163 167 return;
164 168
165 169 error:
166 170 abortmsgerrno("failed to restore signal handlers");
167 171 }
168 172
169 173 /* This implementation is based on hgext/pager.py (post 369741ef7253)
170 174 * Return 0 if pager is not started, or pid of the pager */
171 175 pid_t setuppager(const char *pagercmd)
172 176 {
173 177 assert(pagerpid == 0);
174 178 if (!pagercmd)
175 179 return 0;
176 180
177 181 int pipefds[2];
178 182 if (pipe(pipefds) < 0)
179 183 return 0;
180 184 pid_t pid = fork();
181 185 if (pid < 0)
182 186 goto error;
183 187 if (pid > 0) {
184 188 close(pipefds[0]);
185 189 if (dup2(pipefds[1], fileno(stdout)) < 0)
186 190 goto error;
187 191 if (isatty(fileno(stderr))) {
188 192 if (dup2(pipefds[1], fileno(stderr)) < 0)
189 193 goto error;
190 194 }
191 195 close(pipefds[1]);
192 196 pagerpid = pid;
193 197 return pid;
194 198 } else {
195 199 dup2(pipefds[0], fileno(stdin));
196 200 close(pipefds[0]);
197 201 close(pipefds[1]);
198 202
199 203 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
200 204 if (r < 0) {
201 205 abortmsgerrno("cannot start pager '%s'", pagercmd);
202 206 }
203 207 return 0;
204 208 }
205 209
206 210 error:
207 211 close(pipefds[0]);
208 212 close(pipefds[1]);
209 213 abortmsgerrno("failed to prepare pager");
210 214 return 0;
211 215 }
212 216
213 217 void waitpager(void)
214 218 {
215 219 if (pagerpid == 0)
216 220 return;
217 221
218 222 /* close output streams to notify the pager its input ends */
219 223 fclose(stdout);
220 224 fclose(stderr);
221 225 while (1) {
222 226 pid_t ret = waitpid(pagerpid, NULL, 0);
223 227 if (ret == -1 && errno == EINTR)
224 228 continue;
225 229 break;
226 230 }
227 231 }
General Comments 0
You need to be logged in to leave comments. Login now