Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2011 Red Hat, Inc.
3 : : * Copyright 2023 Collabora Ltd.
4 : : *
5 : : * SPDX-License-Identifier: LicenseRef-old-glib-tests
6 : : *
7 : : * This work is provided "as is"; redistribution and modification
8 : : * in whole or in part, in any medium, physical or electronic is
9 : : * permitted without restriction.
10 : : *
11 : : * This work is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 : : *
15 : : * In no event shall the authors or contributors be liable for any
16 : : * direct, indirect, incidental, special, exemplary, or consequential
17 : : * damages (including, but not limited to, procurement of substitute
18 : : * goods or services; loss of use, data, or profits; or business
19 : : * interruption) however caused and on any theory of liability, whether
20 : : * in contract, strict liability, or tort (including negligence or
21 : : * otherwise) arising in any way out of the use of this software, even
22 : : * if advised of the possibility of such damage.
23 : : *
24 : : * Author: Colin Walters <walters@verbum.org>
25 : : */
26 : :
27 : : #include "config.h"
28 : :
29 : : #include "glib-private.h"
30 : : #include "glib-unix.h"
31 : : #include "gstdio.h"
32 : :
33 : : #include <string.h>
34 : : #include <pwd.h>
35 : : #include <unistd.h>
36 : :
37 : : #include "testutils.h"
38 : :
39 : : static void
40 : 0 : async_signal_safe_message (const char *message)
41 : : {
42 [ # # ]: 0 : if (write (2, message, strlen (message)) < 0 ||
43 : 0 : write (2, "\n", 1) < 0)
44 : : {
45 : : /* ignore: not much we can do */
46 : : }
47 : 0 : }
48 : :
49 : : static void
50 : 1 : test_closefrom (void)
51 : : {
52 : : /* Enough file descriptors to be confident that we're operating on
53 : : * all of them */
54 : 1 : const int N_FDS = 20;
55 : : int *fds;
56 : : int fd;
57 : : int i;
58 : : pid_t child;
59 : : int wait_status;
60 : :
61 : : /* The loop that populates @fds with pipes assumes this */
62 : 1 : g_assert (N_FDS % 2 == 0);
63 : :
64 : 1 : g_test_summary ("Test g_closefrom(), g_fdwalk_set_cloexec()");
65 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3247");
66 : :
67 [ + + ]: 4 : for (fd = 0; fd <= 2; fd++)
68 : : {
69 : : int flags;
70 : :
71 : 3 : g_assert_no_errno ((flags = fcntl (fd, F_GETFD)));
72 : 3 : g_assert_no_errno (fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC));
73 : : }
74 : :
75 : 1 : fds = g_new0 (int, N_FDS);
76 : :
77 [ + + ]: 11 : for (i = 0; i < N_FDS; i += 2)
78 : : {
79 : 10 : GError *error = NULL;
80 : : int pipefd[2];
81 : : int res;
82 : :
83 : : /* Intentionally neither O_CLOEXEC nor FD_CLOEXEC */
84 : 10 : res = g_unix_open_pipe (pipefd, 0, &error);
85 : 10 : g_assert (res);
86 : 10 : g_assert_no_error (error);
87 : 10 : g_clear_error (&error);
88 : 10 : fds[i] = pipefd[0];
89 : 10 : fds[i + 1] = pipefd[1];
90 : : }
91 : :
92 : 1 : child = fork ();
93 : :
94 : : /* Child process exits with status = 100 + the first wrong fd,
95 : : * or 0 if all were correct */
96 [ - + ]: 1 : if (child == 0)
97 : : {
98 [ # # ]: 0 : for (i = 0; i < N_FDS; i++)
99 : : {
100 : 0 : int flags = fcntl (fds[i], F_GETFD);
101 : :
102 [ # # ]: 0 : if (flags == -1)
103 : : {
104 : 0 : async_signal_safe_message ("fd should not have been closed");
105 : 0 : _exit (100 + fds[i]);
106 : : }
107 : :
108 [ # # ]: 0 : if (flags & FD_CLOEXEC)
109 : : {
110 : 0 : async_signal_safe_message ("fd should not have been close-on-exec yet");
111 : 0 : _exit (100 + fds[i]);
112 : : }
113 : : }
114 : :
115 : 0 : g_fdwalk_set_cloexec (3);
116 : :
117 [ # # ]: 0 : for (i = 0; i < N_FDS; i++)
118 : : {
119 : 0 : int flags = fcntl (fds[i], F_GETFD);
120 : :
121 [ # # ]: 0 : if (flags == -1)
122 : : {
123 : 0 : async_signal_safe_message ("fd should not have been closed");
124 : 0 : _exit (100 + fds[i]);
125 : : }
126 : :
127 [ # # ]: 0 : if (!(flags & FD_CLOEXEC))
128 : : {
129 : 0 : async_signal_safe_message ("fd should have been close-on-exec");
130 : 0 : _exit (100 + fds[i]);
131 : : }
132 : : }
133 : :
134 : 0 : g_closefrom (3);
135 : :
136 [ # # ]: 0 : for (fd = 0; fd <= 2; fd++)
137 : : {
138 : 0 : int flags = fcntl (fd, F_GETFD);
139 : :
140 [ # # ]: 0 : if (flags == -1)
141 : : {
142 : 0 : async_signal_safe_message ("fd should not have been closed");
143 : 0 : _exit (100 + fd);
144 : : }
145 : :
146 [ # # ]: 0 : if (flags & FD_CLOEXEC)
147 : : {
148 : 0 : async_signal_safe_message ("fd should not have been close-on-exec");
149 : 0 : _exit (100 + fd);
150 : : }
151 : : }
152 : :
153 [ # # ]: 0 : for (i = 0; i < N_FDS; i++)
154 : : {
155 [ # # # # ]: 0 : if (fcntl (fds[i], F_GETFD) != -1 || errno != EBADF)
156 : : {
157 : 0 : async_signal_safe_message ("fd should have been closed");
158 : 0 : _exit (100 + fds[i]);
159 : : }
160 : : }
161 : :
162 : 0 : _exit (0);
163 : : }
164 : :
165 : 1 : g_assert_no_errno (waitpid (child, &wait_status, 0));
166 : :
167 [ + - ]: 1 : if (WIFEXITED (wait_status))
168 : : {
169 : 1 : int exit_status = WEXITSTATUS (wait_status);
170 : :
171 [ - + ]: 1 : if (exit_status != 0)
172 : 0 : g_test_fail_printf ("File descriptor %d in incorrect state", exit_status - 100);
173 : : }
174 : : else
175 : : {
176 : 0 : g_test_fail_printf ("Unexpected wait status %d", wait_status);
177 : : }
178 : :
179 [ + + ]: 21 : for (i = 0; i < N_FDS; i++)
180 : : {
181 : 20 : GError *error = NULL;
182 : :
183 : 20 : g_close (fds[i], &error);
184 : 20 : g_assert_no_error (error);
185 : 20 : g_clear_error (&error);
186 : : }
187 : :
188 : 1 : g_free (fds);
189 : :
190 [ + - ]: 1 : if (g_test_undefined ())
191 : : {
192 : 1 : g_test_trap_subprocess ("/glib-unix/closefrom/subprocess/einval",
193 : : 0, G_TEST_SUBPROCESS_DEFAULT);
194 : 1 : g_test_trap_assert_passed ();
195 : : }
196 : 1 : }
197 : :
198 : : static void
199 : 1 : test_closefrom_subprocess_einval (void)
200 : : {
201 : : int res;
202 : : int errsv;
203 : :
204 : 1 : g_log_set_always_fatal (G_LOG_FATAL_MASK);
205 : 1 : g_log_set_fatal_mask ("GLib", G_LOG_FATAL_MASK);
206 : :
207 : 1 : errno = 0;
208 : 1 : res = g_closefrom (-1);
209 : 1 : errsv = errno;
210 : 1 : g_assert_cmpint (res, ==, -1);
211 : 1 : g_assert_cmpint (errsv, ==, EINVAL);
212 : :
213 : 1 : errno = 0;
214 : 1 : res = g_fdwalk_set_cloexec (-42);
215 : 1 : errsv = errno;
216 : 1 : g_assert_cmpint (res, ==, -1);
217 : 1 : g_assert_cmpint (errsv, ==, EINVAL);
218 : 1 : }
219 : :
220 : : static void
221 : 1 : test_pipe (void)
222 : : {
223 : 1 : GError *error = NULL;
224 : : int pipefd[2];
225 : : char buf[1024];
226 : : gssize bytes_read;
227 : : gboolean res;
228 : :
229 : 1 : res = g_unix_open_pipe (pipefd, O_CLOEXEC, &error);
230 : 1 : g_assert (res);
231 : 1 : g_assert_no_error (error);
232 : :
233 : 1 : g_assert_cmpint (write (pipefd[1], "hello", sizeof ("hello")), ==, sizeof ("hello"));
234 : 1 : memset (buf, 0, sizeof (buf));
235 : 1 : bytes_read = read (pipefd[0], buf, sizeof(buf) - 1);
236 : 1 : g_assert_cmpint (bytes_read, >, 0);
237 : 1 : buf[bytes_read] = '\0';
238 : :
239 : 1 : close (pipefd[0]);
240 : 1 : close (pipefd[1]);
241 : :
242 : 1 : g_assert (g_str_has_prefix (buf, "hello"));
243 : 1 : }
244 : :
245 : : static void
246 : 1 : test_pipe_fd_cloexec (void)
247 : : {
248 : 1 : GError *error = NULL;
249 : : int pipefd[2];
250 : : char buf[1024];
251 : : gssize bytes_read;
252 : : gboolean res;
253 : :
254 : 1 : g_test_summary ("Test that FD_CLOEXEC is still accepted as an argument to g_unix_open_pipe()");
255 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3459");
256 : :
257 : 1 : res = g_unix_open_pipe (pipefd, FD_CLOEXEC, &error);
258 : 1 : g_assert (res);
259 : 1 : g_assert_no_error (error);
260 : :
261 : 1 : g_assert_cmpint (write (pipefd[1], "hello", sizeof ("hello")), ==, sizeof ("hello"));
262 : 1 : memset (buf, 0, sizeof (buf));
263 : 1 : bytes_read = read (pipefd[0], buf, sizeof(buf) - 1);
264 : 1 : g_assert_cmpint (bytes_read, >, 0);
265 : 1 : buf[bytes_read] = '\0';
266 : :
267 : 1 : close (pipefd[0]);
268 : 1 : close (pipefd[1]);
269 : :
270 : 1 : g_assert_true (g_str_has_prefix (buf, "hello"));
271 : 1 : }
272 : :
273 : : static void
274 : 1 : test_pipe_stdio_overwrite (void)
275 : : {
276 : 1 : GError *error = NULL;
277 : : int pipefd[2], ret;
278 : : gboolean res;
279 : : int stdin_fd;
280 : :
281 : :
282 : 1 : g_test_summary ("Test that g_unix_open_pipe() will use the first available FD, even if it’s stdin/stdout/stderr");
283 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2795");
284 : :
285 : 1 : stdin_fd = dup (STDIN_FILENO);
286 : 1 : g_assert_cmpint (stdin_fd, >, 0);
287 : :
288 : 1 : g_close (STDIN_FILENO, &error);
289 : 1 : g_assert_no_error (error);
290 : :
291 : 1 : res = g_unix_open_pipe (pipefd, O_CLOEXEC, &error);
292 : 1 : g_assert_no_error (error);
293 : 1 : g_assert_true (res);
294 : :
295 : 1 : g_assert_cmpint (pipefd[0], ==, STDIN_FILENO);
296 : :
297 : 1 : g_close (pipefd[0], &error);
298 : 1 : g_assert_no_error (error);
299 : :
300 : 1 : g_close (pipefd[1], &error);
301 : 1 : g_assert_no_error (error);
302 : :
303 : 1 : ret = dup2 (stdin_fd, STDIN_FILENO);
304 : 1 : g_assert_cmpint (ret, >=, 0);
305 : :
306 : 1 : g_close (stdin_fd, &error);
307 : 1 : g_assert_no_error (error);
308 : 1 : }
309 : :
310 : : static void
311 : 1 : test_pipe_struct (void)
312 : : {
313 : 1 : GError *error = NULL;
314 : 1 : GUnixPipe pair = G_UNIX_PIPE_INIT;
315 : : char buf[1024];
316 : : gssize bytes_read;
317 : : gboolean res;
318 : 1 : int read_end = -1; /* owned */
319 : 1 : int write_end = -1; /* unowned */
320 : : int errsv;
321 : :
322 : 1 : g_test_summary ("Test GUnixPipe structure");
323 : :
324 : 1 : res = g_unix_pipe_open (&pair, O_CLOEXEC, &error);
325 : 1 : g_assert_no_error (error);
326 : 1 : g_assert_true (res);
327 : :
328 : 1 : read_end = g_unix_pipe_steal (&pair, G_UNIX_PIPE_END_READ);
329 : 1 : g_assert_cmpint (read_end, >=, 0);
330 : 1 : g_assert_cmpint (g_unix_pipe_steal (&pair, G_UNIX_PIPE_END_READ), ==, -1);
331 : 1 : g_assert_cmpint (g_unix_pipe_get (&pair, G_UNIX_PIPE_END_READ), ==, -1);
332 : 1 : write_end = g_unix_pipe_get (&pair, G_UNIX_PIPE_END_WRITE);
333 : 1 : g_assert_cmpint (write_end, >=, 0);
334 : 1 : g_assert_cmpint (g_unix_pipe_get (&pair, G_UNIX_PIPE_END_WRITE), ==, write_end);
335 : :
336 : 1 : g_assert_cmpint (write (write_end, "hello", sizeof ("hello")), ==, sizeof ("hello"));
337 : 1 : memset (buf, 0, sizeof (buf));
338 : 1 : bytes_read = read (read_end, buf, sizeof(buf) - 1);
339 : 1 : g_assert_cmpint (bytes_read, ==, sizeof ("hello"));
340 : 1 : buf[bytes_read] = '\0';
341 : :
342 : : /* This is one of the few errno values guaranteed by Standard C.
343 : : * We set it here to check that g_unix_pipe_clear doesn't alter errno. */
344 : 1 : errno = EILSEQ;
345 : :
346 : 1 : g_unix_pipe_clear (&pair);
347 : 1 : errsv = errno;
348 : 1 : g_assert_cmpint (errsv, ==, EILSEQ);
349 : :
350 : 1 : g_assert_cmpint (pair.fds[0], ==, -1);
351 : 1 : g_assert_cmpint (pair.fds[1], ==, -1);
352 : :
353 : : /* The read end wasn't closed, because it was stolen first */
354 : 1 : g_clear_fd (&read_end, &error);
355 : 1 : g_assert_no_error (error);
356 : :
357 : : /* The write end was closed, because it wasn't stolen */
358 : 1 : assert_fd_was_closed (write_end);
359 : :
360 : 1 : g_assert_cmpstr (buf, ==, "hello");
361 : 1 : }
362 : :
363 : : static void
364 : 1 : test_pipe_struct_auto (void)
365 : : {
366 : : #ifdef g_autofree
367 : : int i;
368 : :
369 : 1 : g_test_summary ("Test g_auto(GUnixPipe)");
370 : :
371 : : /* Let g_auto close the read end, the write end, neither, or both */
372 [ + + ]: 5 : for (i = 0; i < 4; i++)
373 : : {
374 : 4 : int read_end = -1; /* unowned */
375 : 4 : int write_end = -1; /* unowned */
376 : : int errsv;
377 : :
378 : : {
379 : 8 : g_auto(GUnixPipe) pair = G_UNIX_PIPE_INIT;
380 : 4 : GError *error = NULL;
381 : : gboolean res;
382 : :
383 : 4 : res = g_unix_pipe_open (&pair, O_CLOEXEC, &error);
384 : 4 : g_assert_no_error (error);
385 : 4 : g_assert_true (res);
386 : :
387 : 4 : read_end = pair.fds[G_UNIX_PIPE_END_READ];
388 : 4 : g_assert_cmpint (read_end, >=, 0);
389 : 4 : write_end = pair.fds[G_UNIX_PIPE_END_WRITE];
390 : 4 : g_assert_cmpint (write_end, >=, 0);
391 : :
392 [ + + ]: 4 : if (i & 1)
393 : : {
394 : : /* This also exercises g_unix_pipe_close() with error */
395 : 2 : res = g_unix_pipe_close (&pair, G_UNIX_PIPE_END_READ, &error);
396 : 2 : g_assert_no_error (error);
397 : 2 : g_assert_true (res);
398 : : }
399 : :
400 : : /* This also exercises g_unix_pipe_close() without error */
401 [ + + ]: 4 : if (i & 2)
402 : 2 : g_unix_pipe_close (&pair, G_UNIX_PIPE_END_WRITE, NULL);
403 : :
404 : : /* This is one of the few errno values guaranteed by Standard C.
405 : : * We set it here to check that a g_auto(GUnixPipe) close doesn't
406 : : * alter errno. */
407 : 4 : errno = EILSEQ;
408 : : }
409 : :
410 : 4 : errsv = errno;
411 : 4 : g_assert_cmpint (errsv, ==, EILSEQ);
412 : 4 : assert_fd_was_closed (read_end);
413 : 4 : assert_fd_was_closed (write_end);
414 : : }
415 : : #else
416 : : g_test_skip ("g_auto not supported by compiler");
417 : : #endif
418 : 1 : }
419 : :
420 : : static void
421 : 1 : test_error (void)
422 : : {
423 : 1 : GError *error = NULL;
424 : : gboolean res;
425 : :
426 : 1 : res = g_unix_set_fd_nonblocking (123456, TRUE, &error);
427 : 1 : g_assert_cmpint (errno, ==, EBADF);
428 : 1 : g_assert (!res);
429 : 1 : g_assert_error (error, G_UNIX_ERROR, 0);
430 : 1 : g_clear_error (&error);
431 : 1 : }
432 : :
433 : : static void
434 : 1 : test_nonblocking (void)
435 : : {
436 : 1 : GError *error = NULL;
437 : : int pipefd[2];
438 : : gboolean res;
439 : : int flags;
440 : :
441 : 1 : res = g_unix_open_pipe (pipefd, O_CLOEXEC, &error);
442 : 1 : g_assert (res);
443 : 1 : g_assert_no_error (error);
444 : :
445 : 1 : res = g_unix_set_fd_nonblocking (pipefd[0], TRUE, &error);
446 : 1 : g_assert (res);
447 : 1 : g_assert_no_error (error);
448 : :
449 : 1 : flags = fcntl (pipefd[0], F_GETFL);
450 : 1 : g_assert_cmpint (flags, !=, -1);
451 : 1 : g_assert (flags & O_NONBLOCK);
452 : :
453 : 1 : res = g_unix_set_fd_nonblocking (pipefd[0], FALSE, &error);
454 : 1 : g_assert (res);
455 : 1 : g_assert_no_error (error);
456 : :
457 : 1 : flags = fcntl (pipefd[0], F_GETFL);
458 : 1 : g_assert_cmpint (flags, !=, -1);
459 : 1 : g_assert (!(flags & O_NONBLOCK));
460 : :
461 : 1 : close (pipefd[0]);
462 : 1 : close (pipefd[1]);
463 : 1 : }
464 : :
465 : : static gboolean sig_received = FALSE;
466 : : static gboolean sig_timeout = FALSE;
467 : : static int sig_counter = 0;
468 : :
469 : : static gboolean
470 : 5 : on_sig_received (gpointer user_data)
471 : : {
472 : 5 : GMainLoop *loop = user_data;
473 : 5 : g_main_loop_quit (loop);
474 : 5 : sig_received = TRUE;
475 : 5 : sig_counter ++;
476 : 5 : return G_SOURCE_REMOVE;
477 : : }
478 : :
479 : : static gboolean
480 : 0 : on_sig_timeout (gpointer data)
481 : : {
482 : 0 : GMainLoop *loop = data;
483 : 0 : g_main_loop_quit (loop);
484 : 0 : sig_timeout = TRUE;
485 : 0 : return G_SOURCE_REMOVE;
486 : : }
487 : :
488 : : static gboolean
489 : 3 : exit_mainloop (gpointer data)
490 : : {
491 : 3 : GMainLoop *loop = data;
492 : 3 : g_main_loop_quit (loop);
493 : 3 : return G_SOURCE_REMOVE;
494 : : }
495 : :
496 : : static gboolean
497 : 6 : on_sig_received_2 (gpointer data)
498 : : {
499 : 6 : GMainLoop *loop = data;
500 : :
501 : 6 : sig_counter ++;
502 [ + + ]: 6 : if (sig_counter == 2)
503 : 3 : g_main_loop_quit (loop);
504 : 6 : return G_SOURCE_REMOVE;
505 : : }
506 : :
507 : : static void
508 : 3 : test_signal (int signum)
509 : : {
510 : : GMainLoop *mainloop;
511 : : int id;
512 : :
513 : 3 : mainloop = g_main_loop_new (NULL, FALSE);
514 : :
515 : 3 : sig_received = FALSE;
516 : 3 : sig_counter = 0;
517 : 3 : g_unix_signal_add (signum, on_sig_received, mainloop);
518 : 3 : kill (getpid (), signum);
519 : 3 : g_assert (!sig_received);
520 : 3 : id = g_timeout_add (5000, on_sig_timeout, mainloop);
521 : 3 : g_main_loop_run (mainloop);
522 : 3 : g_assert (sig_received);
523 : 3 : sig_received = FALSE;
524 : 3 : g_source_remove (id);
525 : :
526 : : /* Ensure we don't get double delivery */
527 : 3 : g_timeout_add (500, exit_mainloop, mainloop);
528 : 3 : g_main_loop_run (mainloop);
529 : 3 : g_assert (!sig_received);
530 : :
531 : : /* Ensure that two sources for the same signal get it */
532 : 3 : sig_counter = 0;
533 : 3 : g_unix_signal_add (signum, on_sig_received_2, mainloop);
534 : 3 : g_unix_signal_add (signum, on_sig_received_2, mainloop);
535 : 3 : id = g_timeout_add (5000, on_sig_timeout, mainloop);
536 : :
537 : 3 : kill (getpid (), signum);
538 : 3 : g_main_loop_run (mainloop);
539 : 3 : g_assert_cmpint (sig_counter, ==, 2);
540 : 3 : g_source_remove (id);
541 : :
542 : 3 : g_main_loop_unref (mainloop);
543 : 3 : }
544 : :
545 : : static void
546 : 2 : test_sighup (void)
547 : : {
548 : 2 : test_signal (SIGHUP);
549 : 2 : }
550 : :
551 : : static void
552 : 1 : test_sigterm (void)
553 : : {
554 : 1 : test_signal (SIGTERM);
555 : 1 : }
556 : :
557 : : static void
558 : 1 : test_sighup_add_remove (void)
559 : : {
560 : : guint id;
561 : : struct sigaction action;
562 : :
563 : 1 : sig_received = FALSE;
564 : 1 : id = g_unix_signal_add (SIGHUP, on_sig_received, NULL);
565 : 1 : g_source_remove (id);
566 : :
567 : 1 : sigaction (SIGHUP, NULL, &action);
568 : 1 : g_assert (action.sa_handler == SIG_DFL);
569 : 1 : }
570 : :
571 : : static gboolean
572 : 1 : nested_idle (gpointer data)
573 : : {
574 : : GMainLoop *nested;
575 : : GMainContext *context;
576 : : GSource *source;
577 : :
578 : 1 : context = g_main_context_new ();
579 : 1 : nested = g_main_loop_new (context, FALSE);
580 : :
581 : 1 : source = g_unix_signal_source_new (SIGHUP);
582 : 1 : g_source_set_callback (source, on_sig_received, nested, NULL);
583 : 1 : g_source_attach (source, context);
584 : 1 : g_source_unref (source);
585 : :
586 : 1 : kill (getpid (), SIGHUP);
587 : 1 : g_main_loop_run (nested);
588 : 1 : g_assert_cmpint (sig_counter, ==, 1);
589 : :
590 : 1 : g_main_loop_unref (nested);
591 : 1 : g_main_context_unref (context);
592 : :
593 : 1 : return G_SOURCE_REMOVE;
594 : : }
595 : :
596 : : static void
597 : 1 : test_sighup_nested (void)
598 : : {
599 : : GMainLoop *mainloop;
600 : :
601 : 1 : mainloop = g_main_loop_new (NULL, FALSE);
602 : :
603 : 1 : sig_counter = 0;
604 : 1 : sig_received = FALSE;
605 : 1 : g_unix_signal_add (SIGHUP, on_sig_received, mainloop);
606 : 1 : g_idle_add (nested_idle, mainloop);
607 : :
608 : 1 : g_main_loop_run (mainloop);
609 : 1 : g_assert_cmpint (sig_counter, ==, 2);
610 : :
611 : 1 : g_main_loop_unref (mainloop);
612 : 1 : }
613 : :
614 : : static gboolean
615 : 2 : on_sigwinch_received (gpointer data)
616 : : {
617 : 2 : GMainLoop *loop = (GMainLoop *) data;
618 : :
619 : 2 : sig_counter ++;
620 : :
621 [ + + ]: 2 : if (sig_counter == 1)
622 : 1 : kill (getpid (), SIGWINCH);
623 [ + - ]: 1 : else if (sig_counter == 2)
624 : 1 : g_main_loop_quit (loop);
625 [ # # ]: 0 : else if (sig_counter > 2)
626 : : g_assert_not_reached ();
627 : :
628 : : /* Increase the time window in which an issue could happen. */
629 : 2 : g_usleep (G_USEC_PER_SEC);
630 : :
631 : 2 : return G_SOURCE_CONTINUE;
632 : : }
633 : :
634 : : static void
635 : 1 : test_callback_after_signal (void)
636 : : {
637 : : /* Checks that user signal callback is invoked *after* receiving a signal.
638 : : * In other words a new signal is never merged with the one being currently
639 : : * dispatched or whose dispatch had already finished. */
640 : :
641 : : GMainLoop *mainloop;
642 : : GMainContext *context;
643 : : GSource *source;
644 : :
645 : 1 : sig_counter = 0;
646 : :
647 : 1 : context = g_main_context_new ();
648 : 1 : mainloop = g_main_loop_new (context, FALSE);
649 : :
650 : 1 : source = g_unix_signal_source_new (SIGWINCH);
651 : 1 : g_source_set_callback (source, on_sigwinch_received, mainloop, NULL);
652 : 1 : g_source_attach (source, context);
653 : 1 : g_source_unref (source);
654 : :
655 : 1 : g_assert_cmpint (sig_counter, ==, 0);
656 : 1 : kill (getpid (), SIGWINCH);
657 : 1 : g_main_loop_run (mainloop);
658 : 1 : g_assert_cmpint (sig_counter, ==, 2);
659 : :
660 : 1 : g_main_loop_unref (mainloop);
661 : 1 : g_main_context_unref (context);
662 : 1 : }
663 : :
664 : : static void
665 : 1 : test_get_passwd_entry_root (void)
666 : : {
667 : : struct passwd *pwd;
668 : 1 : GError *local_error = NULL;
669 : :
670 : 1 : g_test_summary ("Tests that g_unix_get_passwd_entry() works for a "
671 : : "known-existing username.");
672 : :
673 : 1 : pwd = g_unix_get_passwd_entry ("root", &local_error);
674 : 1 : g_assert_no_error (local_error);
675 : :
676 : 1 : g_assert_cmpstr (pwd->pw_name, ==, "root");
677 : 1 : g_assert_cmpuint (pwd->pw_uid, ==, 0);
678 : :
679 : 1 : g_free (pwd);
680 : 1 : }
681 : :
682 : : static void
683 : 1 : test_get_passwd_entry_nonexistent (void)
684 : : {
685 : : struct passwd *pwd;
686 : 1 : GError *local_error = NULL;
687 : :
688 : 1 : g_test_summary ("Tests that g_unix_get_passwd_entry() returns an error for a "
689 : : "nonexistent username.");
690 : :
691 : 1 : pwd = g_unix_get_passwd_entry ("thisusernamedoesntexist", &local_error);
692 : 1 : g_assert_error (local_error, G_UNIX_ERROR, 0);
693 : 1 : g_assert_null (pwd);
694 : :
695 : 1 : g_clear_error (&local_error);
696 : 1 : }
697 : :
698 : : static void
699 : 1 : _child_wait_watch_cb (GPid pid,
700 : : gint wait_status,
701 : : gpointer user_data)
702 : : {
703 : 1 : gboolean *p_got_callback = user_data;
704 : :
705 : 1 : g_assert_nonnull (p_got_callback);
706 : 1 : g_assert_false (*p_got_callback);
707 : 1 : *p_got_callback = TRUE;
708 : 1 : }
709 : :
710 : : static void
711 : 1 : test_child_wait (void)
712 : : {
713 : : gboolean r;
714 : : GPid pid;
715 : : guint id;
716 : : pid_t pid2;
717 : : int wstatus;
718 : 1 : gboolean got_callback = FALSE;
719 : 1 : gboolean iterate_maincontext = g_test_rand_bit ();
720 : : char **argv;
721 : : int errsv;
722 : :
723 : : /* - We spawn a trivial child process that exits after a short time.
724 : : * - We schedule a g_child_watch_add()
725 : : * - we may iterate the GMainContext a bit. Randomly we either get the
726 : : * child-watcher callback or not.
727 : : * - if we didn't get the callback, we g_source_remove() the child watcher.
728 : : *
729 : : * Afterwards, if the callback didn't fire, we check that we are able to waitpid()
730 : : * on the process ourselves. Of course, if the child watcher notified, the waitpid()
731 : : * will fail with ECHILD.
732 : : */
733 : :
734 [ - + ]: 1 : argv = g_test_rand_bit () ? ((char *[]){ "/bin/sleep", "0.05", NULL }) : ((char *[]){ "/bin/true", NULL });
735 : :
736 : 1 : r = g_spawn_async (NULL,
737 : : argv,
738 : : NULL,
739 : : G_SPAWN_DO_NOT_REAP_CHILD,
740 : : NULL,
741 : : NULL,
742 : : &pid,
743 : : NULL);
744 [ - + ]: 1 : if (!r)
745 : : {
746 : : /* Some odd system without /bin/sleep? Skip the test. */
747 : 0 : g_test_skip ("failure to spawn test process in test_child_wait()");
748 : 0 : return;
749 : : }
750 : :
751 : 1 : g_assert_cmpint (pid, >=, 1);
752 : :
753 [ - + ]: 1 : if (g_test_rand_bit ())
754 : 0 : g_usleep (g_test_rand_int_range (0, (G_USEC_PER_SEC / 10)));
755 : :
756 : 1 : id = g_child_watch_add (pid, _child_wait_watch_cb, &got_callback);
757 : :
758 [ - + ]: 1 : if (g_test_rand_bit ())
759 : 0 : g_usleep (g_test_rand_int_range (0, (G_USEC_PER_SEC / 10)));
760 : :
761 [ + - ]: 1 : if (iterate_maincontext)
762 : : {
763 : 1 : gint64 start_usec = g_get_monotonic_time ();
764 : 1 : gint64 end_usec = start_usec + g_test_rand_int_range (0, (G_USEC_PER_SEC / 10));
765 : :
766 [ + + + - ]: 144 : while (!got_callback && g_get_monotonic_time () < end_usec)
767 : 143 : g_main_context_iteration (NULL, FALSE);
768 : : }
769 : :
770 [ - + ]: 1 : if (!got_callback)
771 : 0 : g_source_remove (id);
772 : :
773 : 1 : errno = 0;
774 : 1 : pid2 = waitpid (pid, &wstatus, 0);
775 : 1 : errsv = errno;
776 [ + - ]: 1 : if (got_callback)
777 : : {
778 : 1 : g_assert_true (iterate_maincontext);
779 : 1 : g_assert_cmpint (errsv, ==, ECHILD);
780 : 1 : g_assert_cmpint (pid2, <, 0);
781 : : }
782 : : else
783 : : {
784 : 0 : g_assert_cmpint (errsv, ==, 0);
785 : 0 : g_assert_cmpint (pid2, ==, pid);
786 : 0 : g_assert_true (WIFEXITED (wstatus));
787 : 0 : g_assert_cmpint (WEXITSTATUS (wstatus), ==, 0);
788 : : }
789 : : }
790 : :
791 : : int
792 : 2 : main (int argc,
793 : : char *argv[])
794 : : {
795 : 2 : g_test_init (&argc, &argv, NULL);
796 : :
797 : 2 : g_test_add_func ("/glib-unix/closefrom", test_closefrom);
798 : 2 : g_test_add_func ("/glib-unix/closefrom/subprocess/einval",
799 : : test_closefrom_subprocess_einval);
800 : 2 : g_test_add_func ("/glib-unix/pipe", test_pipe);
801 : 2 : g_test_add_func ("/glib-unix/pipe/fd-cloexec", test_pipe_fd_cloexec);
802 : 2 : g_test_add_func ("/glib-unix/pipe-stdio-overwrite", test_pipe_stdio_overwrite);
803 : 2 : g_test_add_func ("/glib-unix/pipe-struct", test_pipe_struct);
804 : 2 : g_test_add_func ("/glib-unix/pipe-struct-auto", test_pipe_struct_auto);
805 : 2 : g_test_add_func ("/glib-unix/error", test_error);
806 : 2 : g_test_add_func ("/glib-unix/nonblocking", test_nonblocking);
807 : 2 : g_test_add_func ("/glib-unix/sighup", test_sighup);
808 : 2 : g_test_add_func ("/glib-unix/sigterm", test_sigterm);
809 : 2 : g_test_add_func ("/glib-unix/sighup_again", test_sighup);
810 : 2 : g_test_add_func ("/glib-unix/sighup_add_remove", test_sighup_add_remove);
811 : 2 : g_test_add_func ("/glib-unix/sighup_nested", test_sighup_nested);
812 : 2 : g_test_add_func ("/glib-unix/callback_after_signal", test_callback_after_signal);
813 : 2 : g_test_add_func ("/glib-unix/get-passwd-entry/root", test_get_passwd_entry_root);
814 : 2 : g_test_add_func ("/glib-unix/get-passwd-entry/nonexistent", test_get_passwd_entry_nonexistent);
815 : 2 : g_test_add_func ("/glib-unix/child-wait", test_child_wait);
816 : :
817 : 2 : return g_test_run();
818 : : }
|