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 | |
30 | static void |
31 | test_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 | |
55 | static void |
56 | test_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 | |
68 | static void |
69 | test_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 | |
100 | static gboolean sig_received = FALSE; |
101 | static gboolean sig_timeout = FALSE; |
102 | static int sig_counter = 0; |
103 | |
104 | static gboolean |
105 | on_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 | |
114 | static gboolean |
115 | on_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 | |
123 | static gboolean |
124 | exit_mainloop (gpointer data) |
125 | { |
126 | GMainLoop *loop = data; |
127 | g_main_loop_quit (loop); |
128 | return G_SOURCE_REMOVE; |
129 | } |
130 | |
131 | static gboolean |
132 | on_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 | |
142 | static void |
143 | test_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 | |
180 | static void |
181 | test_sighup (void) |
182 | { |
183 | test_signal (SIGHUP); |
184 | } |
185 | |
186 | static void |
187 | test_sigterm (void) |
188 | { |
189 | test_signal (SIGTERM); |
190 | } |
191 | |
192 | static void |
193 | test_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 | |
206 | static gboolean |
207 | nested_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 | |
231 | static void |
232 | test_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 | |
249 | static gboolean |
250 | on_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 | |
269 | static void |
270 | test_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 | |
299 | static void |
300 | test_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 | |
317 | static void |
318 | test_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 | |
333 | int |
334 | main (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 | |