Finished and deployed CORBA sandbox separation
[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 #include "../client/vfs.h"
36
37
38 /* CONFIG: */
39
40 #define HEARTBEAT_SOURCE_CHECK_EVENTS (G_IO_IN|G_IO_PRI)
41 #define HEARTBEAT_SOURCE_CHECK_REVENTS (HEARTBEAT_SOURCE_CHECK_EVENTS|G_IO_ERR|G_IO_HUP|G_IO_NVAL)
42
43
44 gboolean validate_CORBA_Environment(const CORBA_Environment *evp)
45 {
46         g_return_val_if_fail(evp->_major==CORBA_NO_EXCEPTION,FALSE);
47
48         return TRUE;
49 }
50
51 CORBA_Environment captive_corba_ev;
52 CORBA_ORB captive_corba_orb;
53 PortableServer_POA captive_corba_poa;
54
55 static void corba_shutdown_atexit(void);
56
57 static gboolean corba_init(const char *pname,CORBA_Environment *evp,CORBA_ORB *orbp,PortableServer_POA *poap)
58 {
59 static gboolean done=FALSE;
60 int orb_argc=1;
61 gchar *orb_argv[]={
62                 (gchar *)captive_strdup_alloca(pname),
63                 NULL};
64
65         g_return_val_if_fail(evp!=NULL,FALSE);
66         g_return_val_if_fail(orbp!=NULL,FALSE);
67         g_return_val_if_fail(poap!=NULL,FALSE);
68
69         if (done)
70                 return TRUE;
71
72         /* Init 'ev' */
73         CORBA_exception_init(evp);
74
75         /* Init 'orb' */
76         *orbp=CORBA_ORB_init(&orb_argc,orb_argv,"orbit-local-orb",evp);
77         g_return_val_if_fail(*orbp!=CORBA_OBJECT_NIL,FALSE);
78         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
79
80         /* Init 'poa' */
81         *poap=(PortableServer_POA)CORBA_ORB_resolve_initial_references(*orbp,"RootPOA",evp);
82         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
83         {
84 PortableServer_POAManager poa_mgr;
85                 poa_mgr=PortableServer_POA__get_the_POAManager(*poap,evp);
86                 g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
87                 PortableServer_POAManager_activate(poa_mgr,evp);
88                 g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
89                 CORBA_Object_release((CORBA_Object)poa_mgr,evp);
90                 g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
91                 }
92
93         g_atexit(corba_shutdown_atexit);
94
95         done=TRUE;
96         return TRUE;
97 }
98
99
100 static CORBA_ORB heartbeat_source_callback_orb=CORBA_OBJECT_NIL;
101
102 gboolean corba_shutdown(CORBA_Environment *evp,CORBA_ORB *orbp,PortableServer_POA *poap)
103 {
104 PortableServer_POA poa;
105 CORBA_ORB orb;
106
107         g_return_val_if_fail(evp!=NULL,FALSE);
108         g_return_val_if_fail(orbp!=NULL,FALSE);
109         g_return_val_if_fail(poap!=NULL,FALSE);
110
111         /* Shutdown 'poa' */
112         poa=*poap;
113         *poap=CORBA_OBJECT_NIL;
114         CORBA_Object_release((CORBA_Object)poa,evp);
115         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
116
117         /* Shutdown 'orb' */
118         orb=*orbp;
119         *orbp=CORBA_OBJECT_NIL;
120         heartbeat_source_callback_orb=CORBA_OBJECT_NIL;
121         CORBA_ORB_destroy(orb,evp);
122         g_return_val_if_fail(validate_CORBA_Environment(evp),FALSE);
123
124         /* Shutdown 'ev' */
125         CORBA_exception_free(evp);
126
127         return TRUE;
128 }
129
130 static void corba_shutdown_atexit(void)
131 {
132 gboolean errbool;
133
134         errbool=corba_shutdown(&captive_corba_ev,&captive_corba_orb,&captive_corba_poa);
135         g_assert(errbool==TRUE);
136 }
137
138
139 void sandbox_child_shutdown(void)
140 {
141         /* Do not fail by passing logging messages to the master. */
142         impl_Captive_Vfs_registerGlibLogFunc_disable();
143
144         g_main_loop_quit(linc_main_get_loop());
145 }
146
147
148 static gboolean heartbeat_source_callback(gpointer data /* unused */)
149 {
150         g_return_val_if_fail(heartbeat_source_callback_orb!=CORBA_OBJECT_NIL,FALSE);    /* the source should be removed */
151
152         sandbox_child_shutdown();
153
154         return FALSE;   /* the source should be removed */
155 }
156
157
158 static void sandbox_child(int Vfs_IOR_fd_write,GSource *gsource,CaptiveVfsObject *child_captive_vfs_object) G_GNUC_NORETURN;
159 static void sandbox_child(int Vfs_IOR_fd_write,GSource *gsource,CaptiveVfsObject *child_captive_vfs_object)
160 {
161 Captive_Vfs Vfs_object;
162 gboolean errbool;
163 int errint;
164 guint errguint;
165
166         errint=close(0);        /* STDIN */
167         g_assert(errint==0);
168         errint=close(1);        /* STDOUT */
169         g_assert(errint==0);
170         errint=close(2);        /* STDERR */
171         g_assert(errint==0);
172
173         g_assert(captive_corba_child_options==NULL);
174         captive_corba_child_options=&child_captive_vfs_object->options;
175
176         heartbeat_source_callback_orb=captive_corba_orb;
177
178         /* linc_main_get_loop() makes sense only after corba_init() -> CORBA_ORB_init() */
179         errguint=g_source_attach(
180                         gsource,        /* source */
181                         g_main_loop_get_context(linc_main_get_loop())); /* context; NULL means 'default context' */
182         g_assert(errguint!=0);
183
184         /* Init 'Vfs_object' */
185         Vfs_object=impl_Captive_Vfs__create(captive_corba_poa,&captive_corba_ev);
186         g_assert(validate_CORBA_Environment(&captive_corba_ev));
187
188         /* pass IOR to our parent */
189         {
190 char *Vfs_IOR;
191                 Vfs_IOR=CORBA_ORB_object_to_string(captive_corba_orb,Vfs_object,&captive_corba_ev);
192                 g_assert(validate_CORBA_Environment(&captive_corba_ev));
193                 g_assert(Vfs_IOR!=NULL);
194                 errint=write(Vfs_IOR_fd_write,Vfs_IOR,strlen(Vfs_IOR)+1);
195                 g_assert((unsigned)errint==strlen(Vfs_IOR)+1);
196                 errint=close(Vfs_IOR_fd_write);
197                 g_assert(errint==0);
198                 CORBA_free(Vfs_IOR);
199                 }
200
201         /* CORBA_ORB_run() -> linc_main_loop_run() -> g_main_loop_run()
202          * and therefore we should be safe with glib events handling.
203          */
204         CORBA_ORB_run(captive_corba_orb,&captive_corba_ev);
205         g_assert(validate_CORBA_Environment(&captive_corba_ev));
206
207         /* Shutdown 'Vfs' servant */
208         CORBA_Object_release(Vfs_object,&captive_corba_ev);
209         g_assert(validate_CORBA_Environment(&captive_corba_ev));
210
211         errbool=corba_shutdown(&captive_corba_ev,&captive_corba_orb,&captive_corba_poa);
212         g_assert(errbool==TRUE);
213
214         _exit(EXIT_SUCCESS);
215 }
216
217 static void sandbox_parent
218                 (int Vfs_IOR_fd_read,Captive_Vfs *corba_Vfs_object_return,Captive_GlibLogFunc *corba_GlibLogFunc_object_return)
219 {
220 char *Vfs_IOR;
221 gsize Vfs_IOR_size;
222 Captive_Vfs Vfs_object;
223 Captive_GlibLogFunc GlibLogFunc_object;
224 int errint;
225
226         g_return_if_fail(Vfs_IOR_fd_read!=-1);
227         g_return_if_fail(corba_Vfs_object_return!=NULL);
228         g_return_if_fail(corba_GlibLogFunc_object_return!=NULL);
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(captive_corba_orb,Vfs_IOR,&captive_corba_ev);
238         g_assert(validate_CORBA_Environment(&captive_corba_ev));
239         g_free(Vfs_IOR);
240
241         /* Init 'GlibLogFunc_object' */
242         GlibLogFunc_object=impl_Captive_GlibLogFunc__create(captive_corba_poa,&captive_corba_ev);
243         g_assert(validate_CORBA_Environment(&captive_corba_ev));
244         Captive_Vfs_registerGlibLogFunc(Vfs_object,GlibLogFunc_object,&captive_corba_ev);
245         g_assert(validate_CORBA_Environment(&captive_corba_ev));
246
247         *corba_Vfs_object_return=Vfs_object;
248         *corba_GlibLogFunc_object_return=GlibLogFunc_object;
249 }
250
251 static gboolean heartbeat_source_prepare(GSource *source,gint *timeout)
252 {
253         *timeout=-1;
254
255         return FALSE;
256 }
257
258 static GPollFD heartbeat_source_check_gpollfd;
259
260 static gboolean heartbeat_source_check(GSource *source)
261 {
262         return !!(heartbeat_source_check_gpollfd.revents & HEARTBEAT_SOURCE_CHECK_REVENTS);
263 }
264
265 static gboolean heartbeat_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
266 {
267         g_assert(callback!=NULL);
268         return (*callback)(user_data);
269 }
270
271 static GSourceFuncs heartbeat_source_watch_funcs={
272                 heartbeat_source_prepare,
273                 heartbeat_source_check,
274                 heartbeat_source_dispatch,
275                 NULL,   /* finalize */
276                 };
277
278
279 gboolean captive_sandbox_spawn(CaptiveVfsObject *child_captive_vfs_object,
280                 Captive_Vfs *corba_Vfs_object_return,Captive_GlibLogFunc *corba_GlibLogFunc_object_return,int *parentheart_fds_1_return)
281 {
282 /* Vfs_IOR_fds[0] for reading by sandbox_parent() - client,
283  * Vfs_IOR_fds[1] for writing by sandbox_child()  - server
284  */
285 int Vfs_IOR_fds[2],parentheart_fds[2];
286 int errint;
287 gboolean errbool;
288
289         g_return_val_if_fail(child_captive_vfs_object!=NULL,FALSE);
290         g_return_val_if_fail(corba_Vfs_object_return!=NULL,FALSE);
291         g_return_val_if_fail(corba_GlibLogFunc_object_return!=NULL,FALSE);
292         g_return_val_if_fail(parentheart_fds_1_return!=NULL,FALSE);
293
294         errint=pipe(Vfs_IOR_fds);
295         g_return_val_if_fail(errint==0,FALSE);
296         errint=pipe(parentheart_fds);
297         g_return_val_if_fail(errint==0,FALSE);
298
299         /* We cannot initialize 'parent' and 'child' name specifically as
300          * we need to have persistence master and its children will be already
301          * CORBA-initialized during their spawn.
302          */
303         errbool=corba_init("captive-sandbox",&captive_corba_ev,&captive_corba_orb,&captive_corba_poa);
304         g_return_val_if_fail(errbool==TRUE,FALSE);
305
306         /* Currently never called anywhere:
307          *     errbool=corba_shutdown(&captive_corba_ev,&captive_corba_orb,&captive_corba_poa);
308          *     g_assert(errbool==TRUE);
309          */
310
311         switch (fork()) {
312                 case -1:        /* error */
313                         g_return_val_if_reached(FALSE);
314
315                 case 0: { /* child */
316 GSource *gsource;
317
318                         errint=close(Vfs_IOR_fds[0]);   /* close Vfs_IOR_fd_read */
319                         g_return_val_if_fail(errint==0,FALSE);
320                         errint=close(parentheart_fds[1]);       /* close parentheart_fd_write */
321                         g_return_val_if_fail(errint==0,FALSE);
322
323                         /* attach heartbeat_source_callback() to watch for any abnormalities
324                          * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
325                          */
326                         gsource=g_source_new(&heartbeat_source_watch_funcs,sizeof(GSource));
327                         g_return_val_if_fail(gsource!=NULL,FALSE);
328                         g_source_set_callback(
329                                         gsource,        /* source */
330                                         heartbeat_source_callback,      /* func */
331                                         NULL,   /* data */
332                                         NULL);  /* notify */
333                         heartbeat_source_check_gpollfd.fd=parentheart_fds[0];   /* parentheart_fd_read */
334                         heartbeat_source_check_gpollfd.events=HEARTBEAT_SOURCE_CHECK_EVENTS;
335                         heartbeat_source_check_gpollfd.revents=0;
336                         g_source_add_poll(gsource,&heartbeat_source_check_gpollfd);
337                         sandbox_child(Vfs_IOR_fds[1],gsource,child_captive_vfs_object); /* pass Vfs_IOR_fd_write */
338                         g_return_val_if_reached(FALSE);
339                         } /* NOTREACHED */
340
341                 default:        /* parent */
342                         errint=close(Vfs_IOR_fds[1]);   /* close Vfs_IOR_fd_write */
343                         g_return_val_if_fail(errint==0,FALSE);
344                         errint=close(parentheart_fds[0]);       /* close parentheart_fd_read */
345                         g_return_val_if_fail(errint==0,FALSE);
346                         errint=fcntl(parentheart_fds[1],F_SETFD,FD_CLOEXEC);
347                         /* This fcntl(2) may not be enough - some fork(2) may duplicate this
348                          * write descriptor and even if we do some process finish the child
349                          * may remain alone connected with some unknown fork(2)er from us.
350                          * Currently I am not aware such case would occur.
351                          */
352                         g_return_val_if_fail(errint==0,FALSE);
353                         *parentheart_fds_1_return=parentheart_fds[1];
354                         sandbox_parent(
355                                         Vfs_IOR_fds[0], /* Vfs_IOR_fd_read */
356                                         corba_Vfs_object_return,        /* corba_Vfs_object_return */
357                                         corba_GlibLogFunc_object_return);       /* corba_GlibLogFunc_object_return */
358                         /* 'parentheart_fds[1]' - parentheart_fd_write - is left open here */
359                         return TRUE;
360                 }
361         /* NOTREACHED */
362         g_return_val_if_reached(FALSE);
363 }
364
365
366 GnomeVFSResult captive_sandbox_parent_return_from_CORBA_Environment(CORBA_Environment *evp)
367 {
368 GnomeVFSResult r;
369
370         if (evp->_major==CORBA_NO_EXCEPTION)
371                 return GNOME_VFS_OK;
372
373         if (!strcmp(ex_Captive_GnomeVFSResultException,CORBA_exception_id(evp)))
374                 r=((Captive_GnomeVFSResultException *)CORBA_exception_value(evp))->gnome_vfs_result;
375         else {
376                 r=GNOME_VFS_ERROR_GENERIC;
377                 g_warning(_("CORBA Exception occured: id=\"%s\", value=%p"),
378                                 CORBA_exception_id(evp),CORBA_exception_value(evp));
379                 }
380         CORBA_exception_free(evp);
381
382         return r;
383 }
384
385
386 void captive_sandbox_child_GnomeVFSResultException_throw(CORBA_Environment *evp,GnomeVFSResult errvfsresult)
387 {
388 Captive_GnomeVFSResultException *gnome_vfs_result_exception;
389
390         g_return_if_fail(evp!=NULL);
391
392         gnome_vfs_result_exception=Captive_GnomeVFSResultException__alloc();
393         gnome_vfs_result_exception->gnome_vfs_result=errvfsresult;
394         CORBA_exception_set(evp,CORBA_USER_EXCEPTION,ex_Captive_GnomeVFSResultException,gnome_vfs_result_exception);
395 }