##// END OF EJS Templates
rust-chg: remove SIGCHLD handler which won't work in oxidized chg...
Yuya Nishihara -
r40155:e70b616a default
parent child Browse files
Show More
@@ -1,177 +1,151
1 1 /*
2 2 * Signal handlers for cHg
3 3 *
4 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.
8 8 */
9 9
10 10 #include <assert.h>
11 11 #include <errno.h>
12 12 #include <signal.h>
13 13 #include <string.h>
14 #include <sys/wait.h>
15 14 #include <unistd.h>
16 15
17 #ifdef __GNUC__
18 #define UNUSED_ __attribute__((unused))
19 #else
20 #define UNUSED_
21 #endif
22
23 static pid_t pagerpid = 0;
24 16 static pid_t peerpgid = 0;
25 17 static pid_t peerpid = 0;
26 18
27 19 static void forwardsignal(int sig)
28 20 {
29 21 assert(peerpid > 0);
30 22 (void)kill(peerpid, sig);
31 23 }
32 24
33 25 static void forwardsignaltogroup(int sig)
34 26 {
35 27 /* prefer kill(-pgid, sig), fallback to pid if pgid is invalid */
36 28 pid_t killpid = peerpgid > 1 ? -peerpgid : peerpid;
37 29 (void)kill(killpid, sig);
38 30 }
39 31
40 32 static void handlestopsignal(int sig)
41 33 {
42 34 sigset_t unblockset, oldset;
43 35 struct sigaction sa, oldsa;
44 36 if (sigemptyset(&unblockset) < 0)
45 37 return;
46 38 if (sigaddset(&unblockset, sig) < 0)
47 39 return;
48 40 memset(&sa, 0, sizeof(sa));
49 41 sa.sa_handler = SIG_DFL;
50 42 sa.sa_flags = SA_RESTART;
51 43 if (sigemptyset(&sa.sa_mask) < 0)
52 44 return;
53 45
54 46 forwardsignal(sig);
55 47 if (raise(sig) < 0) /* resend to self */
56 48 return;
57 49 if (sigaction(sig, &sa, &oldsa) < 0)
58 50 return;
59 51 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
60 52 return;
61 53 /* resent signal will be handled before sigprocmask() returns */
62 54 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
63 55 return;
64 56 if (sigaction(sig, &oldsa, NULL) < 0)
65 57 return;
66 58 }
67 59
68 static void handlechildsignal(int sig UNUSED_)
69 {
70 if (peerpid == 0 || pagerpid == 0)
71 return;
72 /* if pager exits, notify the server with SIGPIPE immediately.
73 * otherwise the server won't get SIGPIPE if it does not write
74 * anything. (issue5278) */
75 if (waitpid(pagerpid, NULL, WNOHANG) == pagerpid)
76 kill(peerpid, SIGPIPE);
77 }
78
79 60 /*
80 61 * Installs signal handlers.
81 62 *
82 63 * Returns 0 on success, -1 on error and errno is set appropriately.
83 64 * Installed handlers wouldn't be cleaned up on error.
84 65 */
85 66 int setupsignalhandler(pid_t pid, pid_t pgid)
86 67 {
87 68 if (pid <= 0) {
88 69 errno = EINVAL;
89 70 return -1;
90 71 }
91 72 peerpid = pid;
92 73 peerpgid = (pgid <= 1 ? 0 : pgid);
93 74
94 75 struct sigaction sa;
95 76 memset(&sa, 0, sizeof(sa));
96 77
97 78 /* deadly signals meant to be sent to a process group:
98 79 * - SIGHUP: usually generated by the kernel, when termination of a
99 80 * process causes that process group to become orphaned
100 81 * - SIGINT: usually generated by the terminal */
101 82 sa.sa_handler = forwardsignaltogroup;
102 83 sa.sa_flags = SA_RESTART;
103 84 if (sigemptyset(&sa.sa_mask) < 0)
104 85 return -1;
105 86 if (sigaction(SIGHUP, &sa, NULL) < 0)
106 87 return -1;
107 88 if (sigaction(SIGINT, &sa, NULL) < 0)
108 89 return -1;
109 90
110 91 /* terminate frontend by double SIGTERM in case of server freeze */
111 92 sa.sa_handler = forwardsignal;
112 93 sa.sa_flags |= SA_RESETHAND;
113 94 if (sigaction(SIGTERM, &sa, NULL) < 0)
114 95 return -1;
115 96
116 97 /* notify the worker about window resize events */
117 98 sa.sa_flags = SA_RESTART;
118 99 if (sigaction(SIGWINCH, &sa, NULL) < 0)
119 100 return -1;
120 101 /* forward user-defined signals */
121 102 if (sigaction(SIGUSR1, &sa, NULL) < 0)
122 103 return -1;
123 104 if (sigaction(SIGUSR2, &sa, NULL) < 0)
124 105 return -1;
125 106 /* propagate job control requests to worker */
126 107 sa.sa_handler = forwardsignal;
127 108 sa.sa_flags = SA_RESTART;
128 109 if (sigaction(SIGCONT, &sa, NULL) < 0)
129 110 return -1;
130 111 sa.sa_handler = handlestopsignal;
131 112 sa.sa_flags = SA_RESTART;
132 113 if (sigaction(SIGTSTP, &sa, NULL) < 0)
133 114 return -1;
134 /* get notified when pager exits */
135 sa.sa_handler = handlechildsignal;
136 sa.sa_flags = SA_RESTART;
137 if (sigaction(SIGCHLD, &sa, NULL) < 0)
138 return -1;
139 115
140 116 return 0;
141 117 }
142 118
143 119 /*
144 120 * Restores signal handlers to the default, and masks SIGINT.
145 121 *
146 122 * Returns 0 on success, -1 on error and errno is set appropriately.
147 123 */
148 124 int restoresignalhandler(void)
149 125 {
150 126 struct sigaction sa;
151 127 memset(&sa, 0, sizeof(sa));
152 128 sa.sa_handler = SIG_DFL;
153 129 sa.sa_flags = SA_RESTART;
154 130 if (sigemptyset(&sa.sa_mask) < 0)
155 131 return -1;
156 132
157 133 if (sigaction(SIGHUP, &sa, NULL) < 0)
158 134 return -1;
159 135 if (sigaction(SIGTERM, &sa, NULL) < 0)
160 136 return -1;
161 137 if (sigaction(SIGWINCH, &sa, NULL) < 0)
162 138 return -1;
163 139 if (sigaction(SIGCONT, &sa, NULL) < 0)
164 140 return -1;
165 141 if (sigaction(SIGTSTP, &sa, NULL) < 0)
166 142 return -1;
167 if (sigaction(SIGCHLD, &sa, NULL) < 0)
168 return -1;
169 143
170 144 /* ignore Ctrl+C while shutting down to make pager exits cleanly */
171 145 sa.sa_handler = SIG_IGN;
172 146 if (sigaction(SIGINT, &sa, NULL) < 0)
173 147 return -1;
174 148
175 149 peerpid = 0;
176 150 return 0;
177 151 }
@@ -1,83 +1,87
1 1 // Copyright 2018 Yuya Nishihara <yuya@tcha.org>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5
6 6 use futures::Future;
7 7 use futures::future::IntoFuture;
8 8 use std::io;
9 9 use std::os::unix::io::AsRawFd;
10 10 use std::os::unix::process::ExitStatusExt;
11 11 use std::process::{Command, Stdio};
12 12 use tokio;
13 13 use tokio_process::{ChildStdin, CommandExt};
14 14
15 15 use super::message::CommandSpec;
16 16 use super::procutil;
17 17
18 18 /// Callback to process shell command requests received from server.
19 19 pub trait SystemHandler: Sized {
20 20 type PagerStdin: AsRawFd;
21 21 type SpawnPagerResult: IntoFuture<Item = (Self, Self::PagerStdin), Error = io::Error>;
22 22 type RunSystemResult: IntoFuture<Item = (Self, i32), Error = io::Error>;
23 23
24 24 /// Handles pager command request.
25 25 ///
26 26 /// Returns the pipe to be attached to the server if the pager is spawned.
27 27 fn spawn_pager(self, spec: CommandSpec) -> Self::SpawnPagerResult;
28 28
29 29 /// Handles system command request.
30 30 ///
31 31 /// Returns command exit code (positive) or signal number (negative).
32 32 fn run_system(self, spec: CommandSpec) -> Self::RunSystemResult;
33 33 }
34 34
35 35 /// Default cHg implementation to process requests received from server.
36 36 pub struct ChgUiHandler {
37 37 }
38 38
39 39 impl ChgUiHandler {
40 40 pub fn new() -> ChgUiHandler {
41 41 ChgUiHandler {}
42 42 }
43 43 }
44 44
45 45 impl SystemHandler for ChgUiHandler {
46 46 type PagerStdin = ChildStdin;
47 47 type SpawnPagerResult = io::Result<(Self, Self::PagerStdin)>;
48 48 type RunSystemResult = Box<dyn Future<Item = (Self, i32), Error = io::Error> + Send>;
49 49
50 50 fn spawn_pager(self, spec: CommandSpec) -> Self::SpawnPagerResult {
51 51 let mut pager = new_shell_command(&spec)
52 52 .stdin(Stdio::piped())
53 53 .spawn_async()?;
54 54 let pin = pager.stdin().take().unwrap();
55 55 procutil::set_blocking_fd(pin.as_raw_fd())?;
56 // TODO: if pager exits, notify the server with SIGPIPE immediately.
57 // otherwise the server won't get SIGPIPE if it does not write
58 // anything. (issue5278)
59 // kill(peerpid, SIGPIPE);
56 60 tokio::spawn(pager.map(|_| ()).map_err(|_| ())); // just ignore errors
57 61 Ok((self, pin))
58 62 }
59 63
60 64 fn run_system(self, spec: CommandSpec) -> Self::RunSystemResult {
61 65 let fut = new_shell_command(&spec)
62 66 .spawn_async()
63 67 .into_future()
64 68 .flatten()
65 69 .map(|status| {
66 70 let code = status.code().or_else(|| status.signal().map(|n| -n))
67 71 .expect("either exit code or signal should be set");
68 72 (self, code)
69 73 });
70 74 Box::new(fut)
71 75 }
72 76 }
73 77
74 78 fn new_shell_command(spec: &CommandSpec) -> Command {
75 79 let mut builder = Command::new("/bin/sh");
76 80 builder
77 81 .arg("-c")
78 82 .arg(&spec.command)
79 83 .current_dir(&spec.current_dir)
80 84 .env_clear()
81 85 .envs(spec.envs.iter().cloned());
82 86 builder
83 87 }
General Comments 0
You need to be logged in to leave comments. Login now