2 * Init and cleanup code of libcaptive to be called by client application
3 * Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
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
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.
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
22 #include "init.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 */
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 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() */
43 #include <glib/gstrfuncs.h>
44 #include <glib/glist.h>
45 #include "giochannel-blind.h"
46 #include <glib-object.h>
47 #include "reactos/internal/se.h" /* for SeInit2() */
48 #include "captive/leave.h"
49 #include "captive/options.h"
50 #include <libgnomevfs/gnome-vfs-result.h>
52 #include <reactos/ddk/obfuncs.h>
54 #include "captive/macros.h"
55 #include "../storage/relastblock.h" /* for captive_storage_relastblock() */
56 #include "../cc/sharedcachemap.h" /* for captive_shared_cache_map_flush_all() */
57 #include "standalone.h"
60 struct captive_options *captive_options;
62 /* Are we initialized? */
63 static gboolean active;
65 /* Module of fs module itself loaded by captive_w32_init() */
66 static PMODULE_OBJECT ModuleObject;
68 /* Driver in fs module loaded by captive_w32_init() */
69 DRIVER_OBJECT captive_DriverObject;
70 PDRIVER_REINITIALIZE captive_DriverObject_ReinitRoutine;
71 PVOID captive_DriverObject_ReinitRoutine_Context;
73 /* Structure holding the pointer to the toplevel IRP */
74 static TOP_LEVEL_IRP TopLevelIrp; /* TODO:thread */
77 void *_local_unwind2_addr;
80 gboolean captive_debug_messages_disabled=FALSE;
82 static gboolean captive_w32_init(void)
87 g_return_val_if_fail(captive_options!=NULL,FALSE);
88 g_return_val_if_fail(captive_options->filesystem.type!=CAPTIVE_OPTIONS_MODULE_TYPE_EMPTY,FALSE);
90 /* captive_giochannel_size() only _after_ g_io_channel_set_encoding() ! */
91 captive_image_size=captive_giochannel_size(captive_image_iochannel);
92 g_return_val_if_fail(captive_image_size>0,FALSE);
94 /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() begins. */
95 /* ExpInitializeExecutive(); */
96 /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() begins
97 * here as the rest of the function does a lot of hardware initializations.
100 /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() begins. */
101 InitializeListHead(&ModuleTextListHead);
102 /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() ends. */
103 KeLowerIrql(DISPATCH_LEVEL);
105 /* FIXME: create default nls tables? Really still needed? */
106 /* Obsolete: RtlpInitNlsTables(); */
110 KeInitializeDispatcher();
112 KeLowerIrql(PASSIVE_LEVEL);
114 g_assert(errbool==TRUE);
117 g_assert(errbool==TRUE);
118 /* PiInitProcessManager(); */
119 /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() begins. */
120 PsInitProcessManagment();
121 PsInitThreadManagment();
122 /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() ends. */
126 /* LdrInitModuleManagement(); */
127 /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement() begins
128 * here as the rest "Create module object for {NTOSKRNL,HAL}"
129 * is dependent on {NTOSKRNL,HAL} PE image headers not provided by libcaptive.
131 /* Initialize the module list and spinlock */
132 InitializeListHead(&ModuleListHead);
133 KeInitializeSpinLock(&ModuleListLock);
134 /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement ends. */
137 NtInitializeEventImplementation();
139 /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() ends. */
140 /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() ends. */
142 /* Simulate our PE headers and export the symbols of our complete libraries */
143 captive_kernel_exports();
145 errbool=captive_cdrom_init();
146 g_return_val_if_fail(errbool==TRUE,FALSE);
147 errbool=captive_disk_init();
148 g_return_val_if_fail(errbool==TRUE,FALSE);
150 while (captive_options->load_module) {
151 PMODULE_OBJECT ModuleObject_tmp;
154 /* load the module */
155 err=captive_LdrLoadModule(
156 captive_options->load_module->data,
157 &ModuleObject_tmp); /* ModuleObjectp */
158 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
160 captive_options_module_free(captive_options->load_module->data);
161 /* also frees 'options->load_module->data' */
162 captive_options->load_module=g_list_delete_link(captive_options->load_module,captive_options->load_module);
165 /* Patch 'ntoskrnl.exe' loaded by 'captive_options->load_module' above. */
167 CHAR *KeNumberProcessorsp=captive_Module_GetExportAddress("ntoskrnl.exe","KeNumberProcessors");
169 g_assert(*KeNumberProcessorsp==0);
170 *KeNumberProcessorsp=KeNumberProcessors;
171 g_assert(*KeNumberProcessorsp==1);
173 /* Apply AFTER any symbols sanity checks above! */
174 if (captive_options->debug_messages)
175 captive_kernel_patches_debug();
177 captive_kernel_patches_nondebug();
179 _local_unwind2_addr=captive_Module_GetExportAddress("ntoskrnl.exe","_local_unwind2");
181 /* Initialize 'FsRtlLegalAnsiCharacterArray'.
182 * It requires 'ntoskrnl.exe' loaded by 'captive_options->load_module' above;
183 * captive_kernel_patches_debug()/captive_kernel_patches_nondebug() should not be needed.
185 captive_FsRtlLegalAnsiCharacterArray_init();
187 /* set TopLevelIrp() - FIXME: where is it set by native reactos? */
188 PsGetCurrentThread()->TopLevelIrp=&TopLevelIrp; /* otherwise Io{Get,Set}TopLevelIrp() would SIGSEGV */
190 /* Begin possible handling of foreign W32 binary code here */
191 /* If you want to disable SIGSEGV handler if not needed:
192 * if (ModuleObject->Flags & MODULE_FLAG_PE)
194 captive_signal_init();
196 /* You must have already captive_signal_init() passed here as the module may
197 * call some functions from W32 ntoskrnl.exe.
199 captive_DriverObject_ReinitRoutine=NULL;
200 err=captive_LdrpLoadAndCallImage(
201 &ModuleObject, /* ModuleObjectp */
202 &captive_options->filesystem, /* options_module */
203 &captive_DriverObject, /* DriverEntry_DriverObject */
204 captive_utf8_to_UnicodeString_alloca("\\captive\\filesystem")); /* DriverEntry_RegistryPath */
205 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
206 if (captive_DriverObject_ReinitRoutine) {
207 captive_stdcall_call_12((CaptiveStdCallFunc12)captive_DriverObject_ReinitRoutine,
208 &captive_DriverObject, /* DriverObject */
209 captive_DriverObject_ReinitRoutine_Context, /* Context */
210 (gpointer)1); /* Count: # of calls of ReinitRoutine incl. the current one */
217 static void captive_log_init_g_log_func_discard
218 (const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
220 g_return_if_fail(message!=NULL);
225 static void captive_log_init_g_log_func
226 (const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data /* unused */)
230 g_return_if_fail(message!=NULL);
232 /* unused: LOG_EMERG */
233 /* unused: LOG_ALERT */
234 /**/ if (log_level&G_LOG_LEVEL_ERROR)
236 else if (log_level&G_LOG_LEVEL_CRITICAL)
238 else if (log_level&G_LOG_LEVEL_WARNING)
239 priority=LOG_WARNING;
240 else if (log_level&G_LOG_LEVEL_MESSAGE)
242 else if (log_level&G_LOG_LEVEL_INFO)
244 else if (log_level&G_LOG_LEVEL_DEBUG)
247 priority=LOG_WARNING;
249 syslog(priority,"%s%s%s",
250 (!(log_level&G_LOG_FLAG_RECURSION) ? "" : "RECURSION: "),
251 (!(log_level&G_LOG_FLAG_FATAL ) ? "" : "FATAL: "),
255 void captive_log_init(const struct captive_options *captive_options)
257 g_return_if_fail(captive_options!=NULL);
259 /* FIXME: Fix sharing of different 'debug_messages' for various sandboxes. */
260 captive_debug_messages_disabled=!captive_options->debug_messages;
262 /* FIXME: Fix sharing of different 'debug_messages' for various sandboxes. */
263 if (!captive_options->debug_messages) {
264 /* FIXME: Save handler_id and destroy it in captive_vfs_close(). */
266 G_LOG_DOMAIN, /* log_domain; "Captive" */
268 | G_LOG_FLAG_RECURSION
270 /* The same mask is in:
271 * libcaptive/sandbox/server-GLogFunc.c
272 * libcaptive/client/init.c
274 | G_LOG_LEVEL_MESSAGE
277 captive_log_init_g_log_func_discard, /* log_func */
278 NULL); /* user_data */
280 /* We are not the sandboxed slave;
281 * 'syslog_facility' would be '-1' in slave anyway as it is not transferred through CORBA.
283 if (!captive_options->sandbox || (captive_options->sandbox_server_argv || captive_options->sandbox_server_ior)) {
284 if (captive_options->syslog_facility!=-1) {
286 /* FIXME: Prefix 'ident' by device/mountpoint. */
287 G_LOG_DOMAIN, /* ident; "Captive"; FIXME: lowercase it for syslog(3)? */
288 LOG_CONS|LOG_PID, /* options */
289 captive_options->syslog_facility); /* facility */
290 /* FIXME: Save handler_id and destroy it in captive_vfs_close(). */
292 G_LOG_DOMAIN, /* log_domain; "Captive" */
294 | 0 /* !G_LOG_FLAG_RECURSION */
296 | (G_LOG_LEVEL_MASK & ~(captive_options->debug_messages ? 0 : 0
297 /* The same mask is in:
298 * libcaptive/sandbox/server-GLogFunc.c
299 * libcaptive/client/init.c
301 | G_LOG_LEVEL_MESSAGE
303 | G_LOG_LEVEL_DEBUG)),
304 (GLogFunc)captive_log_init_g_log_func, /* log_func */
305 NULL); /* user_data */
313 * Expects #captive_options: Parsed by captive_options_parse().
314 * %NULL value is forbidden. Field #image_iochannel %NULL value is forbidden.
316 * Initializes %libcaptive and loads the specified filesystem.
318 * Returns: %TRUE if successfuly initialized.
320 gboolean captive_init(void)
324 /* We are in sandbox child and we have the right to fail. */
325 g_log_set_always_fatal(~(0
331 g_return_val_if_fail(captive_standalone_init_done==TRUE,FALSE);
332 g_return_val_if_fail(active==FALSE,FALSE);
334 g_return_val_if_fail(captive_options!=NULL,FALSE);
335 g_return_val_if_fail(captive_options->image_iochannel!=NULL,FALSE);
337 /* Do not: g_type_init();
338 * as it is done by: captive_standalone_init()
341 captive_log_init(captive_options);
343 if (captive_options->rwmode==CAPTIVE_OPTION_RWMODE_RW && !captive_options->sandbox)
344 g_error(_("Rejecting --rw --no-sandbox operation as too dangerous - use --blind or --sandbox"));
346 captive_image_iochannel=captive_options->image_iochannel;
347 g_io_channel_ref(captive_image_iochannel);
349 captive_image_iochannel=captive_storage_relastblock(captive_image_iochannel);
351 if (captive_options->rwmode==CAPTIVE_OPTION_RWMODE_BLIND) {
352 GIOChannel *captive_image_iochannel_orig;
354 captive_image_iochannel_orig=captive_image_iochannel;
355 captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(captive_image_iochannel,
356 TRUE); /* writeable */
357 g_io_channel_unref(captive_image_iochannel_orig); /* reffed by captive_giochannel_blind_new() */
360 /* Do not initialize 'captive_image_size' by captive_giochannel_size() here
361 * as we yet need to do g_io_channel_set_encoding().
364 errbool=captive_w32_init();
365 g_return_val_if_fail(errbool==TRUE,FALSE);
372 static void dismount_volume(void)
374 IO_STATUS_BLOCK IoStatusBlock;
375 PEXTENDED_IO_STACK_LOCATION StackPtr;
378 DEVICE_OBJECT *DeviceObject=captive_DriverObject.DeviceObject;
379 OBJECT_ATTRIBUTES dir_ObjectAttributes;
381 FILE_OBJECT *FileObject;
382 GnomeVFSResult errvfsresult;
384 IO_STATUS_BLOCK dir_IoStatusBlock;
386 * TraceFS reported only IRP_MJ_FLUSH_BUFFERS
387 * and IRP_MJ_SHUTDOWN.
388 * Apparently it is not enough, FSCTL_DISMOUNT_VOLUME is needed,
389 * otherwise NT-5.1 autochkdsks the disk and W2000 may give BSOD during boot.
392 /* First item value assumed to be 0. */
393 STEP_IRP_MJ_FLUSH_BUFFERS_PRE,
395 /* DISABLED: STATUS_ACCESS_DENIED; FIXME: Why?
396 * The official way of device modification is: LOCK,DISMOUNT
397 * but LOCK fails for Captive if any file was written (and closed).
399 STEP_FSCTL_LOCK_VOLUME,
401 STEP_FSCTL_DISMOUNT_VOLUME,
402 STEP_IRP_MJ_FLUSH_BUFFERS_POST,
407 errvfsresult=captive_ObjectAttributes_init("/!Captive!del",&dir_ObjectAttributes);
408 g_return_if_fail(errvfsresult==GNOME_VFS_OK);
410 /* wanted: * IoCreateFile()->ObCreateObject(,,,IoFileObjectType)->
411 * ->(IoFileObjectType->Create==IopCreateFile)()->IoMountVolume()
413 CAPTIVE_MEMZERO(&dir_IoStatusBlock); /* FIXME: Try to pre-clear it - uninitialized otherwise? */
414 dir_IoStatusBlock.Information=FILE_OPENED; /* FIXME: Try to pre-set it - uninitialized otherwise? */
416 &dir_Handle, /* FileHandle */
417 GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE|0x80, /* DesiredAccess; 0xC0100080=GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE|0x80 */
418 &dir_ObjectAttributes, /* ObjectAttributes */
419 &dir_IoStatusBlock, /* IoStatusBlock */
420 NULL, /* AllocationSize; ignored for open */
421 FILE_ATTRIBUTE_NORMAL, /* FileAttributes; ignored for open */
422 (FILE_SHARE_READ | FILE_SHARE_WRITE), /* ShareAccess; 0 means exclusive */
423 FILE_OPEN, /* CreateDisposition */
424 /* FILE_SYNCHRONOUS_IO_{,NON}ALERT: We need to allow W32 filesystem
425 * any waits to not to let it return STATUS_CANT_WAIT us.
426 * Alertability should have only effect on asynchronous events
427 * from KeWaitForSingleObject() by setting/clearing its parameter 'Alertable'.
429 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, /* CreateOptions; FILE_DIRECTORY_FILE is forbidden */
432 CreateFileTypeNone, /* CreateFileType */
433 NULL, /* ExtraCreateParameters */
435 g_free(dir_ObjectAttributes.ObjectName); /* left from captive_gnomevfs_uri_parent_init() */
436 g_return_if_fail(NT_SUCCESS(err));
437 g_return_if_fail(NT_SUCCESS(err)==NT_SUCCESS(dir_IoStatusBlock.Status));
438 g_return_if_fail(dir_IoStatusBlock.Information==FILE_OPENED);
440 Status=ObReferenceObjectByHandle(dir_Handle,FILE_LIST_DIRECTORY,IoFileObjectType,UserMode,(PVOID *)&FileObject,NULL);
441 g_assert(NT_SUCCESS(Status));
443 g_assert(FileObject->FileName.Length==0);
444 /* 'FileObject->FileName.MaximumLength' is not reset by IoCreateFile(). */
445 g_assert(FileObject->FileName.Buffer==NULL);
446 FileObject->FileName.MaximumLength=2;
447 FileObject->FileName.Buffer=&wzero;
449 for (stepi=0;stepi<STEP_MAX;stepi++) {
450 Irp=IoAllocateIrp(DeviceObject->StackSize,TRUE);
451 g_return_if_fail(Irp!=NULL);
453 Irp->UserIosb=&IoStatusBlock;
454 Irp->UserEvent=&FileObject->Event;
455 Irp->Tail.Overlay.Thread=PsGetCurrentThread();
457 StackPtr=(EXTENDED_IO_STACK_LOCATION *)IoGetNextIrpStackLocation(Irp);
459 case STEP_IRP_MJ_FLUSH_BUFFERS_PRE:
460 StackPtr->MajorFunction=IRP_MJ_FLUSH_BUFFERS;
462 #if 0 /* Disabled, see 'STEP_FSCTL_LOCK_VOLUME'. */
463 case STEP_FSCTL_LOCK_VOLUME:
464 StackPtr->MajorFunction=IRP_MJ_FILE_SYSTEM_CONTROL;
465 StackPtr->MinorFunction=IRP_MN_USER_FS_REQUEST;
466 StackPtr->Parameters.FileSystemControl.OutputBufferLength=0;
467 StackPtr->Parameters.FileSystemControl.InputBufferLength=0;
468 StackPtr->Parameters.FileSystemControl.FsControlCode=FSCTL_LOCK_VOLUME;
469 StackPtr->Parameters.FileSystemControl.Type3InputBuffer=NULL;
472 case STEP_FSCTL_DISMOUNT_VOLUME:
473 StackPtr->MajorFunction=IRP_MJ_FILE_SYSTEM_CONTROL;
474 StackPtr->MinorFunction=IRP_MN_USER_FS_REQUEST;
475 StackPtr->Parameters.FileSystemControl.OutputBufferLength=0;
476 StackPtr->Parameters.FileSystemControl.InputBufferLength=0;
477 StackPtr->Parameters.FileSystemControl.FsControlCode=FSCTL_DISMOUNT_VOLUME;
478 StackPtr->Parameters.FileSystemControl.Type3InputBuffer=NULL;
480 case STEP_IRP_MJ_FLUSH_BUFFERS_POST:
481 StackPtr->MajorFunction=IRP_MJ_FLUSH_BUFFERS;
483 default: g_assert_not_reached();
487 StackPtr->DeviceObject=DeviceObject; /* FIXME: FileObject->Vpb->DeviceObject ? */
488 StackPtr->FileObject=FileObject;
489 StackPtr->CompletionRoutine=NULL;
491 /* IoCallDriver() will do one ObDereferenceObject(FileObject)
492 * in its IoSecondStageCompletion().
493 * Do not leave to dereference it itself as we need its 'FileObject->Event'.
495 ObReferenceObject(FileObject);
497 Status=IoCallDriver(DeviceObject,Irp);
498 if (Status==STATUS_PENDING) {
499 KeWaitForSingleObject(&FileObject->Event,Executive,KernelMode,FALSE,NULL);
500 Status=IoStatusBlock.Status;
502 g_assert(NT_SUCCESS(Status)
503 || (Status==STATUS_MEDIA_WRITE_PROTECTED && captive_options->rwmode==CAPTIVE_OPTION_RWMODE_RO));
506 ObDereferenceObject(FileObject);
510 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject);
511 void captive_cc_flush(void);
516 * Closes down %libcaptive. It should flush all pending buffers and successfuly
517 * close the filesystem. Variable #captive_options->image_iochannel will not be set to %NULL,
518 * you should close such channel yourself.
520 * Returns: %TRUE if successfuly shutdown.
522 gboolean captive_shutdown(void)
524 GIOStatus erriostatus;
526 g_return_val_if_fail(active==TRUE,FALSE);
527 g_return_val_if_fail(captive_image_iochannel!=NULL,FALSE);
528 g_return_val_if_fail(captive_options->image_iochannel!=NULL,FALSE);
530 /* Invoke all pending idle functions just to not to forget for anything... */
531 while (g_main_context_iteration(
532 NULL, /* context; NULL means default one */
533 FALSE)) /* may_block */
534 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_main_context_iteration() proceeded",G_STRLOC);
536 /* Do not: captive_cc_flush(); * based on captive_leave(), not g_main idle *
537 * replaced by IRP_MJ_FLUSH_BUFFERS.
540 /* Probably not needed: captive_shared_cache_map_flush_all();
545 /* Probably not needed: captive_shared_cache_map_flush_all();
548 /* Invoke all pending idle functions just to not to forget for anything... */
549 while (g_main_context_iteration(
550 NULL, /* context; NULL means default one */
551 FALSE)) /* may_block */
552 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_main_context_iteration() proceeded",G_STRLOC);
555 captive_PoQueueShutdownWorkItem_hooklist_invoke();
558 /* Do not: captive_cc_flush();
559 * as the dirty blocks should have been already commited by dismount_volume(),
560 * any further commits would get us just STATUS_VOLUME_DISMOUNTED.
563 /* Do not: captive_cc_unmounting=TRUE;
564 * Without dismount_volume() it was:
565 * During IoShutdownRegisteredFileSystems() - IRP_MJ_SHUTDOWN to be specific
566 * some buffers will be written but after the IofCallDriver() it will be
567 * no longer possible to flush such buffers to their DeviceVolumeFile.
568 * Therefore we must flush such buffers on the fly although such behaviour
569 * would crash us in regular case as filesystems access BCBs even after their
571 * Currently the dirty blocks should have been already commited by dismount_volume(),
574 /* FIXME: ntoskrnl/ex/power.c/NtShutdownSystem() does
575 * IoShutdownRegistered{Devices,FileSystems} order; is it correct?
577 IoShutdownRegisteredFileSystems();
579 /* Do not: captive_cc_FileObject_delete(NULL);
580 * as the dirty blocks should have been already commited by dismount_volume(),
581 * any further commits would get us just STATUS_VOLUME_DISMOUNTED.
584 IoShutdownRegisteredDevices();
586 /* Just a sanity if 'captive_image_iochannel' is already reffed a bit more... */
587 erriostatus=g_io_channel_flush(
588 captive_image_iochannel, /* channel */
590 g_assert(erriostatus==G_IO_STATUS_NORMAL);
592 g_io_channel_unref(captive_image_iochannel);
593 captive_image_iochannel=NULL;