]>
Commit | Line | Data |
---|---|---|
94120488 ER |
1 | # from Fedora |
2 | # fix signal handling (#1073698) | |
3 | # submitted to upstream (Oliver Rauch) via email, 2014-07-03 | |
4 | ||
12d07cfb AM |
5 | From 3b5d3b7e1f320b0bfbe48024a586c0a22375aa2d Mon Sep 17 00:00:00 2001 |
6 | From: Nils Philippsen <nils@redhat.com> | |
7 | Date: Thu, 3 Jul 2014 10:38:03 +0200 | |
8 | Subject: [PATCH] patch: signal-handling | |
9 | ||
10 | Squashed commit of the following: | |
11 | ||
12 | commit 1e9e8cf5edc469114c8eadf46817cd5c1261b35c | |
13 | Author: Nils Philippsen <nils@redhat.com> | |
14 | Date: Thu Jul 3 10:14:52 2014 +0200 | |
15 | ||
16 | don't use g_unix_open_pipe(), g_unix_fd_add() | |
17 | ||
18 | These functions have only recently been added to glib. Use pipe()/ | |
19 | fcntl() and g_io_channel_unix_new()/g_io_add_watch() instead which are | |
20 | available in the minimum glib version needed for gtk+-2.x. | |
21 | ||
22 | commit acbdf3f693d3d2a78ee7490ca1bf76957daf00cf | |
23 | Author: Nils Philippsen <nils@redhat.com> | |
24 | Date: Thu Mar 13 13:38:12 2014 +0100 | |
25 | ||
26 | separate signal handlers in top and bottom half | |
27 | ||
28 | This is to avoid race-conditions occurring when a signal is received | |
29 | while the signal handler is not yet finished. It also avoids calling | |
30 | non-reentrant functions from a signal handler. The top half (the real | |
31 | signal handler) just writes a character into a pipe which gets picked up | |
32 | and serviced by the bottom half from the normal event loop, this | |
33 | serializes things and makes using non-reentrant functions safe. | |
34 | --- | |
35 | src/xsane.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ | |
36 | 1 file changed, 136 insertions(+), 15 deletions(-) | |
37 | ||
38 | diff --git a/src/xsane.c b/src/xsane.c | |
39 | index 2b9211b..fc2ebbe 100644 | |
40 | --- a/src/xsane.c | |
41 | +++ b/src/xsane.c | |
42 | @@ -47,6 +47,7 @@ | |
43 | #endif | |
44 | ||
45 | #include <sys/wait.h> | |
46 | +#include <glib-unix.h> | |
47 | ||
48 | #include <stdarg.h> | |
49 | ||
50 | @@ -121,6 +122,7 @@ static const Preferences_medium_t pref_default_medium[]= | |
51 | ||
52 | int DBG_LEVEL = 0; | |
53 | static guint xsane_resolution_timer = 0; | |
54 | +static int xsane_signal_pipe[2]; | |
55 | ||
56 | /* ---------------------------------------------------------------------------------------------------------------------- */ | |
57 | ||
58 | @@ -161,8 +163,11 @@ void xsane_pref_save(void); | |
59 | static int xsane_pref_restore(void); | |
60 | static void xsane_pref_save_media(void); | |
61 | static void xsane_pref_restore_media(void); | |
62 | -static RETSIGTYPE xsane_quit_handler(int signal); | |
63 | -static RETSIGTYPE xsane_sigchld_handler(int signal); | |
64 | +static RETSIGTYPE xsane_signal_handler_top_half(int signal); | |
65 | +static gboolean xsane_signal_handler_bottom_half(GIOChannel *source, | |
66 | + GIOCondition condition, | |
67 | + gpointer user_data); | |
68 | +static void xsane_sigchld_handler(void); | |
69 | static void xsane_quit(void); | |
70 | static void xsane_exit(void); | |
71 | static gint xsane_standard_option_win_delete(GtkWidget *widget, gpointer data); | |
72 | @@ -2296,16 +2301,119 @@ static void xsane_pref_restore_media(void) | |
73 | ||
74 | /* ---------------------------------------------------------------------------------------------------------------------- */ | |
75 | ||
76 | -static RETSIGTYPE xsane_quit_handler(int signal) | |
77 | +static RETSIGTYPE xsane_signal_handler_top_half(int signal) | |
78 | { | |
79 | - DBG(DBG_proc, "xsane_quit_handler\n"); | |
80 | + const char *msg_func = "xsane_signal_handler_top_half(): "; | |
81 | + const char *msg_short_write = "Short write() while processing signal.\n"; | |
82 | + const char *msg_err = "Error during write().\n"; | |
83 | + char sig_char; | |
84 | + ssize_t written; | |
85 | + int errno_saved = errno; | |
86 | ||
87 | - xsane_quit(); | |
88 | + switch (signal) | |
89 | + { | |
90 | + case SIGTERM: | |
91 | + sig_char = 't'; | |
92 | + break; | |
93 | + case SIGINT: | |
94 | + sig_char = 'i'; | |
95 | + break; | |
96 | + case SIGHUP: | |
97 | + sig_char = 'h'; | |
98 | + break; | |
99 | + case SIGCHLD: | |
100 | + sig_char = 'c'; | |
101 | + break; | |
102 | + default: | |
103 | + sig_char = '?'; | |
104 | + break; | |
105 | + } | |
106 | + | |
107 | + if ((written = write(xsane_signal_pipe[1], &sig_char, 1)) <= 0) | |
108 | + { | |
109 | + /* At this point, all bets are off. Salvage what we can. */ | |
110 | + | |
111 | + const char *msg = (written == 0) ? msg_short_write : msg_err; | |
112 | + | |
113 | + if ((write(STDERR_FILENO, msg_func, strlen(msg_func)) < 0) || | |
114 | + (write(STDERR_FILENO, msg, strlen(msg)) < 0)) | |
115 | + { | |
116 | + /* This is really a no-op, but at this point it doesn't really matter | |
117 | + * anymore if the writes succeeded or not. */ | |
118 | + goto bail_out; | |
119 | + } | |
120 | + | |
121 | +bail_out: | |
122 | + /* Ignore SIGCHLD errors, zombie processes don't hurt that much. */ | |
123 | + if (signal != SIGCHLD) | |
124 | + { | |
125 | + struct SIGACTION act; | |
126 | + memset(&act, 0, sizeof(act)); | |
127 | + act.sa_handler = SIG_DFL; | |
128 | + sigaction(signal, &act, NULL); | |
129 | + raise(signal); | |
130 | + } | |
131 | + } | |
132 | + | |
133 | + errno = errno_saved; | |
134 | +} | |
135 | + | |
136 | +static gboolean xsane_signal_handler_bottom_half(GIOChannel *source, | |
137 | + GIOCondition condition, | |
138 | + gpointer user_data) | |
139 | +{ | |
140 | + char sig_char; | |
141 | + ssize_t readlen; | |
142 | + | |
143 | + DBG(DBG_proc, "xsane_signal_handler_bottom_half\n"); | |
144 | + | |
145 | + while ((readlen = read(xsane_signal_pipe[0], &sig_char, 1)) != 0) | |
146 | + { | |
147 | + if (readlen < 0) | |
148 | + { | |
149 | + if (errno == EINTR) | |
150 | + { | |
151 | + /* if interrupted by signal, just repeat reading */ | |
152 | + continue; | |
153 | + } | |
154 | + else | |
155 | + { | |
156 | + break; | |
157 | + } | |
158 | + } | |
159 | + | |
160 | + switch (sig_char) | |
161 | + { | |
162 | + case 't': | |
163 | + case 'i': | |
164 | + case 'h': | |
165 | + xsane_quit(); | |
166 | + break; | |
167 | + case 'c': | |
168 | + xsane_sigchld_handler(); | |
169 | + break; | |
170 | + default: | |
171 | + DBG(DBG_error, | |
172 | + "Don't know how to cope with character-encoded signal: '%c'\n", | |
173 | + sig_char); | |
174 | + break; | |
175 | + } | |
176 | + } | |
177 | + | |
178 | + /* previous invocation might have read more than it should, so ignore | |
179 | + * EAGAIN/EWOULDBLOCK */ | |
180 | + if (readlen < 0 && errno != EAGAIN && errno != EWOULDBLOCK) | |
181 | + { | |
182 | + DBG(DBG_error, "Error while reading from pipe: %d '%s'\n", errno, | |
183 | + strerror(errno)); | |
184 | + } | |
185 | + | |
186 | + return TRUE; | |
187 | } | |
188 | ||
189 | /* ---------------------------------------------------------------------------------------------------------------------- */ | |
190 | ||
191 | -static RETSIGTYPE xsane_sigchld_handler(int signal) | |
192 | +static void xsane_sigchld_handler(void) | |
193 | { | |
194 | int status; | |
195 | XsaneChildprocess **childprocess_listptr = &xsane.childprocess_list; | |
196 | @@ -6026,6 +6134,8 @@ void xsane_interface(int argc, char **argv) | |
197 | { | |
198 | struct SIGACTION act; | |
199 | ||
200 | + GIOChannel *gio_pipe_read; | |
201 | + | |
202 | DBG(DBG_proc, "xsane_interface\n"); | |
203 | ||
204 | xsane.info_label = NULL; | |
205 | @@ -6069,18 +6179,29 @@ void xsane_interface(int argc, char **argv) | |
206 | } | |
207 | } | |
208 | ||
209 | + if ((pipe(xsane_signal_pipe) == -1) || | |
210 | + (fcntl(xsane_signal_pipe[0], F_SETFD, FD_CLOEXEC) == -1) || | |
211 | + (fcntl(xsane_signal_pipe[0], F_SETFL, O_NONBLOCK) == -1) || | |
212 | + (fcntl(xsane_signal_pipe[1], F_SETFD, FD_CLOEXEC) == -1) || | |
213 | + (fcntl(xsane_signal_pipe[1], F_SETFL, O_NONBLOCK) == -1) || | |
214 | + !(gio_pipe_read = g_io_channel_unix_new(xsane_signal_pipe[0])) || | |
215 | + !g_io_add_watch(gio_pipe_read, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI, | |
216 | + xsane_signal_handler_bottom_half, NULL)) | |
217 | + { | |
218 | + DBG(DBG_error, | |
219 | + "Couldn't create signal handling pipe, set flags on it or install\n" | |
220 | + "bottom half of handler.\n"); | |
221 | + exit(1); | |
222 | + } | |
223 | + | |
224 | /* define SIGTERM, SIGINT, SIGHUP-handler to make sure that e.g. all temporary files are deleted */ | |
225 | /* when xsane gets such a signal */ | |
226 | memset(&act, 0, sizeof(act)); | |
227 | - act.sa_handler = xsane_quit_handler; | |
228 | - sigaction(SIGTERM, &act, 0); | |
229 | - sigaction(SIGINT, &act, 0); | |
230 | - sigaction(SIGHUP, &act, 0); | |
231 | - | |
232 | - /* add a signal handler that cleans up zombie child processes */ | |
233 | - memset(&act, 0, sizeof(act)); | |
234 | - act.sa_handler = xsane_sigchld_handler; | |
235 | - sigaction(SIGCHLD, &act, 0); | |
236 | + act.sa_handler = xsane_signal_handler_top_half; | |
237 | + sigaction(SIGTERM, &act, NULL); | |
238 | + sigaction(SIGINT, &act, NULL); | |
239 | + sigaction(SIGHUP, &act, NULL); | |
240 | + sigaction(SIGCHLD, &act, NULL); | |
241 | ||
242 | gtk_main(); | |
243 | sane_exit(); | |
244 | -- | |
245 | 1.9.3 | |
246 |