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 }