Mercurial > projects > pwm
comparison io.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 | ec01c579024a |
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 <stdarg.h> | |
33 #include <stdio.h> | |
34 #include <stdio.h> | |
35 #include <string.h> | |
36 #include <unistd.h> | |
37 | |
38 #include "io.h" | |
39 #include "util.h" | |
40 #include "pwm.h" | |
41 | |
42 static sigjmp_buf signal_env; | |
43 | |
44 static void | |
45 signal_handler(int signal_no) | |
46 { | |
47 siglongjmp(signal_env, signal_no); | |
48 } | |
49 | |
50 int | |
51 io_gl_complete_nothing(WordCompletion *cpl, void *data, const char *line, | |
52 int word_end) | |
53 { | |
54 return (0); | |
55 } | |
56 | |
57 enum io_status | |
58 io_get_char(const char *prompt, int *cp) | |
59 { | |
60 enum io_status retval = IO_OK; | |
61 GetLine *gl = NULL; | |
62 | |
63 gl = new_GetLine(16, 0); | |
64 if (gl== NULL) { | |
65 err(1, "new_GetLine"); | |
66 } | |
67 gl_catch_blocked(gl); | |
68 | |
69 /* prompt with echo off */ | |
70 gl_echo_mode(gl, 0); | |
71 if ((*cp = gl_query_char(gl, prompt, '\0')) == EOF) { | |
72 switch (gl_return_status(gl)) { | |
73 case GLR_SIGNAL: | |
74 retval = IO_SIGNAL; | |
75 break; | |
76 case GLR_ERROR: | |
77 errx(1, "gl_get_line: %s", | |
78 gl_error_message(gl, NULL, 0)); | |
79 default: | |
80 errx(1, "unknown error in gl_get_line"); | |
81 } | |
82 } | |
83 | |
84 /* erase prompt */ | |
85 if (io_printf("\r%*s\r", (int)strlen(prompt), "") == IO_SIGNAL) { | |
86 retval = IO_SIGNAL; | |
87 } | |
88 | |
89 del_GetLine(gl); | |
90 | |
91 return (retval); | |
92 } | |
93 | |
94 enum io_status | |
95 io_get_line(GetLine *gl, const char *prompt, int with_history, | |
96 const char *start_line, int start_pos, size_t buf_size, char *buf) | |
97 { | |
98 enum io_status retval = IO_OK; | |
99 GetLine *gl_private = NULL; | |
100 GlHistoryState state; | |
101 char *line; | |
102 | |
103 if (gl == NULL) { | |
104 gl = gl_private = new_GetLine(buf_size - 1, 0); | |
105 if (gl_private == NULL) { | |
106 err(1, "new_GetLine"); | |
107 } | |
108 gl_catch_blocked(gl_private); | |
109 } | |
110 | |
111 gl_state_of_history(gl, &state); | |
112 gl_toggle_history(gl, with_history); | |
113 | |
114 line = gl_get_line(gl, prompt, start_line, start_pos); | |
115 if (line == NULL) { | |
116 switch (gl_return_status(gl)) { | |
117 case GLR_BLOCKED: | |
118 break; | |
119 case GLR_SIGNAL: | |
120 retval = IO_SIGNAL; | |
121 goto out; | |
122 case GLR_EOF: | |
123 retval = IO_EOF; | |
124 goto out; | |
125 case GLR_ERROR: | |
126 errx(1, "gl_get_line: %s", | |
127 gl_error_message(gl, NULL, 0)); | |
128 default: | |
129 errx(1, "unknown error in gl_get_line"); | |
130 } | |
131 } | |
132 | |
133 if (snprintf(buf, buf_size, "%s", line) >= (int)buf_size) { | |
134 retval = IO_TRUNCATED; | |
135 } | |
136 | |
137 out: | |
138 if (gl != NULL) { | |
139 gl_toggle_history(gl, state.enabled); | |
140 } | |
141 del_GetLine(gl_private); | |
142 | |
143 return (retval); | |
144 } | |
145 | |
146 enum io_status | |
147 io_get_password(const char *prompt, const char *confirm_prompt, | |
148 size_t buf_size, char *buf) | |
149 { | |
150 enum io_status retval = IO_OK; | |
151 GetLine *gl = NULL; | |
152 size_t len; | |
153 char *password_buf = NULL; | |
154 char *confirm_buf = NULL; | |
155 | |
156 gl = new_GetLine(buf_size - 1, 0); | |
157 if (gl == NULL) { | |
158 err(1, "new_GetLine"); | |
159 } | |
160 /* disable default filename completion */ | |
161 gl_customize_completion(gl, NULL, io_gl_complete_nothing); | |
162 gl_echo_mode(gl, 0); | |
163 | |
164 password_buf = xmalloc(buf_size); | |
165 | |
166 if (io_get_line(gl, prompt, 0, NULL, 0, buf_size, password_buf) == | |
167 IO_SIGNAL) { | |
168 retval = IO_SIGNAL; | |
169 goto out; | |
170 } | |
171 len = strlen(password_buf); | |
172 /* strip trailing newline */ | |
173 if ((len > 0) && (password_buf[len - 1] == '\n')) { | |
174 password_buf[--len] = '\0'; | |
175 } | |
176 if (len == 0) { | |
177 retval = IO_PASSWORD_EMPTY; | |
178 goto out; | |
179 } | |
180 | |
181 if (confirm_prompt != NULL) { | |
182 if (io_printf("\n") == IO_SIGNAL) { | |
183 retval = IO_SIGNAL; | |
184 goto out; | |
185 } | |
186 | |
187 /* confirm new password */ | |
188 confirm_buf = xmalloc(buf_size); | |
189 if (io_get_line(gl, confirm_prompt, 0, NULL, 0, | |
190 buf_size, confirm_buf) == IO_SIGNAL) { | |
191 retval = IO_SIGNAL; | |
192 goto out; | |
193 } | |
194 len = strlen(confirm_buf); | |
195 /* strip trailing newline */ | |
196 if ((len > 0) && (confirm_buf[len - 1] == '\n')) { | |
197 confirm_buf[--len] = '\0'; | |
198 } | |
199 if (strcmp(password_buf, confirm_buf) != 0) { | |
200 retval = IO_PASSWORD_MISMATCH; | |
201 goto out; | |
202 } | |
203 } | |
204 | |
205 strcpy(buf, password_buf); | |
206 | |
207 out: | |
208 if (io_printf("\n") == IO_SIGNAL) { | |
209 retval = IO_SIGNAL; | |
210 goto out; | |
211 } | |
212 free(password_buf); | |
213 free(confirm_buf); | |
214 del_GetLine(gl); | |
215 | |
216 return (retval); | |
217 } | |
218 | |
219 enum io_status | |
220 io_dputs(int fd, const char *s) | |
221 { | |
222 struct sigaction action; | |
223 struct sigaction oaction; | |
224 int signal_no = 0; | |
225 const char *p = s; | |
226 size_t remaining; | |
227 ssize_t n; | |
228 | |
229 /* install signal handlers */ | |
230 action.sa_handler = signal_handler; | |
231 action.sa_flags = 0; | |
232 sigemptyset(&action.sa_mask); | |
233 sigaddset(&action.sa_mask, SIGINT); | |
234 sigaddset(&action.sa_mask, SIGTERM); | |
235 sigaddset(&action.sa_mask, SIGHUP); | |
236 sigaddset(&action.sa_mask, SIGQUIT); | |
237 if ((sigaction(SIGINT, &action, &oaction) != 0) || | |
238 (sigaction(SIGTERM, &action, &oaction) != 0) || | |
239 (sigaction(SIGHUP, &action, &oaction) != 0) || | |
240 (sigaction(SIGQUIT, &action, &oaction) != 0)) { | |
241 err(1, "sigaction"); | |
242 } | |
243 | |
244 if ((signal_no = sigsetjmp(signal_env, 1)) != 0) { | |
245 /* signal received, signal mask has been restored */ | |
246 goto out; | |
247 } | |
248 | |
249 remaining = strlen(s); | |
250 while (remaining > 0) { | |
251 pwm_unblock_signals(); | |
252 n = write(fd, p, remaining); | |
253 if ((n < (int)remaining) && (errno != EINTR)) { | |
254 err(1, "write"); | |
255 } | |
256 pwm_block_signals(); | |
257 remaining -= MAX(n, 0); | |
258 p += MAX(n, 0); | |
259 } | |
260 | |
261 out: | |
262 /* restore signal handlers */ | |
263 if ((sigaction(SIGINT, &oaction, NULL) != 0) || | |
264 (sigaction(SIGTERM, &oaction, NULL) != 0) || | |
265 (sigaction(SIGHUP, &oaction, NULL) != 0) || | |
266 (sigaction(SIGQUIT, &oaction, NULL) != 0)) { | |
267 err(1, "sigaction"); | |
268 } | |
269 | |
270 return ((signal_no == 0) ? IO_OK : IO_SIGNAL); | |
271 } | |
272 | |
273 enum io_status | |
274 io_vdprintf(int fd, const char *fmt, va_list args) | |
275 { | |
276 enum io_status retval; | |
277 char *buf; | |
278 | |
279 xvasprintf(&buf, fmt, args); | |
280 retval = io_dputs(fd, buf); | |
281 free(buf); | |
282 | |
283 return (retval); | |
284 } | |
285 | |
286 enum io_status | |
287 io_dprintf(int fd, const char *fmt, ...) | |
288 { | |
289 enum io_status retval; | |
290 va_list args; | |
291 | |
292 va_start(args, fmt); | |
293 retval = io_vdprintf(fd, fmt, args); | |
294 va_end(args); | |
295 | |
296 return (retval); | |
297 } | |
298 | |
299 enum io_status | |
300 io_printf(const char *fmt, ...) | |
301 { | |
302 enum io_status retval; | |
303 va_list args; | |
304 | |
305 va_start(args, fmt); | |
306 retval = io_vdprintf(STDOUT_FILENO, fmt, args); | |
307 va_end(args); | |
308 | |
309 return (retval); | |
310 } |