1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 2005 Matthias Clasen
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library 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. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <glib.h>
21 #include <glib/gstdio.h>
22
23 #ifdef G_OS_UNIX
24 #include <unistd.h>
25 #endif
26 #ifdef G_OS_WIN32
27 #include <process.h>
28 #endif
29
30 static gboolean stop = FALSE;
31 static gint parent_pid;
32
33 /* Passing argc and argv through global variables */
34 static char **local_argv;
35
36 #ifndef G_OS_WIN32
37
38 static void
39 handle_usr1 (int signum)
40 {
41 stop = TRUE;
42 }
43
44 #endif
45
46 static gboolean
47 check_stop (gpointer data)
48 {
49 GMainLoop *loop = data;
50
51 #ifdef G_OS_WIN32
52 stop = g_file_test ("STOP", G_FILE_TEST_EXISTS);
53 #endif
54
55 if (stop)
56 g_main_loop_quit (loop);
57
58 return TRUE;
59 }
60
61 static void
62 write_or_die (const gchar *filename,
63 const gchar *contents,
64 gssize length)
65 {
66 GError *error = NULL;
67 gboolean result;
68
69 result = g_file_set_contents (filename, contents, length, &error);
70 g_assert_no_error (error);
71 g_assert_true (result);
72 }
73
74 static GMappedFile *
75 map_or_die (const gchar *filename,
76 gboolean writable)
77 {
78 GError *error = NULL;
79 GMappedFile *map;
80
81 map = g_mapped_file_new (filename, writable, &error);
82 g_assert_no_error (error);
83 g_assert_nonnull (map);
84
85 return map;
86 }
87
88 static void
89 signal_parent (gpointer data)
90 {
91 #ifndef G_OS_WIN32
92 kill (parent_pid, SIGUSR1);
93 #endif
94 }
95
96 static void
97 child_main (void)
98 {
99 GMappedFile *map;
100 GMainLoop *loop;
101 gchar *dir, *global_filename, *childname;
102
103 dir = g_get_current_dir ();
104 global_filename = g_build_filename (dir, "maptest", NULL);
105 childname = g_build_filename (dir, "mapchild", NULL);
106
107 parent_pid = atoi (local_argv[2]);
108 map = map_or_die (global_filename, FALSE);
109
110 #ifndef G_OS_WIN32
111 signal (SIGUSR1, handle_usr1);
112 #endif
113 loop = g_main_loop_new (NULL, FALSE);
114 g_idle_add (check_stop, loop);
115 g_idle_add_once (signal_parent, NULL);
116 g_main_loop_run (loop);
117
118 g_test_message ("test_child_private: received parent signal");
119
120 write_or_die (childname,
121 g_mapped_file_get_contents (map),
122 g_mapped_file_get_length (map));
123
124 g_free (childname);
125 g_free (global_filename);
126 g_free (dir);
127
128 signal_parent (NULL);
129 }
130
131 static void
132 test_mapping_flags (void)
133 {
134 GMappedFile *map;
135 gchar *dir, *global_filename;
136
137 dir = g_get_current_dir ();
138 global_filename = g_build_filename (dir, "maptest", NULL);
139
140 write_or_die (global_filename, "ABC", -1);
141
142 map = map_or_die (global_filename, FALSE);
143 g_assert_cmpint (g_mapped_file_get_length (map), ==, 3);
144 g_mapped_file_unref (map);
145
146 map = map_or_die (global_filename, TRUE);
147 g_assert_cmpint (g_mapped_file_get_length (map), ==, 3);
148 g_mapped_file_unref (map);
149 g_test_message ("test_mapping: ok");
150
151 /* Cleaning left over files */
152 g_remove ("maptest");
153
154 g_free (global_filename);
155 g_free (dir);
156 }
157
158 static void
159 test_private (void)
160 {
161 GError *error = NULL;
162 GMappedFile *map;
163 gboolean result;
164 gchar *buffer;
165 gsize len;
166 gchar *dir, *global_filename;
167
168 dir = g_get_current_dir ();
169 global_filename = g_build_filename (dir, "maptest", NULL);
170
171 write_or_die (global_filename, "ABC", -1);
172 map = map_or_die (global_filename, TRUE);
173
174 buffer = (gchar *)g_mapped_file_get_contents (map);
175 buffer[0] = '1';
176 buffer[1] = '2';
177 buffer[2] = '3';
178 g_mapped_file_unref (map);
179
180 result = g_file_get_contents (global_filename, &buffer, &len, &error);
181 g_assert_no_error (error);
182 g_assert_true (result);
183 g_assert_cmpint (len, ==, 3);
184 g_assert_cmpstr (buffer, ==, "ABC");
185 g_free (buffer);
186
187 g_free (global_filename);
188 g_free (dir);
189
190 /* Cleaning left over files */
191 g_remove ("maptest");
192
193 g_test_message ("test_private: ok");
194 }
195
196 static void
197 test_child_private (void)
198 {
199 GError *error = NULL;
200 GMappedFile *map;
201 gboolean result;
202 gchar *buffer;
203 gsize len;
204 gchar *child_argv[4];
205 GPid child_pid;
206 #ifndef G_OS_WIN32
207 GMainLoop *loop;
208 #endif
209 gchar pid[100];
210 gchar *dir, *global_filename, *childname;
211
212 #ifdef G_OS_WIN32
213 g_remove ("STOP");
214 g_assert_false (g_file_test ("STOP", G_FILE_TEST_EXISTS));
215 #endif
216
217 dir = g_get_current_dir ();
218 global_filename = g_build_filename (dir, "maptest", NULL);
219 childname = g_build_filename (dir, "mapchild", NULL);
220
221 write_or_die (global_filename, "ABC", -1);
222 map = map_or_die (global_filename, TRUE);
223
224 #ifndef G_OS_WIN32
225 signal (SIGUSR1, handle_usr1);
226 #endif
227
228 g_snprintf (pid, sizeof(pid), "%d", getpid ());
229 child_argv[0] = local_argv[0];
230 child_argv[1] = "mapchild";
231 child_argv[2] = pid;
232 child_argv[3] = NULL;
233
234 result = g_spawn_async (dir, child_argv, NULL,
235 0, NULL, NULL, &child_pid, &error);
236 g_assert_no_error (error);
237 g_assert_true (result);
238 g_test_message ("test_child_private: child spawned");
239
240 #ifndef G_OS_WIN32
241 loop = g_main_loop_new (NULL, FALSE);
242 g_idle_add (check_stop, loop);
243 g_main_loop_run (loop);
244 stop = FALSE;
245 #else
246 g_usleep (2000000);
247 #endif
248
249 g_test_message ("test_child_private: received first child signal");
250
251 buffer = (gchar *)g_mapped_file_get_contents (map);
252 buffer[0] = '1';
253 buffer[1] = '2';
254 buffer[2] = '3';
255 g_mapped_file_unref (map);
256
257 #ifndef G_OS_WIN32
258 kill (child_pid, SIGUSR1);
259 #else
260 g_file_set_contents ("STOP", "Hey there\n", -1, NULL);
261 #endif
262
263 #ifndef G_OS_WIN32
264 g_idle_add (check_stop, loop);
265 g_main_loop_run (loop);
266 #else
267 g_usleep (2000000);
268 #endif
269
270 g_test_message ("test_child_private: received second child signal");
271
272 result = g_file_get_contents (childname, &buffer, &len, &error);
273 g_assert_no_error (error);
274 g_assert_true (result);
275 g_assert_cmpint (len, ==, 3);
276 g_assert_cmpstr (buffer, ==, "ABC");
277 g_free (buffer);
278
279 g_free (childname);
280 g_free (global_filename);
281 g_free (dir);
282
283 /* Cleaning left over files */
284 g_remove ("mapchild");
285 g_remove ("maptest");
286
287 g_test_message ("test_child_private: ok");
288 }
289
290 int
291 main (int argc,
292 char *argv[])
293 {
294 #ifndef G_OS_WIN32
295 sigset_t sig_mask, old_mask;
296
297 sigemptyset (&sig_mask);
298 sigaddset (&sig_mask, SIGUSR1);
299 if (sigprocmask (SIG_UNBLOCK, &sig_mask, &old_mask) == 0)
300 {
301 if (sigismember (&old_mask, SIGUSR1))
302 g_test_message ("SIGUSR1 was blocked, unblocking it");
303 }
304 #endif
305
306 local_argv = argv;
307
308 if (argc > 1)
309 {
310 child_main ();
311 return EXIT_SUCCESS;
312 }
313
314 g_test_init (&argc, &argv, NULL);
315
316 g_test_add_func ("/mapping/flags", test_mapping_flags);
317 g_test_add_func ("/mapping/private", test_private);
318 g_test_add_func ("/mapping/private-child", test_child_private);
319
320 return g_test_run ();
321 }