+libcaptive/sandbox/
[captive.git] / src / libcaptive / sandbox / split.c
1 /* $Id$
2  * Connection of captive-vfs interface through CORBA/ORBit
3  * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program 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
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "split.h"      /* self */
23 #include "sandbox.h"
24 #include "server-GlibLogFunc.h"
25 #include "captive/macros.h"
26 #include <glib/gmacros.h>
27 #include <stdlib.h>
28 #include "captive/rtl-file.h"
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <linc/linc.h>  /* for linc_main_get_loop() */
32 #include "server-GlibLogFunc.h"
33 #include "server-Directory.h"
34 #include "server-VFS.h"
35
36
37 /* CONFIG: */
38
39 #define HEARTBEAT_SOURCE_CHECK_EVENTS (G_IO_IN|G_IO_PRI)
40 #define HEARTBEAT_SOURCE_CHECK_REVENTS (HEARTBEAT_SOURCE_CHECK_EVENTS|G_IO_ERR|G_IO_HUP|G_IO_NVAL)
41
42
43 gboolean validate_CORBA_Environment(const CORBA_Environment *evp)
44 {
45         g_return_val_if_fail(evp->_major==CORBA_NO_EXCEPTION,FALSE);
46
47         return TRUE;
48 }
49
50
51 static gboolean corba_init(const char *pname,CORBA_Environment *evp,CORBA_ORB *orbp,PortableServer_POA *poap)
52 {
53 int orb_argc=1;
54 gchar *orb_argv[]={
55                 (gchar *)captive_strdup_alloca(pname),
56                 NULL};
57
58         g_return_val_if_fail(evp!=NULL,FALSE);
59         g_return_val_if_fail(orbp!=NULL,FALSE);
60         g_return_val_if_fail(poap!=NULL,FALSE);
61
62         /* Init 'ev' */
63         CORBA_exception_init(evp);
64
65         /* Init 'orb' */
66         *orbp=CORBA_ORB_init(&orb_argc,orb_argv,"orbit-local-orb",evp);
67         g_return_val_if_fail(*orbp!=CORBA_OBJECT_NIL,FALSE);
68         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
69
70         /* Init 'poa' */
71         *poap=(PortableServer_POA)CORBA_ORB_resolve_initial_references(*orbp,"RootPOA",evp);
72         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
73         {
74 PortableServer_POAManager poa_mgr;
75                 poa_mgr=PortableServer_POA__get_the_POAManager(*poap,evp);
76                 g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
77                 PortableServer_POAManager_activate(poa_mgr,evp);
78                 g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
79                 CORBA_Object_release((CORBA_Object)poa_mgr,evp);
80                 g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
81                 }
82
83         return TRUE;
84 }
85
86
87 static CORBA_ORB heartbeat_source_callback_orb=CORBA_OBJECT_NIL;
88
89 static gboolean corba_shutdown(CORBA_Environment *evp,CORBA_ORB *orbp,PortableServer_POA *poap)
90 {
91 PortableServer_POA poa;
92 CORBA_ORB orb;
93
94         g_return_val_if_fail(evp!=NULL,FALSE);
95         g_return_val_if_fail(orbp!=NULL,FALSE);
96         g_return_val_if_fail(poap!=NULL,FALSE);
97
98         /* Shutdown 'poa' */
99         poa=*poap;
100         *poap=CORBA_OBJECT_NIL;
101         CORBA_Object_release((CORBA_Object)poa,evp);
102         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
103
104         /* Shutdown 'orb' */
105         orb=*orbp;
106         *orbp=CORBA_OBJECT_NIL;
107         heartbeat_source_callback_orb=CORBA_OBJECT_NIL;
108         CORBA_ORB_destroy(orb,evp);
109         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
110
111         /* Shutdown 'ev' */
112         CORBA_exception_free(evp);
113
114         return TRUE;
115 }
116
117
118 static gboolean corba_servant_object_destroy(PortableServer_POA poa,CORBA_Object reference,CORBA_Environment *evp)
119 {
120 PortableServer_ObjectId *objid;
121 PortableServer_Servant servant;
122
123         g_return_val_if_fail(poa!=CORBA_OBJECT_NIL,FALSE);
124         g_return_val_if_fail(reference!=CORBA_OBJECT_NIL,FALSE);
125         g_return_val_if_fail(evp!=NULL,FALSE);
126
127         objid=PortableServer_POA_reference_to_id(poa,reference,evp);
128         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
129         servant=PortableServer_POA_reference_to_servant(poa,reference,evp);
130         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
131         PortableServer_POA_deactivate_object(poa,objid,evp);
132         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
133         CORBA_free(objid);
134         (*((PortableServer_ServantBase *)servant)->vepv[0]->finalize)(servant,evp);
135         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
136         g_free(servant);
137
138         return TRUE;
139 }
140
141
142 static gboolean heartbeat_source_callback(gpointer data /* unused */)
143 {
144 CORBA_Environment ev;
145
146         g_return_val_if_fail(heartbeat_source_callback_orb!=CORBA_OBJECT_NIL,FALSE);    /* the source should be removed */
147
148         CORBA_exception_init(&ev);
149         /* CORBA_ORB_shutdown() is not enough as 'init_level' still >0 */
150         CORBA_ORB_destroy(
151                         heartbeat_source_callback_orb,  /* orb */
152                         &ev);   /* ev */
153         g_assert(validate_CORBA_Environment(&ev));
154         CORBA_exception_free(&ev);
155
156         return FALSE;   /* the source should be removed */
157 }
158
159
160 static void sandbox_child(int VFS_IOR_fd_write,GSource *gsource) G_GNUC_NORETURN;
161 static void sandbox_child(int VFS_IOR_fd_write,GSource *gsource)
162 {
163 CORBA_Environment ev;
164 CORBA_ORB orb;
165 PortableServer_POA poa;
166 Captive_VFS VFS_object;
167 gboolean errbool;
168 int errint;
169 guint errguint;
170
171         errbool=corba_init("sandbox_child",&ev,&orb,&poa);
172         g_assert(errbool==TRUE);
173         heartbeat_source_callback_orb=orb;
174
175         /* linc_main_get_loop() makes sense only after corba_init() -> CORBA_ORB_init() */
176         errguint=g_source_attach(
177                         gsource,        /* source */
178                         g_main_loop_get_context(linc_main_get_loop())); /* context; NULL means 'default context' */
179         g_assert(errguint!=0);
180
181         /* Init 'VFS_object' */
182         VFS_object=impl_Captive_VFS__create(poa,&ev);
183         g_assert(validate_CORBA_Environment(&ev));
184
185         /* pass IOR to our parent */
186         {
187 char *VFS_IOR;
188                 VFS_IOR=CORBA_ORB_object_to_string(orb,VFS_object,&ev);
189                 g_assert(validate_CORBA_Environment(&ev));
190                 g_assert(VFS_IOR!=NULL);
191                 errint=write(VFS_IOR_fd_write,VFS_IOR,strlen(VFS_IOR)+1);
192                 g_assert((unsigned)errint==strlen(VFS_IOR)+1);
193                 errint=close(VFS_IOR_fd_write);
194                 g_assert(errint==0);
195                 CORBA_free(VFS_IOR);
196                 }
197
198         /* CORBA_ORB_run() -> linc_main_loop_run() -> g_main_loop_run()
199          * and therefore we should be safe with glib events handling.
200          */
201         CORBA_ORB_run(orb,&ev);
202         g_assert(validate_CORBA_Environment(&ev));
203
204         /* Shutdown 'VFS' servant */
205         errbool=corba_servant_object_destroy(poa,VFS_object,&ev);
206         g_assert(errbool==TRUE);
207
208         errbool=corba_shutdown(&ev,&orb,&poa);
209         g_assert(errbool==TRUE);
210
211         _exit(EXIT_SUCCESS);
212 }
213
214 static void sandbox_parent(int VFS_IOR_fd_read)
215 {
216 CORBA_Environment ev;
217 CORBA_ORB orb;
218 PortableServer_POA poa;
219 gboolean errbool;
220 char *VFS_IOR;
221 gsize VFS_IOR_size;
222 Captive_Directory directory_object;
223 Captive_VFS VFS_object;
224 Captive_GlibLogFunc GlibLogFunc_object;
225 int errint;
226
227         errbool=corba_init("sandbox_parent",&ev,&orb,&poa);
228         g_return_if_fail(errbool==TRUE);
229
230         VFS_IOR=captive_rtl_file_read(VFS_IOR_fd_read,&VFS_IOR_size);
231         g_assert(VFS_IOR!=NULL);
232         g_assert(VFS_IOR_size>=1);
233         g_assert(memchr(VFS_IOR,0,VFS_IOR_size)==VFS_IOR+(VFS_IOR_size-1));     /* check exactly for 0-terminated string */
234         errint=close(VFS_IOR_fd_read);
235         g_assert(errint==0);
236
237         VFS_object=CORBA_ORB_string_to_object(orb,VFS_IOR,&ev);
238         g_assert(validate_CORBA_Environment(&ev));
239         g_free(VFS_IOR);
240
241         /* Init 'GlibLogFunc_object' */
242         GlibLogFunc_object=impl_Captive_GlibLogFunc__create(poa,&ev);
243         g_assert(validate_CORBA_Environment(&ev));
244         Captive_VFS_registerGlibLogFunc(VFS_object,GlibLogFunc_object,&ev);
245         g_assert(validate_CORBA_Environment(&ev));
246
247         directory_object=Captive_VFS_openDirectory(VFS_object,"/directory",&ev);
248         g_assert(validate_CORBA_Environment(&ev));
249         puts("TEST DONE");
250
251         /* Shutdown 'GlibLogFunc' servant */
252         errbool=corba_servant_object_destroy(poa,GlibLogFunc_object,&ev);
253         g_assert(errbool==TRUE);
254
255         CORBA_Object_release(VFS_object,&ev);
256         g_assert(validate_CORBA_Environment(&ev));
257
258         errbool=corba_shutdown(&ev,&orb,&poa);
259         g_assert(errbool==TRUE);
260 }
261
262 static gboolean heartbeat_source_prepare(GSource *source,gint *timeout)
263 {
264         *timeout=-1;
265
266         return FALSE;
267 }
268
269 static GPollFD heartbeat_source_check_gpollfd;
270
271 static gboolean heartbeat_source_check(GSource *source)
272 {
273         return !!(heartbeat_source_check_gpollfd.revents & HEARTBEAT_SOURCE_CHECK_REVENTS);
274 }
275
276 static gboolean heartbeat_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
277 {
278         g_assert(callback!=NULL);
279         return (*callback)(user_data);
280 }
281
282 static GSourceFuncs heartbeat_source_watch_funcs={
283                 heartbeat_source_prepare,
284                 heartbeat_source_check,
285                 heartbeat_source_dispatch,
286                 NULL,   /* finalize */
287                 };
288
289
290 void captive_sandbox_init(void)
291 {
292 /* VFS_IOR_fds[0] for reading by sandbox_parent() - client,
293  * VFS_IOR_fds[1] for writing by sandbox_child()  - server
294  */
295 int VFS_IOR_fds[2],parentheart_fds[2];
296 int errint;
297
298         errint=pipe(VFS_IOR_fds);
299         g_assert(errint==0);
300         errint=pipe(parentheart_fds);
301         g_assert(errint==0);
302         switch (fork()) {
303                 case -1:        /* error */
304                         g_assert_not_reached();
305
306                 case 0: { /* child */
307 GSource *gsource;
308
309                         errint=close(VFS_IOR_fds[0]);   /* close VFS_IOR_fd_read */
310                         g_assert(errint==0);
311                         errint=close(parentheart_fds[1]);       /* close parentheart_fd_write */
312                         g_assert(errint==0);
313
314                         /* attach heartbeat_source_callback() to watch for any abnormalities
315                          * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
316                          */
317                         gsource=g_source_new(&heartbeat_source_watch_funcs,sizeof(GSource));
318                         g_assert(gsource!=NULL);
319                         g_source_set_callback(
320                                         gsource,        /* source */
321                                         heartbeat_source_callback,      /* func */
322                                         NULL,   /* data */
323                                         NULL);  /* notify */
324                         heartbeat_source_check_gpollfd.fd=parentheart_fds[0];   /* parentheart_fd_read */
325                         heartbeat_source_check_gpollfd.events=HEARTBEAT_SOURCE_CHECK_EVENTS;
326                         heartbeat_source_check_gpollfd.revents=0;
327                         g_source_add_poll(gsource,&heartbeat_source_check_gpollfd);
328                         sandbox_child(VFS_IOR_fds[1],gsource);  /* pass VFS_IOR_fd_write */
329                         } /* NOTREACHED */
330
331                 default:        /* parent */
332                         errint=close(VFS_IOR_fds[1]);   /* close VFS_IOR_fd_write */
333                         g_assert(errint==0);
334                         errint=close(parentheart_fds[0]);       /* close parentheart_fd_read */
335                         g_assert(errint==0);
336                         errint=fcntl(parentheart_fds[1],F_SETFD,FD_CLOEXEC);
337                         /* This fcntl(2) may not be enough - some fork(2) may duplicate this
338                          * write descriptor and even if we do some process finish the child
339                          * may remain alone connected with some unknown fork(2)er from us.
340                          * Currently I am not aware such case would occur.
341                          */
342                         g_assert(errint==0);
343                         sandbox_parent(VFS_IOR_fds[0]); /* pass VFS_IOR_fd_read, it will be closed there */
344                         break;
345                 }
346         /* 'parentheart_fds[1]' - parentheart_fd_write - is left open here */
347 }