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#include <pwd.h>
29
30static void
31test_pipe (void)
32{
33 GError *error = NULL;
34 int pipefd[2];
35 char buf[1024];
36 gssize bytes_read;
37 gboolean res;
38
39 res = g_unix_open_pipe (fds: pipefd, FD_CLOEXEC, error: &error);
40 g_assert (res);
41 g_assert_no_error (error);
42
43 write (fd: pipefd[1], buf: "hello", n: sizeof ("hello"));
44 memset (s: buf, c: 0, n: sizeof (buf));
45 bytes_read = read (fd: pipefd[0], buf: buf, nbytes: sizeof(buf) - 1);
46 g_assert_cmpint (bytes_read, >, 0);
47 buf[bytes_read] = '\0';
48
49 close (fd: pipefd[0]);
50 close (fd: pipefd[1]);
51
52 g_assert (g_str_has_prefix (buf, "hello"));
53}
54
55static void
56test_error (void)
57{
58 GError *error = NULL;
59 gboolean res;
60
61 res = g_unix_set_fd_nonblocking (fd: 123456, TRUE, error: &error);
62 g_assert_cmpint (errno, ==, EBADF);
63 g_assert (!res);
64 g_assert_error (error, G_UNIX_ERROR, 0);
65 g_clear_error (err: &error);
66}
67
68static void
69test_nonblocking (void)
70{
71 GError *error = NULL;
72 int pipefd[2];
73 gboolean res;
74 int flags;
75
76 res = g_unix_open_pipe (fds: pipefd, FD_CLOEXEC, error: &error);
77 g_assert (res);
78 g_assert_no_error (error);
79
80 res = g_unix_set_fd_nonblocking (fd: pipefd[0], TRUE, error: &error);
81 g_assert (res);
82 g_assert_no_error (error);
83
84 flags = fcntl (fd: pipefd[0], F_GETFL);
85 g_assert_cmpint (flags, !=, -1);
86 g_assert (flags & O_NONBLOCK);
87
88 res = g_unix_set_fd_nonblocking (fd: pipefd[0], FALSE, error: &error);
89 g_assert (res);
90 g_assert_no_error (error);
91
92 flags = fcntl (fd: pipefd[0], F_GETFL);
93 g_assert_cmpint (flags, !=, -1);
94 g_assert (!(flags & O_NONBLOCK));
95
96 close (fd: pipefd[0]);
97 close (fd: pipefd[1]);
98}
99
100static gboolean sig_received = FALSE;
101static gboolean sig_timeout = FALSE;
102static int sig_counter = 0;
103
104static gboolean
105on_sig_received (gpointer user_data)
106{
107 GMainLoop *loop = user_data;
108 g_main_loop_quit (loop);
109 sig_received = TRUE;
110 sig_counter ++;
111 return G_SOURCE_REMOVE;
112}
113
114static gboolean
115on_sig_timeout (gpointer data)
116{
117 GMainLoop *loop = data;
118 g_main_loop_quit (loop);
119 sig_timeout = TRUE;
120 return G_SOURCE_REMOVE;
121}
122
123static gboolean
124exit_mainloop (gpointer data)
125{
126 GMainLoop *loop = data;
127 g_main_loop_quit (loop);
128 return G_SOURCE_REMOVE;
129}
130
131static gboolean
132on_sig_received_2 (gpointer data)
133{
134 GMainLoop *loop = data;
135
136 sig_counter ++;
137 if (sig_counter == 2)
138 g_main_loop_quit (loop);
139 return G_SOURCE_REMOVE;
140}
141
142static void
143test_signal (int signum)
144{
145 GMainLoop *mainloop;
146 int id;
147
148 mainloop = g_main_loop_new (NULL, FALSE);
149
150 sig_received = FALSE;
151 sig_counter = 0;
152 g_unix_signal_add (signum, handler: on_sig_received, user_data: mainloop);
153 kill (pid: getpid (), sig: signum);
154 g_assert (!sig_received);
155 id = g_timeout_add (interval: 5000, function: on_sig_timeout, data: mainloop);
156 g_main_loop_run (loop: mainloop);
157 g_assert (sig_received);
158 sig_received = FALSE;
159 g_source_remove (tag: id);
160
161 /* Ensure we don't get double delivery */
162 g_timeout_add (interval: 500, function: exit_mainloop, data: mainloop);
163 g_main_loop_run (loop: mainloop);
164 g_assert (!sig_received);
165
166 /* Ensure that two sources for the same signal get it */
167 sig_counter = 0;
168 g_unix_signal_add (signum, handler: on_sig_received_2, user_data: mainloop);
169 g_unix_signal_add (signum, handler: on_sig_received_2, user_data: mainloop);
170 id = g_timeout_add (interval: 5000, function: on_sig_timeout, data: mainloop);
171
172 kill (pid: getpid (), sig: signum);
173 g_main_loop_run (loop: mainloop);
174 g_assert_cmpint (sig_counter, ==, 2);
175 g_source_remove (tag: id);
176
177 g_main_loop_unref (loop: mainloop);
178}
179
180static void
181test_sighup (void)
182{
183 test_signal (SIGHUP);
184}
185
186static void
187test_sigterm (void)
188{
189 test_signal (SIGTERM);
190}
191
192static void
193test_sighup_add_remove (void)
194{
195 guint id;
196 struct sigaction action;
197
198 sig_received = FALSE;
199 id = g_unix_signal_add (SIGHUP, handler: on_sig_received, NULL);
200 g_source_remove (tag: id);
201
202 sigaction (SIGHUP, NULL, oact: &action);
203 g_assert (action.sa_handler == SIG_DFL);
204}
205
206static gboolean
207nested_idle (gpointer data)
208{
209 GMainLoop *nested;
210 GMainContext *context;
211 GSource *source;
212
213 context = g_main_context_new ();
214 nested = g_main_loop_new (context, FALSE);
215
216 source = g_unix_signal_source_new (SIGHUP);
217 g_source_set_callback (source, func: on_sig_received, data: nested, NULL);
218 g_source_attach (source, context);
219 g_source_unref (source);
220
221 kill (pid: getpid (), SIGHUP);
222 g_main_loop_run (loop: nested);
223 g_assert_cmpint (sig_counter, ==, 1);
224
225 g_main_loop_unref (loop: nested);
226 g_main_context_unref (context);
227
228 return G_SOURCE_REMOVE;
229}
230
231static void
232test_sighup_nested (void)
233{
234 GMainLoop *mainloop;
235
236 mainloop = g_main_loop_new (NULL, FALSE);
237
238 sig_counter = 0;
239 sig_received = FALSE;
240 g_unix_signal_add (SIGHUP, handler: on_sig_received, user_data: mainloop);
241 g_idle_add (function: nested_idle, data: mainloop);
242
243 g_main_loop_run (loop: mainloop);
244 g_assert_cmpint (sig_counter, ==, 2);
245
246 g_main_loop_unref (loop: mainloop);
247}
248
249static gboolean
250on_sigwinch_received (gpointer data)
251{
252 GMainLoop *loop = (GMainLoop *) data;
253
254 sig_counter ++;
255
256 if (sig_counter == 1)
257 kill (pid: getpid (), SIGWINCH);
258 else if (sig_counter == 2)
259 g_main_loop_quit (loop);
260 else if (sig_counter > 2)
261 g_assert_not_reached ();
262
263 /* Increase the time window in which an issue could happen. */
264 g_usleep (G_USEC_PER_SEC);
265
266 return G_SOURCE_CONTINUE;
267}
268
269static void
270test_callback_after_signal (void)
271{
272 /* Checks that user signal callback is invoked *after* receiving a signal.
273 * In other words a new signal is never merged with the one being currently
274 * dispatched or whose dispatch had already finished. */
275
276 GMainLoop *mainloop;
277 GMainContext *context;
278 GSource *source;
279
280 sig_counter = 0;
281
282 context = g_main_context_new ();
283 mainloop = g_main_loop_new (context, FALSE);
284
285 source = g_unix_signal_source_new (SIGWINCH);
286 g_source_set_callback (source, func: on_sigwinch_received, data: mainloop, NULL);
287 g_source_attach (source, context);
288 g_source_unref (source);
289
290 g_assert_cmpint (sig_counter, ==, 0);
291 kill (pid: getpid (), SIGWINCH);
292 g_main_loop_run (loop: mainloop);
293 g_assert_cmpint (sig_counter, ==, 2);
294
295 g_main_loop_unref (loop: mainloop);
296 g_main_context_unref (context);
297}
298
299static void
300test_get_passwd_entry_root (void)
301{
302 struct passwd *pwd;
303 GError *local_error = NULL;
304
305 g_test_summary (summary: "Tests that g_unix_get_passwd_entry() works for a "
306 "known-existing username.");
307
308 pwd = g_unix_get_passwd_entry (user_name: "root", error: &local_error);
309 g_assert_no_error (local_error);
310
311 g_assert_cmpstr (pwd->pw_name, ==, "root");
312 g_assert_cmpuint (pwd->pw_uid, ==, 0);
313
314 g_free (mem: pwd);
315}
316
317static void
318test_get_passwd_entry_nonexistent (void)
319{
320 struct passwd *pwd;
321 GError *local_error = NULL;
322
323 g_test_summary (summary: "Tests that g_unix_get_passwd_entry() returns an error for a "
324 "nonexistent username.");
325
326 pwd = g_unix_get_passwd_entry (user_name: "thisusernamedoesntexist", error: &local_error);
327 g_assert_error (local_error, G_UNIX_ERROR, 0);
328 g_assert_null (pwd);
329
330 g_clear_error (err: &local_error);
331}
332
333int
334main (int argc,
335 char *argv[])
336{
337 g_test_init (argc: &argc, argv: &argv, NULL);
338
339 g_test_add_func (testpath: "/glib-unix/pipe", test_func: test_pipe);
340 g_test_add_func (testpath: "/glib-unix/error", test_func: test_error);
341 g_test_add_func (testpath: "/glib-unix/nonblocking", test_func: test_nonblocking);
342 g_test_add_func (testpath: "/glib-unix/sighup", test_func: test_sighup);
343 g_test_add_func (testpath: "/glib-unix/sigterm", test_func: test_sigterm);
344 g_test_add_func (testpath: "/glib-unix/sighup_again", test_func: test_sighup);
345 g_test_add_func (testpath: "/glib-unix/sighup_add_remove", test_func: test_sighup_add_remove);
346 g_test_add_func (testpath: "/glib-unix/sighup_nested", test_func: test_sighup_nested);
347 g_test_add_func (testpath: "/glib-unix/callback_after_signal", test_func: test_callback_after_signal);
348 g_test_add_func (testpath: "/glib-unix/get-passwd-entry/root", test_func: test_get_passwd_entry_root);
349 g_test_add_func (testpath: "/glib-unix/get-passwd-entry/nonexistent", test_func: test_get_passwd_entry_nonexistent);
350
351 return g_test_run();
352}
353

source code of gtk/subprojects/glib/glib/tests/unix.c