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