1 /*
2 * Copyright (C) 2011 Red Hat, Inc.
3 *
4 * This work is provided "as is"; redistribution and modification
5 * in whole or in part, in any medium, physical or electronic is
6 * permitted without restriction.
7 *
8 * This work is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * In no event shall the authors or contributors be liable for any
13 * direct, indirect, incidental, special, exemplary, or consequential
14 * damages (including, but not limited to, procurement of substitute
15 * goods or services; loss of use, data, or profits; or business
16 * interruption) however caused and on any theory of liability, whether
17 * in contract, strict liability, or tort (including negligence or
18 * otherwise) arising in any way out of the use of this software, even
19 * if advised of the possibility of such damage.
20 *
21 * Author: Colin Walters <walters@verbum.org>
22 */
23
24 #include "config.h"
25
26 #include "glib-unix.h"
27 #include <string.h>
28
29 static void
test_pipe(void)30 test_pipe (void)
31 {
32 GError *error = NULL;
33 int pipefd[2];
34 char buf[1024];
35 gssize bytes_read;
36 gboolean res;
37
38 res = g_unix_open_pipe (pipefd, FD_CLOEXEC, &error);
39 g_assert (res);
40 g_assert_no_error (error);
41
42 write (pipefd[1], "hello", sizeof ("hello"));
43 memset (buf, 0, sizeof (buf));
44 bytes_read = read (pipefd[0], buf, sizeof(buf) - 1);
45 g_assert_cmpint (bytes_read, >, 0);
46 buf[bytes_read] = '\0';
47
48 close (pipefd[0]);
49 close (pipefd[1]);
50
51 g_assert (g_str_has_prefix (buf, "hello"));
52 }
53
54 static void
test_error(void)55 test_error (void)
56 {
57 GError *error = NULL;
58 gboolean res;
59
60 res = g_unix_set_fd_nonblocking (123456, TRUE, &error);
61 g_assert_cmpint (errno, ==, EBADF);
62 g_assert (!res);
63 g_assert_error (error, G_UNIX_ERROR, 0);
64 g_clear_error (&error);
65 }
66
67 static void
test_nonblocking(void)68 test_nonblocking (void)
69 {
70 GError *error = NULL;
71 int pipefd[2];
72 gboolean res;
73 int flags;
74
75 res = g_unix_open_pipe (pipefd, FD_CLOEXEC, &error);
76 g_assert (res);
77 g_assert_no_error (error);
78
79 res = g_unix_set_fd_nonblocking (pipefd[0], TRUE, &error);
80 g_assert (res);
81 g_assert_no_error (error);
82
83 flags = fcntl (pipefd[0], F_GETFL);
84 g_assert_cmpint (flags, !=, -1);
85 g_assert (flags & O_NONBLOCK);
86
87 res = g_unix_set_fd_nonblocking (pipefd[0], FALSE, &error);
88 g_assert (res);
89 g_assert_no_error (error);
90
91 flags = fcntl (pipefd[0], F_GETFL);
92 g_assert_cmpint (flags, !=, -1);
93 g_assert (!(flags & O_NONBLOCK));
94
95 close (pipefd[0]);
96 close (pipefd[1]);
97 }
98
99 static gboolean sig_received = FALSE;
100 static gboolean sig_timeout = FALSE;
101 static int sig_counter = 0;
102
103 static gboolean
on_sig_received(gpointer user_data)104 on_sig_received (gpointer user_data)
105 {
106 GMainLoop *loop = user_data;
107 g_main_loop_quit (loop);
108 sig_received = TRUE;
109 sig_counter ++;
110 return G_SOURCE_REMOVE;
111 }
112
113 static gboolean
on_sig_timeout(gpointer data)114 on_sig_timeout (gpointer data)
115 {
116 GMainLoop *loop = data;
117 g_main_loop_quit (loop);
118 sig_timeout = TRUE;
119 return G_SOURCE_REMOVE;
120 }
121
122 static gboolean
exit_mainloop(gpointer data)123 exit_mainloop (gpointer data)
124 {
125 GMainLoop *loop = data;
126 g_main_loop_quit (loop);
127 return G_SOURCE_REMOVE;
128 }
129
130 static gboolean
on_sig_received_2(gpointer data)131 on_sig_received_2 (gpointer data)
132 {
133 GMainLoop *loop = data;
134
135 sig_counter ++;
136 if (sig_counter == 2)
137 g_main_loop_quit (loop);
138 return G_SOURCE_REMOVE;
139 }
140
141 static void
test_signal(int signum)142 test_signal (int signum)
143 {
144 GMainLoop *mainloop;
145 int id;
146
147 mainloop = g_main_loop_new (NULL, FALSE);
148
149 sig_received = FALSE;
150 sig_counter = 0;
151 g_unix_signal_add (signum, on_sig_received, mainloop);
152 kill (getpid (), signum);
153 g_assert (!sig_received);
154 id = g_timeout_add (5000, on_sig_timeout, mainloop);
155 g_main_loop_run (mainloop);
156 g_assert (sig_received);
157 sig_received = FALSE;
158 g_source_remove (id);
159
160 /* Ensure we don't get double delivery */
161 g_timeout_add (500, exit_mainloop, mainloop);
162 g_main_loop_run (mainloop);
163 g_assert (!sig_received);
164
165 /* Ensure that two sources for the same signal get it */
166 sig_counter = 0;
167 g_unix_signal_add (signum, on_sig_received_2, mainloop);
168 g_unix_signal_add (signum, on_sig_received_2, mainloop);
169 id = g_timeout_add (5000, on_sig_timeout, mainloop);
170
171 kill (getpid (), signum);
172 g_main_loop_run (mainloop);
173 g_assert_cmpint (sig_counter, ==, 2);
174 g_source_remove (id);
175
176 g_main_loop_unref (mainloop);
177 }
178
179 static void
test_sighup(void)180 test_sighup (void)
181 {
182 test_signal (SIGHUP);
183 }
184
185 static void
test_sigterm(void)186 test_sigterm (void)
187 {
188 test_signal (SIGTERM);
189 }
190
191 static void
test_sighup_add_remove(void)192 test_sighup_add_remove (void)
193 {
194 guint id;
195 struct sigaction action;
196
197 sig_received = FALSE;
198 id = g_unix_signal_add (SIGHUP, on_sig_received, NULL);
199 g_source_remove (id);
200
201 sigaction (SIGHUP, NULL, &action);
202 g_assert (action.sa_handler == SIG_DFL);
203 }
204
205 static gboolean
nested_idle(gpointer data)206 nested_idle (gpointer data)
207 {
208 GMainLoop *nested;
209 GMainContext *context;
210 GSource *source;
211
212 context = g_main_context_new ();
213 nested = g_main_loop_new (context, FALSE);
214
215 source = g_unix_signal_source_new (SIGHUP);
216 g_source_set_callback (source, on_sig_received, nested, NULL);
217 g_source_attach (source, context);
218 g_source_unref (source);
219
220 kill (getpid (), SIGHUP);
221 g_main_loop_run (nested);
222 g_assert_cmpint (sig_counter, ==, 1);
223
224 g_main_loop_unref (nested);
225 g_main_context_unref (context);
226
227 return G_SOURCE_REMOVE;
228 }
229
230 static void
test_sighup_nested(void)231 test_sighup_nested (void)
232 {
233 GMainLoop *mainloop;
234
235 mainloop = g_main_loop_new (NULL, FALSE);
236
237 sig_counter = 0;
238 sig_received = FALSE;
239 g_unix_signal_add (SIGHUP, on_sig_received, mainloop);
240 g_idle_add (nested_idle, mainloop);
241
242 g_main_loop_run (mainloop);
243 g_assert_cmpint (sig_counter, ==, 2);
244
245 g_main_loop_unref (mainloop);
246 }
247
248 static gboolean
on_sigwinch_received(gpointer data)249 on_sigwinch_received (gpointer data)
250 {
251 GMainLoop *loop = (GMainLoop *) data;
252
253 sig_counter ++;
254
255 if (sig_counter == 1)
256 kill (getpid (), SIGWINCH);
257 else if (sig_counter == 2)
258 g_main_loop_quit (loop);
259 else if (sig_counter > 2)
260 g_assert_not_reached ();
261
262 /* Increase the time window in which an issue could happen. */
263 g_usleep (G_USEC_PER_SEC);
264
265 return G_SOURCE_CONTINUE;
266 }
267
268 static void
test_callback_after_signal(void)269 test_callback_after_signal (void)
270 {
271 /* Checks that user signal callback is invoked *after* receiving a signal.
272 * In other words a new signal is never merged with the one being currently
273 * dispatched or whose dispatch had already finished. */
274
275 GMainLoop *mainloop;
276 GMainContext *context;
277 GSource *source;
278
279 sig_counter = 0;
280
281 context = g_main_context_new ();
282 mainloop = g_main_loop_new (context, FALSE);
283
284 source = g_unix_signal_source_new (SIGWINCH);
285 g_source_set_callback (source, on_sigwinch_received, mainloop, NULL);
286 g_source_attach (source, context);
287 g_source_unref (source);
288
289 g_assert_cmpint (sig_counter, ==, 0);
290 kill (getpid (), SIGWINCH);
291 g_main_loop_run (mainloop);
292 g_assert_cmpint (sig_counter, ==, 2);
293
294 g_main_loop_unref (mainloop);
295 g_main_context_unref (context);
296 }
297
298 int
main(int argc,char * argv[])299 main (int argc,
300 char *argv[])
301 {
302 g_test_init (&argc, &argv, NULL);
303
304 g_test_add_func ("/glib-unix/pipe", test_pipe);
305 g_test_add_func ("/glib-unix/error", test_error);
306 g_test_add_func ("/glib-unix/nonblocking", test_nonblocking);
307 g_test_add_func ("/glib-unix/sighup", test_sighup);
308 g_test_add_func ("/glib-unix/sigterm", test_sigterm);
309 g_test_add_func ("/glib-unix/sighup_again", test_sighup);
310 g_test_add_func ("/glib-unix/sighup_add_remove", test_sighup_add_remove);
311 g_test_add_func ("/glib-unix/sighup_nested", test_sighup_nested);
312 g_test_add_func ("/glib-unix/callback_after_signal", test_callback_after_signal);
313
314 return g_test_run();
315 }
316