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

source code of gtk/subprojects/glib/tests/mapping-test.c