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