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