Mercurial > projects > pwm
comparison proc.c @ 19:5c6155c8e9b6
Handle signals
Handled signals are generally blocked and only unblocked when doing blocking
I/O, i.e. either when reading commands or printing results. A (possibly
queued) signal will then interrupt I/O and can be dealt with in the main loop.
author | Guido Berhoerster <guido+pwm@berhoerster.name> |
---|---|
date | Fri, 01 Sep 2017 22:33:41 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
18:1e39a251cbe9 | 19:5c6155c8e9b6 |
---|---|
1 /* | |
2 * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name> | |
3 * | |
4 * Permission is hereby granted, free of charge, to any person obtaining | |
5 * a copy of this software and associated documentation files (the | |
6 * "Software"), to deal in the Software without restriction, including | |
7 * without limitation the rights to use, copy, modify, merge, publish, | |
8 * distribute, sublicense, and/or sell copies of the Software, and to | |
9 * permit persons to whom the Software is furnished to do so, subject to | |
10 * the following conditions: | |
11 * | |
12 * The above copyright notice and this permission notice shall be included | |
13 * in all copies or substantial portions of the Software. | |
14 * | |
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
22 */ | |
23 | |
24 #include "compat.h" | |
25 | |
26 #ifdef HAVE_ERR_H | |
27 #include <err.h> | |
28 #endif /* HAVE_ERR_H */ | |
29 #include <errno.h> | |
30 #include <setjmp.h> | |
31 #include <signal.h> | |
32 #include <string.h> | |
33 #include <sys/wait.h> | |
34 #include <unistd.h> | |
35 | |
36 #include "util.h" | |
37 #include "proc.h" | |
38 #include "pwm.h" | |
39 | |
40 #define PIPE_R 0 | |
41 #define PIPE_W 1 | |
42 | |
43 static sigjmp_buf signal_env; | |
44 | |
45 static void | |
46 signal_handler(int signal_no) | |
47 { | |
48 siglongjmp(signal_env, signal_no); | |
49 } | |
50 | |
51 enum io_status | |
52 proc_open(struct proc *proc, const char *command, const char *type) | |
53 { | |
54 int pipe_fds[2]; | |
55 pid_t pid; | |
56 | |
57 if ((strlen(type) != 1) || ((*type != 'r') && (*type != 'w'))) { | |
58 return (IO_ERROR); | |
59 } | |
60 | |
61 if (pipe(pipe_fds) < 0) { | |
62 return (IO_ERROR); | |
63 } | |
64 | |
65 switch (pid = fork()) { | |
66 case -1: | |
67 return (IO_ERROR); | |
68 case 0: | |
69 if (*type == 'r') { | |
70 close(pipe_fds[PIPE_R]); | |
71 dup2(pipe_fds[PIPE_W], STDOUT_FILENO); | |
72 } else { | |
73 close(pipe_fds[PIPE_W]); | |
74 dup2(pipe_fds[PIPE_R], STDIN_FILENO); | |
75 } | |
76 closefrom(STDERR_FILENO + 1); | |
77 execlp("sh", "sh", "-c", command, (char *)0); | |
78 err(1, "execlp"); | |
79 default: | |
80 if (*type == 'r') { | |
81 close(pipe_fds[PIPE_W]); | |
82 proc->fd = pipe_fds[PIPE_R]; | |
83 } else { | |
84 close(pipe_fds[PIPE_R]); | |
85 proc->fd = pipe_fds[PIPE_W]; | |
86 } | |
87 proc->pid = pid; | |
88 return (IO_OK); | |
89 } | |
90 } | |
91 | |
92 enum io_status | |
93 proc_close(struct proc *proc) | |
94 { | |
95 struct sigaction action; | |
96 struct sigaction oaction; | |
97 int signal_no = 0; | |
98 pid_t wpid; | |
99 int status; | |
100 | |
101 close(proc->fd); | |
102 | |
103 /* install signal handlers */ | |
104 action.sa_handler = signal_handler; | |
105 action.sa_flags = 0; | |
106 sigemptyset(&action.sa_mask); | |
107 sigaddset(&action.sa_mask, SIGINT); | |
108 sigaddset(&action.sa_mask, SIGTERM); | |
109 sigaddset(&action.sa_mask, SIGHUP); | |
110 sigaddset(&action.sa_mask, SIGQUIT); | |
111 if ((sigaction(SIGINT, &action, &oaction) != 0) || | |
112 (sigaction(SIGTERM, &action, &oaction) != 0) || | |
113 (sigaction(SIGHUP, &action, &oaction) != 0) || | |
114 (sigaction(SIGQUIT, &action, &oaction) != 0)) { | |
115 err(1, "sigaction"); | |
116 } | |
117 | |
118 if ((signal_no = sigsetjmp(signal_env, 1)) != 0) { | |
119 /* | |
120 * signal received, signal mask has been restored, send SIGTERM | |
121 * to the child process, wait 500 ms and send a SIGKILL if the | |
122 * child still exists | |
123 */ | |
124 kill(proc->pid, SIGTERM); | |
125 nanosleep(&(struct timespec){ .tv_nsec = 500 * 1000 * 1000 }, | |
126 NULL); | |
127 do { | |
128 wpid = waitpid(proc->pid, &status, WNOHANG); | |
129 } while ((wpid == -1) && (errno == EINTR)); | |
130 if (wpid == proc->pid) { | |
131 goto out; | |
132 } | |
133 | |
134 kill(proc->pid, SIGKILL); | |
135 do { | |
136 wpid = waitpid(proc->pid, &status, 0); | |
137 } while ((wpid == -1) && (errno == EINTR)); | |
138 | |
139 goto out; | |
140 } | |
141 | |
142 pwm_unblock_signals(); | |
143 do { | |
144 wpid = waitpid(proc->pid, &status, 0); | |
145 } while ((wpid == -1) && (errno == EINTR)); | |
146 pwm_block_signals(); | |
147 | |
148 out: | |
149 /* restore signal handlers */ | |
150 if ((sigaction(SIGINT, &oaction, NULL) != 0) || | |
151 (sigaction(SIGTERM, &oaction, NULL) != 0) || | |
152 (sigaction(SIGHUP, &oaction, NULL) != 0) || | |
153 (sigaction(SIGQUIT, &oaction, NULL) != 0)) { | |
154 err(1, "sigaction"); | |
155 } | |
156 | |
157 proc->fd = -1; | |
158 proc->pid = (pid_t)-1; | |
159 | |
160 return ((signal_no == 0) ? IO_OK : IO_SIGNAL); | |
161 } |