+Option '--debug-messages' (default off) - variable 'captive_debug_messages'
[captive.git] / src / libcaptive / client / init.c
1 /* $Id$
2  * Init and cleanup code of libcaptive to be called by client application
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 "captive/client.h"     /* self */
23 #include "captive/ldr.h"
24 #include "captive/ldr_exports.h"
25 #include "captive/unicode.h"
26 #include "captive/rtl-file.h"
27 #include <glib/gtypes.h>
28 #include <glib/gmessages.h>
29 #include "reactos/internal/ldr.h"
30 #include "reactos/napi/types.h"
31 #include "reactos/internal/kd.h"        /* for KDB_LOADDRIVER_HOOK */
32 #include <fcntl.h>
33 #include <sys/mman.h>   /* for PROT_READ, MAP_SHARED */
34 #include "reactos/ddk/kefuncs.h"        /* for KeInitializeSpinLock() */
35 #include "reactos/internal/ntoskrnl.h"  /* for RtlpInitNlsTables() and IoInit() */
36 #include "reactos/internal/ps.h"        /* for PsInitProcessManagment() and PsInitThreadManagment() */
37 #include "reactos/ddk/iofuncs.h"        /* for IoCreateFile() */
38 #include "captive/storage.h"
39 #include "captive/signal.h"     /* for captive_signal_init() */
40 #include "reactos/ddk/psfuncs.h"        /* for PsGetCurrentThread() */
41 #include <stdio.h>
42 #include <popt.h>
43 #include <glib/gstrfuncs.h>
44 #include <glib/glist.h>
45 #include <glib/gutils.h>        /* for g_atexit() */
46 #include "giochannel-blind.h"
47
48
49 /* Are we initialized? */
50 static gboolean active;
51
52 /* Module of fs module itself loaded by captive_w32_init() */
53 static PMODULE_OBJECT ModuleObject;
54
55 /* Driver in fs module loaded by captive_w32_init() */
56 static DRIVER_OBJECT DriverObject;
57
58 /* Structure holding the pointer to the toplevel IRP */
59 static TOP_LEVEL_IRP TopLevelIrp;       /* TODO:thread */
60
61
62 gchar *captive_option_filesystem;
63 enum captive_option_rwmode captive_option_rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
64 enum captive_option_media captive_option_media=CAPTIVE_OPTION_MEDIA_DISK;
65 gboolean captive_debug_messages=FALSE;
66 GIOChannel *captive_image_iochannel;
67 /* Non-blinded original GIOChannel for flushing during shutdown. */
68 static GIOChannel *captive_image_iochannel_orig;
69 guint64 captive_image_size;
70 static GList *captive_load_module;
71
72
73 static gchar *captive_popt_optarg;
74
75
76 static void arg_filesystem(void)
77 {
78         g_free(captive_option_filesystem);
79         captive_option_filesystem=g_strdup(captive_popt_optarg);
80 }
81
82 static void arg_load_module(void)
83 {
84         captive_load_module=g_list_append(captive_load_module,g_strdup(captive_popt_optarg));
85 }
86
87 static void arg_ro(void)
88 {
89         captive_option_rwmode=CAPTIVE_OPTION_RWMODE_RO;
90 }
91 static void arg_blind(void)
92 {
93         captive_option_rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
94 }
95 static void arg_rw(void)
96 {
97         captive_option_rwmode=CAPTIVE_OPTION_RWMODE_RW;
98 }
99
100 static void arg_cdrom(void)
101 {
102         captive_option_media=CAPTIVE_OPTION_MEDIA_CDROM;
103 }
104 static void arg_disk(void)
105 {
106         captive_option_media=CAPTIVE_OPTION_MEDIA_DISK;
107 }
108
109 static void arg_debug_messages(void)
110 {
111         captive_debug_messages=TRUE;
112 }
113
114
115 static void captive_popt_callback
116                 (poptContext con,enum poptCallbackReason reason,const struct poptOption *opt,const char *arg,const void *data);
117
118 const struct poptOption captive_popt[]={
119                 { argInfo:POPT_ARG_INTL_DOMAIN, arg:(void *)PACKAGE },
120                 { argInfo:POPT_ARG_CALLBACK,    arg:(void *)captive_popt_callback },
121 #define POPT_OFFSET 2
122 #define CAPTIVE_POPT(longname,argInfoP,argP,descripP,argDescripP) \
123                 { \
124                         longName: (longname), \
125                         shortName: 0, \
126                         argInfo: (argInfoP), \
127                         arg: (void *)argP, \
128                         val: 0, \
129                         descrip: (descripP), \
130                         argDescrip: (argDescripP), \
131                 }
132 #define CAPTIVE_POPT_STRING(longname,descripP,argDescripP) \
133                 CAPTIVE_POPT(longname,POPT_ARG_STRING,&captive_popt_optarg,descripP,argDescripP)
134 #define CAPTIVE_POPT_NONE(longname,descripP) \
135                 CAPTIVE_POPT(longname,POPT_ARG_NONE  ,NULL                ,descripP,NULL       )
136
137                 CAPTIVE_POPT_STRING("filesystem"    ,N_("Path to .sys or .so filesystem module file"),N_("pathname")),
138                 CAPTIVE_POPT_STRING("load-module"   ,N_("Path to any W32 module to load w/o initialization"),N_("pathname")),
139                 CAPTIVE_POPT_NONE(  "ro"            ,N_("Read/write mode: Any write access will be forbidden")),
140                 CAPTIVE_POPT_NONE(  "blind"         ,N_("Read/write mode: All writes are just simulated in memory (default)")),
141                 CAPTIVE_POPT_NONE(  "rw"            ,N_("Read/write mode: Write directly to the image file/device")),
142                 CAPTIVE_POPT_NONE(  "cdrom"         ,N_("Media type: CD-ROM")),
143                 CAPTIVE_POPT_NONE(  "disk"          ,N_("Media type: Disk (default)")),
144                 CAPTIVE_POPT_NONE(  "debug-messages",N_("Turn on debugging messages")),
145
146 #undef CAPTIVE_POPT_NONE
147 #undef CAPTIVE_POPT_STRING
148 #undef CAPTIVE_POPT
149                 POPT_TABLEEND
150                 };
151
152 static const struct poptOption captive_popt_standalone[]={
153                 CAPTIVE_POPT_INCLUDE,
154                 POPT_AUTOHELP
155                 POPT_TABLEEND
156                 };
157
158
159 static void (*const popt_func_table[])(void)={
160                 arg_filesystem,
161                 arg_load_module,
162                 arg_ro,
163                 arg_blind,
164                 arg_rw,
165                 arg_cdrom,
166                 arg_disk,
167                 arg_debug_messages,
168                 };
169
170
171 /* poptCallbackType captive_popt_callback */
172 static void captive_popt_callback
173                 (poptContext con,enum poptCallbackReason reason,const struct poptOption *opt,const char *arg,const void *data)
174 {
175 gint funci;
176
177         g_return_if_fail(reason==POPT_CALLBACK_REASON_OPTION);
178
179         funci=(opt-(captive_popt+POPT_OFFSET));
180         g_return_if_fail(funci>=0);
181         g_return_if_fail(funci<(gint)G_N_ELEMENTS(popt_func_table));
182         if (popt_func_table[funci])
183                 (*popt_func_table[funci])();
184         free(captive_popt_optarg);
185         captive_popt_optarg=NULL;       /* sanity, shouldn't be needed */
186 }
187
188
189 static gboolean captive_w32_init(void)
190 {
191 NTSTATUS err;
192 gboolean errbool;
193 GIOStatus erriostatus;
194
195         g_return_val_if_fail(captive_option_filesystem!=NULL,FALSE);
196
197         erriostatus=g_io_channel_set_encoding(captive_image_iochannel,
198                         NULL,   /* encoding; force binary data */
199                         NULL);  /* error */
200         g_assert(erriostatus==G_IO_STATUS_NORMAL);
201
202         /* captive_giochannel_size() only _after_ g_io_channel_set_encoding() ! */
203         captive_image_size=captive_giochannel_size(captive_image_iochannel);
204         g_return_val_if_fail(captive_image_size>0,FALSE);
205
206         /* Initialize 'FsRtlLegalAnsiCharacterArray'. */
207         captive_FsRtlLegalAnsiCharacterArray_init();
208
209         /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() begins. */
210         /* ExpInitializeExecutive(); */
211                 /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() begins
212                  * here as the rest of the function does a lot of hardware initializations.
213                  */
214                 /* LdrInit1(); */
215                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() begins. */
216                         InitializeListHead(&ModuleTextListHead);
217                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() ends. */
218                 KeLowerIrql(DISPATCH_LEVEL);
219                 /*...*/
220                 KeLowerIrql(PASSIVE_LEVEL);
221                 /*...*/
222                 /* create default nls tables */
223                 RtlpInitNlsTables();
224                 /*...*/
225                 ObInit();
226                 /*...*/
227                 /* PiInitProcessManager(); */
228                         /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() begins. */
229                         PsInitProcessManagment();
230                         PsInitThreadManagment();
231                         /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() ends. */
232                 /*...*/
233                 IoInit();
234                 /*...*/
235                 /* LdrInitModuleManagement(); */
236                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement() begins
237                          * here as the rest "Create module object for {NTOSKRNL,HAL}"
238                          * is dependent on {NTOSKRNL,HAL} PE image headers not provided by libcaptive.
239                          */
240                         /* Initialize the module list and spinlock */
241                         InitializeListHead(&ModuleListHead);
242                         KeInitializeSpinLock(&ModuleListLock);
243                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement ends. */
244                 /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() ends. */
245         /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() ends. */
246
247         /* Simulate our PE headers and export the symbols of our complete libraries */
248         captive_kernel_exports();
249
250         errbool=captive_cdrom_init();
251         g_return_val_if_fail(errbool==TRUE,FALSE);
252         errbool=captive_disk_init();
253         g_return_val_if_fail(errbool==TRUE,FALSE);
254
255         while (captive_load_module) {
256 gchar *modulename=captive_load_module->data;
257 PMODULE_OBJECT ModuleObject_tmp;
258 NTSTATUS err;
259
260                 /* load the module */
261                 err=LdrLoadModule(
262                                 captive_utf8_to_UnicodeString_alloca(modulename),       /* ModuleName */
263                                 &ModuleObject_tmp);     /* ModuleObjectp */
264                 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
265
266                 captive_load_module=g_list_remove(captive_load_module,modulename);
267                 }
268
269         /* Patch 'ntoskrnl.exe' loaded by 'captive_load_module' above. */
270         captive_kernel_patches();
271
272         /* set TopLevelIrp() - FIXME: where is it set by native reactos? */
273         PsGetCurrentThread()->TopLevelIrp=&TopLevelIrp; /* otherwise Io{Get,Set}TopLevelIrp() would SIGSEGV */
274
275         /* Begin possible handling of foreign W32 binary code here */
276         /* If you want to disable SIGSEGV handler if not needed:
277          *      if (ModuleObject->Flags & MODULE_FLAG_PE)
278          */
279         captive_signal_init();
280
281         /* You must have already captive_signal_init() passed here as the module may
282          * call some functions from W32 ntoskrnl.exe.
283          */
284         err=captive_LdrpLoadAndCallImage(
285                         &ModuleObject,  /* ModuleObjectp */
286                         captive_utf8_to_UnicodeString_alloca(captive_option_filesystem),        /* ModuleName */
287                         &DriverObject,  /* DriverEntry_DriverObject */
288                         captive_utf8_to_UnicodeString_alloca("\\captive\\filesystem")); /* DriverEntry_RegistryPath */
289         g_return_val_if_fail(NT_SUCCESS(err),FALSE);
290
291         return TRUE;
292 }
293
294
295 static void captive_shutdown_atexit(void);
296
297
298 static void     log_discard_func(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
299 {
300         /* NOP */
301 }
302
303
304 /**
305  * captive_init:
306  * @captive_args: String with possible options to parse by popt.
307  * %NULL value is permitted.
308  * @image_iochannel: Host OS file of the disk image to mount.
309  * %NULL value is permitted (initialization would not apply in such case).
310  *
311  * Initializes %libcaptive and loads the specified filesystem.
312  * You can supply %NULL value for @image_iochannel - in such case no library
313  * initialization is done; only passed @captive_args are parsed. Function
314  * will return you %FALSE value as it is not yet initializied.
315  *
316  * You should supply only @captive_args with %NULL @image_iochannel if you
317  * need to parse+examine the arguments to properly initialize @image_iochannel.
318  *
319  * Returns: %TRUE if successfuly initialized.
320  */
321 gboolean captive_init(const gchar *captive_args,GIOChannel *image_iochannel)
322 {
323 gboolean errbool;
324 int errint;
325
326 #ifdef MAINTAINER_MODE
327         g_log_set_always_fatal(~(0
328                         |G_LOG_LEVEL_MESSAGE
329                         |G_LOG_LEVEL_INFO
330                         |G_LOG_LEVEL_DEBUG
331                         ));
332 #endif
333
334         g_return_val_if_fail(active==FALSE,FALSE);
335
336         /* (optionally) parse the given 'captive_args' string */
337         if (captive_args) {
338 int captive_args_argc;
339 const char **captive_args_argv=NULL;
340 poptContext context;
341 gboolean r=FALSE;
342
343                 errint=poptParseArgvString(captive_args,&captive_args_argc,&captive_args_argv);
344                 if (errint!=0) {
345                         g_assert_not_reached(); /* argument parsing args_error */
346                         goto args_err;
347                         }
348                 context=poptGetContext(
349                                 PACKAGE,        /* name */
350                                 captive_args_argc,captive_args_argv,    /* argc,argv */
351                                 captive_popt_standalone,        /* options */
352                                 POPT_CONTEXT_KEEP_FIRST);
353                 if (context==NULL) {
354                         g_assert_not_reached(); /* argument recognization args_error */
355                         goto args_err_argv;
356                         }
357                 errint=poptReadDefaultConfig(context,
358                                 TRUE);  /* useEnv */
359                 if (errint!=0) {
360                         g_assert_not_reached(); /* argument recognization args_error */
361                         goto args_err_context;
362                         }
363                 errint=poptGetNextOpt(context);
364                 if (errint!=-1) {
365                         g_assert_not_reached(); /* some non-callbacked argument reached */
366                         goto args_err_context;
367                         }
368                 /* FIXME: reject non-"--"-prefixed arguments; how to detected them? */
369                 r=TRUE; /* success */
370 args_err_context:
371                 poptFreeContext(context);
372 args_err_argv:
373                 free(captive_args_argv);        /* may be NULL here */
374 args_err:
375                 if (!r) {
376                         puts("FIXME: HELP HERE");
377                         g_return_val_if_reached(r);
378                         }
379                 }
380
381         /* If we were just requested to parse the arguments. */
382         if (!image_iochannel)
383                 return FALSE;
384
385         if (!captive_debug_messages) {
386                 g_log_set_handler(
387                                 G_LOG_DOMAIN,   /* log_domain; "Captive" */
388                                 0       /* log_levels */
389                                                 | G_LOG_FLAG_RECURSION
390                                                 | G_LOG_FLAG_FATAL
391                                                 | G_LOG_LEVEL_MESSAGE
392                                                 | G_LOG_LEVEL_INFO
393                                                 | G_LOG_LEVEL_DEBUG,
394                                 log_discard_func,       /* log_func */
395                                 NULL);  /* user_data */
396                 }
397
398         if (captive_option_rwmode==CAPTIVE_OPTION_RWMODE_BLIND)
399                 captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(image_iochannel);
400         else
401                 captive_image_iochannel=image_iochannel;
402         captive_image_iochannel_orig=image_iochannel;
403
404         /* Do not initialize 'captive_image_size' by captive_giochannel_size() here
405          * as we yet need to do g_io_channel_set_encoding().
406          */
407
408         errbool=captive_w32_init();
409         g_return_val_if_fail(errbool==TRUE,FALSE);
410
411         g_atexit(captive_shutdown_atexit);
412
413         active=TRUE;
414         return TRUE;
415 }
416
417
418 /**
419  * captive_shutdown:
420  *
421  * Closes down %libcaptive. It should flush all pending buffers and successfuly
422  * close the filesystem. Variable #captive_image_iochannel will be set to %NULL,
423  * you should close such channel yourself.
424  *
425  * Returns: %TRUE if successfuly shutdown.
426  */
427 gboolean captive_shutdown(void)
428 {
429 GIOStatus erriostatus;
430
431         g_return_val_if_fail(active==TRUE,FALSE);
432         g_return_val_if_fail(captive_image_iochannel!=NULL,FALSE);
433
434         /* FIXME: ntoskrnl/ex/power.c/NtShutdownSystem() does
435          * IoShutdownRegistered{Devices,FileSystems} order; is it correct?
436          */
437         IoShutdownRegisteredFileSystems();
438         IoShutdownRegisteredDevices();
439
440         /* libcaptive is not authorized to shutdown 'captive_image_channel'. */
441         erriostatus=g_io_channel_flush(
442                         captive_image_iochannel,        /* channel */
443                         NULL);  /* error */
444         g_assert(erriostatus==G_IO_STATUS_NORMAL);
445
446         /* 'captive_image_iochannel' may be blinded wrapper. */
447         if (captive_image_iochannel!=captive_image_iochannel_orig) {
448                 erriostatus=g_io_channel_flush(
449                                 captive_image_iochannel_orig,   /* channel */
450                                 NULL);  /* error */
451                 g_assert(erriostatus==G_IO_STATUS_NORMAL);
452                 }
453
454         captive_image_iochannel=NULL;
455
456         active=FALSE;
457         return TRUE;
458 }
459
460
461 static void captive_shutdown_atexit(void)
462 {
463         if (active)
464                 captive_shutdown();
465 }