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
32 static gchar *dir, *filename, *displayname, *childname;
33
34 static gboolean stop = FALSE;
35
36 static gint parent_pid;
37
38 #ifndef G_OS_WIN32
39
40 static void
handle_usr1(int signum)41 handle_usr1 (int signum)
42 {
43 stop = TRUE;
44 }
45
46 #endif
47
48 static gboolean
check_stop(gpointer data)49 check_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
63 static void
write_or_die(const gchar * filename,const gchar * contents,gssize length)64 write_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))
72 {
73 displayname = g_filename_display_name (childname);
74 g_print ("failed to write '%s': %s\n",
75 displayname, error->message);
76 exit (1);
77 }
78 }
79
80 static GMappedFile *
map_or_die(const gchar * filename,gboolean writable)81 map_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);
89 if (!map)
90 {
91 displayname = g_filename_display_name (childname);
92 g_print ("failed to map '%s' non-writable, shared: %s\n",
93 displayname, error->message);
94 exit (1);
95 }
96
97 return map;
98 }
99
100 static gboolean
signal_parent(gpointer data)101 signal_parent (gpointer data)
102 {
103 #ifndef G_OS_WIN32
104 kill (parent_pid, SIGUSR1);
105 #endif
106 return G_SOURCE_REMOVE;
107 }
108
109 static int
child_main(int argc,char * argv[])110 child_main (int argc, char *argv[])
111 {
112 GMappedFile *map;
113 GMainLoop *loop;
114
115 parent_pid = atoi (argv[2]);
116 map = map_or_die (filename, FALSE);
117
118 #ifndef G_OS_WIN32
119 signal (SIGUSR1, handle_usr1);
120 #endif
121 loop = g_main_loop_new (NULL, FALSE);
122 g_idle_add (check_stop, loop);
123 g_idle_add (signal_parent, NULL);
124 g_main_loop_run (loop);
125
126 g_message ("test_child_private: received parent signal");
127
128 write_or_die (childname,
129 g_mapped_file_get_contents (map),
130 g_mapped_file_get_length (map));
131
132 signal_parent (NULL);
133
134 return 0;
135 }
136
137 static void
test_mapping(void)138 test_mapping (void)
139 {
140 GMappedFile *map;
141
142 write_or_die (filename, "ABC", -1);
143
144 map = map_or_die (filename, FALSE);
145 g_assert (g_mapped_file_get_length (map) == 3);
146 g_mapped_file_free (map);
147
148 map = map_or_die (filename, TRUE);
149 g_assert (g_mapped_file_get_length (map) == 3);
150 g_mapped_file_free (map);
151 g_message ("test_mapping: ok");
152 }
153
154 static void
test_private(void)155 test_private (void)
156 {
157 GError *error = NULL;
158 GMappedFile *map;
159 gchar *buffer;
160 gsize len;
161
162 write_or_die (filename, "ABC", -1);
163 map = map_or_die (filename, TRUE);
164
165 buffer = (gchar *)g_mapped_file_get_contents (map);
166 buffer[0] = '1';
167 buffer[1] = '2';
168 buffer[2] = '3';
169 g_mapped_file_free (map);
170
171 if (!g_file_get_contents (filename, &buffer, &len, &error))
172 {
173 g_print ("failed to read '%s': %s\n",
174 displayname, error->message);
175 exit (1);
176
177 }
178 g_assert (len == 3);
179 g_assert (strcmp (buffer, "ABC") == 0);
180 g_free (buffer);
181
182 g_message ("test_private: ok");
183 }
184
185 static void
test_child_private(gchar * argv0)186 test_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, "ABC", -1);
205 map = map_or_die (filename, TRUE);
206
207 #ifndef G_OS_WIN32
208 signal (SIGUSR1, handle_usr1);
209 #endif
210
211 g_snprintf (pid, sizeof(pid), "%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 (dir, child_argv, NULL,
217 0, NULL, NULL, &child_pid, &error))
218 {
219 g_print ("failed to spawn child: %s\n",
220 error->message);
221 exit (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 (check_stop, 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 (map);
237 buffer[0] = '1';
238 buffer[1] = '2';
239 buffer[2] = '3';
240 g_mapped_file_free (map);
241
242 #ifndef G_OS_WIN32
243 kill (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 (check_stop, 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 (childname, &buffer, &len, &error))
258 {
259 gchar *name;
260
261 name = g_filename_display_name (childname);
262 g_print ("failed to read '%s': %s\n", name, error->message);
263 exit (1);
264 }
265 g_assert (len == 3);
266 g_assert (strcmp (buffer, "ABC") == 0);
267 g_free (buffer);
268
269 g_message ("test_child_private: ok");
270 }
271
272 static int
parent_main(int argc,char * argv[])273 parent_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 (argv[0]);
284
285 return 0;
286 }
287
288 int
main(int argc,char * argv[])289 main (int argc,
290 char *argv[])
291 {
292 int ret;
293 #ifndef G_OS_WIN32
294 sigset_t sig_mask, old_mask;
295
296 sigemptyset (&sig_mask);
297 sigaddset (&sig_mask, SIGUSR1);
298 if (sigprocmask (SIG_UNBLOCK, &sig_mask, &old_mask) == 0)
299 {
300 if (sigismember (&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 (dir, "maptest", NULL);
307 displayname = g_filename_display_name (filename);
308 childname = g_build_filename (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 (childname);
316 g_free (filename);
317 g_free (displayname);
318 g_free (dir);
319
320 return ret;
321 }
322