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 test_pipe (void)
41 {
42 GError *error = NULL;
43 int pipefd[2];
44 char buf[1024];
45 gssize bytes_read;
46 gboolean res;
47
48 res = g_unix_open_pipe (pipefd, O_CLOEXEC, &error);
49 g_assert (res);
50 g_assert_no_error (error);
51
52 g_assert_cmpint (write (pipefd[1], "hello", sizeof ("hello")), ==, sizeof ("hello"));
53 memset (buf, 0, sizeof (buf));
54 bytes_read = read (pipefd[0], buf, sizeof(buf) - 1);
55 g_assert_cmpint (bytes_read, >, 0);
56 buf[bytes_read] = '\0';
57
58 close (pipefd[0]);
59 close (pipefd[1]);
60
61 g_assert (g_str_has_prefix (buf, "hello"));
62 }
63
64 static void
65 test_pipe_fd_cloexec (void)
66 {
67 GError *error = NULL;
68 int pipefd[2];
69 char buf[1024];
70 gssize bytes_read;
71 gboolean res;
72
73 g_test_summary ("Test that FD_CLOEXEC is still accepted as an argument to g_unix_open_pipe()");
74 g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3459");
75
76 res = g_unix_open_pipe (pipefd, FD_CLOEXEC, &error);
77 g_assert (res);
78 g_assert_no_error (error);
79
80 g_assert_cmpint (write (pipefd[1], "hello", sizeof ("hello")), ==, sizeof ("hello"));
81 memset (buf, 0, sizeof (buf));
82 bytes_read = read (pipefd[0], buf, sizeof(buf) - 1);
83 g_assert_cmpint (bytes_read, >, 0);
84 buf[bytes_read] = '\0';
85
86 close (pipefd[0]);
87 close (pipefd[1]);
88
89 g_assert_true (g_str_has_prefix (buf, "hello"));
90 }
91
92 static void
93 test_pipe_stdio_overwrite (void)
94 {
95 GError *error = NULL;
96 int pipefd[2], ret;
97 gboolean res;
98 int stdin_fd;
99
100
101 g_test_summary ("Test that g_unix_open_pipe() will use the first available FD, even if it’s stdin/stdout/stderr");
102 g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2795");
103
104 stdin_fd = dup (STDIN_FILENO);
105 g_assert_cmpint (stdin_fd, >, 0);
106
107 g_close (STDIN_FILENO, &error);
108 g_assert_no_error (error);
109
110 res = g_unix_open_pipe (pipefd, O_CLOEXEC, &error);
111 g_assert_no_error (error);
112 g_assert_true (res);
113
114 g_assert_cmpint (pipefd[0], ==, STDIN_FILENO);
115
116 g_close (pipefd[0], &error);
117 g_assert_no_error (error);
118
119 g_close (pipefd[1], &error);
120 g_assert_no_error (error);
121
122 ret = dup2 (stdin_fd, STDIN_FILENO);
123 g_assert_cmpint (ret, >=, 0);
124
125 g_close (stdin_fd, &error);
126 g_assert_no_error (error);
127 }
128
129 static void
130 test_pipe_struct (void)
131 {
132 GError *error = NULL;
133 GUnixPipe pair = G_UNIX_PIPE_INIT;
134 char buf[1024];
135 gssize bytes_read;
136 gboolean res;
137 int read_end = -1; /* owned */
138 int write_end = -1; /* unowned */
139 int errsv;
140
141 g_test_summary ("Test GUnixPipe structure");
142
143 res = g_unix_pipe_open (&pair, FD_CLOEXEC, &error);
144 g_assert_no_error (error);
145 g_assert_true (res);
146
147 read_end = g_unix_pipe_steal (&pair, G_UNIX_PIPE_END_READ);
148 g_assert_cmpint (read_end, >=, 0);
149 g_assert_cmpint (g_unix_pipe_steal (&pair, G_UNIX_PIPE_END_READ), ==, -1);
150 g_assert_cmpint (g_unix_pipe_get (&pair, G_UNIX_PIPE_END_READ), ==, -1);
151 write_end = g_unix_pipe_get (&pair, G_UNIX_PIPE_END_WRITE);
152 g_assert_cmpint (write_end, >=, 0);
153 g_assert_cmpint (g_unix_pipe_get (&pair, G_UNIX_PIPE_END_WRITE), ==, write_end);
154
155 g_assert_cmpint (write (write_end, "hello", sizeof ("hello")), ==, sizeof ("hello"));
156 memset (buf, 0, sizeof (buf));
157 bytes_read = read (read_end, buf, sizeof(buf) - 1);
158 g_assert_cmpint (bytes_read, ==, sizeof ("hello"));
159 buf[bytes_read] = '\0';
160
161 /* This is one of the few errno values guaranteed by Standard C.
162 * We set it here to check that g_unix_pipe_clear doesn't alter errno. */
163 errno = EILSEQ;
164
165 g_unix_pipe_clear (&pair);
166 errsv = errno;
167 g_assert_cmpint (errsv, ==, EILSEQ);
168
169 g_assert_cmpint (pair.fds[0], ==, -1);
170 g_assert_cmpint (pair.fds[1], ==, -1);
171
172 /* The read end wasn't closed, because it was stolen first */
173 g_clear_fd (&read_end, &error);
174 g_assert_no_error (error);
175
176 /* The write end was closed, because it wasn't stolen */
177 assert_fd_was_closed (write_end);
178
179 g_assert_cmpstr (buf, ==, "hello");
180 }
181
182 static void
183 test_pipe_struct_auto (void)
184 {
185 #ifdef g_autofree
186 int i;
187
188 g_test_summary ("Test g_auto(GUnixPipe)");
189
190 /* Let g_auto close the read end, the write end, neither, or both */
191 for (i = 0; i < 4; i++)
192 {
193 int read_end = -1; /* unowned */
194 int write_end = -1; /* unowned */
195 int errsv;
196
197 {
198 g_auto(GUnixPipe) pair = G_UNIX_PIPE_INIT;
199 GError *error = NULL;
200 gboolean res;
201
202 res = g_unix_pipe_open (&pair, FD_CLOEXEC, &error);
203 g_assert_no_error (error);
204 g_assert_true (res);
205
206 read_end = pair.fds[G_UNIX_PIPE_END_READ];
207 g_assert_cmpint (read_end, >=, 0);
208 write_end = pair.fds[G_UNIX_PIPE_END_WRITE];
209 g_assert_cmpint (write_end, >=, 0);
210
211 if (i & 1)
212 {
213 /* This also exercises g_unix_pipe_close() with error */
214 res = g_unix_pipe_close (&pair, G_UNIX_PIPE_END_READ, &error);
215 g_assert_no_error (error);
216 g_assert_true (res);
217 }
218
219 /* This also exercises g_unix_pipe_close() without error */
220 if (i & 2)
221 g_unix_pipe_close (&pair, G_UNIX_PIPE_END_WRITE, NULL);
222
223 /* This is one of the few errno values guaranteed by Standard C.
224 * We set it here to check that a g_auto(GUnixPipe) close doesn't
225 * alter errno. */
226 errno = EILSEQ;
227 }
228
229 errsv = errno;
230 g_assert_cmpint (errsv, ==, EILSEQ);
231 assert_fd_was_closed (read_end);
232 assert_fd_was_closed (write_end);
233 }
234 #else
235 g_test_skip ("g_auto not supported by compiler");
236 #endif
237 }
238
239 static void
240 test_error (void)
241 {
242 GError *error = NULL;
243 gboolean res;
244
245 res = g_unix_set_fd_nonblocking (123456, TRUE, &error);
246 g_assert_cmpint (errno, ==, EBADF);
247 g_assert (!res);
248 g_assert_error (error, G_UNIX_ERROR, 0);
249 g_clear_error (&error);
250 }
251
252 static void
253 test_nonblocking (void)
254 {
255 GError *error = NULL;
256 int pipefd[2];
257 gboolean res;
258 int flags;
259
260 res = g_unix_open_pipe (pipefd, O_CLOEXEC, &error);
261 g_assert (res);
262 g_assert_no_error (error);
263
264 res = g_unix_set_fd_nonblocking (pipefd[0], TRUE, &error);
265 g_assert (res);
266 g_assert_no_error (error);
267
268 flags = fcntl (pipefd[0], F_GETFL);
269 g_assert_cmpint (flags, !=, -1);
270 g_assert (flags & O_NONBLOCK);
271
272 res = g_unix_set_fd_nonblocking (pipefd[0], FALSE, &error);
273 g_assert (res);
274 g_assert_no_error (error);
275
276 flags = fcntl (pipefd[0], F_GETFL);
277 g_assert_cmpint (flags, !=, -1);
278 g_assert (!(flags & O_NONBLOCK));
279
280 close (pipefd[0]);
281 close (pipefd[1]);
282 }
283
284 static gboolean sig_received = FALSE;
285 static gboolean sig_timeout = FALSE;
286 static int sig_counter = 0;
287
288 static gboolean
289 on_sig_received (gpointer user_data)
290 {
291 GMainLoop *loop = user_data;
292 g_main_loop_quit (loop);
293 sig_received = TRUE;
294 sig_counter ++;
295 return G_SOURCE_REMOVE;
296 }
297
298 static gboolean
299 on_sig_timeout (gpointer data)
300 {
301 GMainLoop *loop = data;
302 g_main_loop_quit (loop);
303 sig_timeout = TRUE;
304 return G_SOURCE_REMOVE;
305 }
306
307 static gboolean
308 exit_mainloop (gpointer data)
309 {
310 GMainLoop *loop = data;
311 g_main_loop_quit (loop);
312 return G_SOURCE_REMOVE;
313 }
314
315 static gboolean
316 on_sig_received_2 (gpointer data)
317 {
318 GMainLoop *loop = data;
319
320 sig_counter ++;
321 if (sig_counter == 2)
322 g_main_loop_quit (loop);
323 return G_SOURCE_REMOVE;
324 }
325
326 static void
327 test_signal (int signum)
328 {
329 GMainLoop *mainloop;
330 int id;
331
332 mainloop = g_main_loop_new (NULL, FALSE);
333
334 sig_received = FALSE;
335 sig_counter = 0;
336 g_unix_signal_add (signum, on_sig_received, mainloop);
337 kill (getpid (), signum);
338 g_assert (!sig_received);
339 id = g_timeout_add (5000, on_sig_timeout, mainloop);
340 g_main_loop_run (mainloop);
341 g_assert (sig_received);
342 sig_received = FALSE;
343 g_source_remove (id);
344
345 /* Ensure we don't get double delivery */
346 g_timeout_add (500, exit_mainloop, mainloop);
347 g_main_loop_run (mainloop);
348 g_assert (!sig_received);
349
350 /* Ensure that two sources for the same signal get it */
351 sig_counter = 0;
352 g_unix_signal_add (signum, on_sig_received_2, mainloop);
353 g_unix_signal_add (signum, on_sig_received_2, mainloop);
354 id = g_timeout_add (5000, on_sig_timeout, mainloop);
355
356 kill (getpid (), signum);
357 g_main_loop_run (mainloop);
358 g_assert_cmpint (sig_counter, ==, 2);
359 g_source_remove (id);
360
361 g_main_loop_unref (mainloop);
362 }
363
364 static void
365 test_sighup (void)
366 {
367 test_signal (SIGHUP);
368 }
369
370 static void
371 test_sigterm (void)
372 {
373 test_signal (SIGTERM);
374 }
375
376 static void
377 test_sighup_add_remove (void)
378 {
379 guint id;
380 struct sigaction action;
381
382 sig_received = FALSE;
383 id = g_unix_signal_add (SIGHUP, on_sig_received, NULL);
384 g_source_remove (id);
385
386 sigaction (SIGHUP, NULL, &action);
387 g_assert (action.sa_handler == SIG_DFL);
388 }
389
390 static gboolean
391 nested_idle (gpointer data)
392 {
393 GMainLoop *nested;
394 GMainContext *context;
395 GSource *source;
396
397 context = g_main_context_new ();
398 nested = g_main_loop_new (context, FALSE);
399
400 source = g_unix_signal_source_new (SIGHUP);
401 g_source_set_callback (source, on_sig_received, nested, NULL);
402 g_source_attach (source, context);
403 g_source_unref (source);
404
405 kill (getpid (), SIGHUP);
406 g_main_loop_run (nested);
407 g_assert_cmpint (sig_counter, ==, 1);
408
409 g_main_loop_unref (nested);
410 g_main_context_unref (context);
411
412 return G_SOURCE_REMOVE;
413 }
414
415 static void
416 test_sighup_nested (void)
417 {
418 GMainLoop *mainloop;
419
420 mainloop = g_main_loop_new (NULL, FALSE);
421
422 sig_counter = 0;
423 sig_received = FALSE;
424 g_unix_signal_add (SIGHUP, on_sig_received, mainloop);
425 g_idle_add (nested_idle, mainloop);
426
427 g_main_loop_run (mainloop);
428 g_assert_cmpint (sig_counter, ==, 2);
429
430 g_main_loop_unref (mainloop);
431 }
432
433 static gboolean
434 on_sigwinch_received (gpointer data)
435 {
436 GMainLoop *loop = (GMainLoop *) data;
437
438 sig_counter ++;
439
440 if (sig_counter == 1)
441 kill (getpid (), SIGWINCH);
442 else if (sig_counter == 2)
443 g_main_loop_quit (loop);
444 else if (sig_counter > 2)
445 g_assert_not_reached ();
446
447 /* Increase the time window in which an issue could happen. */
448 g_usleep (G_USEC_PER_SEC);
449
450 return G_SOURCE_CONTINUE;
451 }
452
453 static void
454 test_callback_after_signal (void)
455 {
456 /* Checks that user signal callback is invoked *after* receiving a signal.
457 * In other words a new signal is never merged with the one being currently
458 * dispatched or whose dispatch had already finished. */
459
460 GMainLoop *mainloop;
461 GMainContext *context;
462 GSource *source;
463
464 sig_counter = 0;
465
466 context = g_main_context_new ();
467 mainloop = g_main_loop_new (context, FALSE);
468
469 source = g_unix_signal_source_new (SIGWINCH);
470 g_source_set_callback (source, on_sigwinch_received, mainloop, NULL);
471 g_source_attach (source, context);
472 g_source_unref (source);
473
474 g_assert_cmpint (sig_counter, ==, 0);
475 kill (getpid (), SIGWINCH);
476 g_main_loop_run (mainloop);
477 g_assert_cmpint (sig_counter, ==, 2);
478
479 g_main_loop_unref (mainloop);
480 g_main_context_unref (context);
481 }
482
483 static void
484 test_get_passwd_entry_root (void)
485 {
486 struct passwd *pwd;
487 GError *local_error = NULL;
488
489 g_test_summary ("Tests that g_unix_get_passwd_entry() works for a "
490 "known-existing username.");
491
492 pwd = g_unix_get_passwd_entry ("root", &local_error);
493 g_assert_no_error (local_error);
494
495 g_assert_cmpstr (pwd->pw_name, ==, "root");
496 g_assert_cmpuint (pwd->pw_uid, ==, 0);
497
498 g_free (pwd);
499 }
500
501 static void
502 test_get_passwd_entry_nonexistent (void)
503 {
504 struct passwd *pwd;
505 GError *local_error = NULL;
506
507 g_test_summary ("Tests that g_unix_get_passwd_entry() returns an error for a "
508 "nonexistent username.");
509
510 pwd = g_unix_get_passwd_entry ("thisusernamedoesntexist", &local_error);
511 g_assert_error (local_error, G_UNIX_ERROR, 0);
512 g_assert_null (pwd);
513
514 g_clear_error (&local_error);
515 }
516
517 static void
518 _child_wait_watch_cb (GPid pid,
519 gint wait_status,
520 gpointer user_data)
521 {
522 gboolean *p_got_callback = user_data;
523
524 g_assert_nonnull (p_got_callback);
525 g_assert_false (*p_got_callback);
526 *p_got_callback = TRUE;
527 }
528
529 static void
530 test_child_wait (void)
531 {
532 gboolean r;
533 GPid pid;
534 guint id;
535 pid_t pid2;
536 int wstatus;
537 gboolean got_callback = FALSE;
538 gboolean iterate_maincontext = g_test_rand_bit ();
539 char **argv;
540 int errsv;
541
542 /* - We spawn a trivial child process that exits after a short time.
543 * - We schedule a g_child_watch_add()
544 * - we may iterate the GMainContext a bit. Randomly we either get the
545 * child-watcher callback or not.
546 * - if we didn't get the callback, we g_source_remove() the child watcher.
547 *
548 * Afterwards, if the callback didn't fire, we check that we are able to waitpid()
549 * on the process ourselves. Of course, if the child watcher notified, the waitpid()
550 * will fail with ECHILD.
551 */
552
553 argv = g_test_rand_bit () ? ((char *[]){ "/bin/sleep", "0.05", NULL }) : ((char *[]){ "/bin/true", NULL });
554
555 r = g_spawn_async (NULL,
556 argv,
557 NULL,
558 G_SPAWN_DO_NOT_REAP_CHILD,
559 NULL,
560 NULL,
561 &pid,
562 NULL);
563 if (!r)
564 {
565 /* Some odd system without /bin/sleep? Skip the test. */
566 g_test_skip ("failure to spawn test process in test_child_wait()");
567 return;
568 }
569
570 g_assert_cmpint (pid, >=, 1);
571
572 if (g_test_rand_bit ())
573 g_usleep (g_test_rand_int_range (0, (G_USEC_PER_SEC / 10)));
574
575 id = g_child_watch_add (pid, _child_wait_watch_cb, &got_callback);
576
577 if (g_test_rand_bit ())
578 g_usleep (g_test_rand_int_range (0, (G_USEC_PER_SEC / 10)));
579
580 if (iterate_maincontext)
581 {
582 gint64 start_usec = g_get_monotonic_time ();
583 gint64 end_usec = start_usec + g_test_rand_int_range (0, (G_USEC_PER_SEC / 10));
584
585 while (!got_callback && g_get_monotonic_time () < end_usec)
586 g_main_context_iteration (NULL, FALSE);
587 }
588
589 if (!got_callback)
590 g_source_remove (id);
591
592 errno = 0;
593 pid2 = waitpid (pid, &wstatus, 0);
594 errsv = errno;
595 if (got_callback)
596 {
597 g_assert_true (iterate_maincontext);
598 g_assert_cmpint (errsv, ==, ECHILD);
599 g_assert_cmpint (pid2, <, 0);
600 }
601 else
602 {
603 g_assert_cmpint (errsv, ==, 0);
604 g_assert_cmpint (pid2, ==, pid);
605 g_assert_true (WIFEXITED (wstatus));
606 g_assert_cmpint (WEXITSTATUS (wstatus), ==, 0);
607 }
608 }
609
610 int
611 main (int argc,
612 char *argv[])
613 {
614 g_test_init (&argc, &argv, NULL);
615
616 g_test_add_func ("/glib-unix/pipe", test_pipe);
617 g_test_add_func ("/glib-unix/pipe/fd-cloexec", test_pipe_fd_cloexec);
618 g_test_add_func ("/glib-unix/pipe-stdio-overwrite", test_pipe_stdio_overwrite);
619 g_test_add_func ("/glib-unix/pipe-struct", test_pipe_struct);
620 g_test_add_func ("/glib-unix/pipe-struct-auto", test_pipe_struct_auto);
621 g_test_add_func ("/glib-unix/error", test_error);
622 g_test_add_func ("/glib-unix/nonblocking", test_nonblocking);
623 g_test_add_func ("/glib-unix/sighup", test_sighup);
624 g_test_add_func ("/glib-unix/sigterm", test_sigterm);
625 g_test_add_func ("/glib-unix/sighup_again", test_sighup);
626 g_test_add_func ("/glib-unix/sighup_add_remove", test_sighup_add_remove);
627 g_test_add_func ("/glib-unix/sighup_nested", test_sighup_nested);
628 g_test_add_func ("/glib-unix/callback_after_signal", test_callback_after_signal);
629 g_test_add_func ("/glib-unix/get-passwd-entry/root", test_get_passwd_entry_root);
630 g_test_add_func ("/glib-unix/get-passwd-entry/nonexistent", test_get_passwd_entry_nonexistent);
631 g_test_add_func ("/glib-unix/child-wait", test_child_wait);
632
633 return g_test_run();
634 }