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 |
41 | handle_usr1 (int signum) |
42 | { |
43 | stop = TRUE; |
44 | } |
45 | |
46 | #endif |
47 | |
48 | static gboolean |
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 |
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: &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 | |
80 | static GMappedFile * |
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: &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 | |
100 | static gboolean |
101 | signal_parent (gpointer data) |
102 | { |
103 | #ifndef G_OS_WIN32 |
104 | kill (pid: parent_pid, SIGUSR1); |
105 | #endif |
106 | return G_SOURCE_REMOVE; |
107 | } |
108 | |
109 | static int |
110 | child_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 | |
137 | static void |
138 | test_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 | |
154 | static 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, 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 | |
185 | static void |
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, 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 | |
272 | static int |
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 (argv0: argv[0]); |
284 | |
285 | return 0; |
286 | } |
287 | |
288 | int |
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 (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 | |