/* $Id$ * developer replaying of bugreport snapshots * Copyright (C) 2003 Jan Kratochvil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; exactly version 2 of June 1991 is required * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include #include #include /* for g_type_init() */ #include #include #include #include #include #include "../../libcaptive/client/giochannel-blind.h" /* FIXME: pathname */ #include #include #include static gchar *optarg_module_path; static const struct poptOption popt_table[]={ #define BUG_REPLAY_POPT(longname,argInfoP,argP,descripP,argDescripP) \ { \ longName: (longname), \ shortName: 0, \ argInfo: (argInfoP), \ arg: (void *)argP, \ val: 0, \ descrip: (descripP), \ argDescrip: (argDescripP), \ } BUG_REPLAY_POPT("module-path",POPT_ARG_STRING,&optarg_module_path,N_("Disk path for W32 modules and filesystem"),N_("path")), #undef BUG_REPLAY_POPT POPT_AUTOHELP POPT_TABLEEND }; static void object_hash_key_destroy_func(const xmlChar *xml_object) { g_return_if_fail(xml_object!=NULL); xmlFree((xmlChar *)xml_object); } static void object_hash_value_destroy_func(GObject *captive_any_object) { g_return_if_fail(G_IS_OBJECT(captive_any_object)); g_object_unref(captive_any_object); } int main(int argc,char **argv) { poptContext context; int errint; const char **cmd_argv; struct captive_options options; xmlTextReader *xml_reader; GList *action_list; /* of 'xmlNode *' */ gboolean preread; gboolean module_is_filesystem=FALSE; CaptiveVfsObject *captive_vfs_object; gboolean action_is_first; xmlNode *xml_node; GHashTable *object_hash; GObject *object; gboolean errbool; CaptiveDirectoryObject *captive_directory_object; CaptiveFileObject *captive_file_object; GnomeVFSResult errgnomevfsresult; struct captive_libxml_string_drop_stack *drop_stack=NULL; const gchar *xml_object; captive_standalone_init(); captive_options_init(&options); context=poptGetContext( PACKAGE, /* name */ argc,(/*en-const*/const char **)argv, /* argc,argv */ popt_table, /* options */ POPT_CONTEXT_POSIXMEHARDER); /* flags; && !POPT_CONTEXT_KEEP_FIRST */ if (context==NULL) { g_assert_not_reached(); /* argument recognization args_error */ return EXIT_FAILURE; } errint=poptReadDefaultConfig(context, TRUE); /* useEnv */ if (errint!=0) { g_assert_not_reached(); /* argument recognization args_error */ return EXIT_FAILURE; } errint=poptGetNextOpt(context); if (errint!=-1) { g_assert_not_reached(); /* some non-callbacked argument reached */ return EXIT_FAILURE; } cmd_argv=poptGetArgs(context); if (!cmd_argv || !cmd_argv[0] || cmd_argv[1]) { g_error("Exactly one argument required - pathname to .captivebug.xml.gz bugreport to replay"); return EXIT_FAILURE; } options.rwmode=CAPTIVE_OPTION_RWMODE_BLIND; options.debug_messages=TRUE; options.image_iochannel=NULL; action_list=NULL; options.media=CAPTIVE_OPTION_MEDIA_UNKNOWN; xml_reader=xmlNewTextReaderFilename(cmd_argv[0]); g_assert(xml_reader!=NULL); preread=FALSE; while (preread || 1==xmlTextReaderRead(xml_reader)) { preread=FALSE; switch (xmlTextReaderNodeType(xml_reader)) { case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT: break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_SIGNIFICANT_WHITESPACE: break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */ break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: /* We do not track tag ends. */ break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: { const xmlChar *xml_name; xml_name=xmlTextReaderName(xml_reader); /**/ if (!xmlStrcmp(xml_name,BAD_CAST "bug")) { /* root tag */ } else if (!xmlStrcmp(xml_name,BAD_CAST "captive")) { const gchar *xml_captive_version; xml_captive_version=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,BAD_CAST "version")); g_assert(xml_captive_version!=NULL); #ifndef MAINTAINER_MODE g_assert(!strcmp(xml_captive_version,VERSION)); #endif } else if (!xmlStrcmp(xml_name,BAD_CAST "filesystem")) { /* contains */ module_is_filesystem=TRUE; } else if (!xmlStrcmp(xml_name,BAD_CAST "load_module")) { /* contains */ module_is_filesystem=FALSE; } else if (!xmlStrcmp(xml_name,BAD_CAST "module")) { const gchar *xml_module_basename; struct captive_options_module *options_module; gboolean errbool; if (module_is_filesystem) options_module=&options.filesystem; else { captive_new(options_module); options.load_module=g_list_append(options.load_module,options_module); } xml_module_basename=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,BAD_CAST "basename")); g_assert(xml_module_basename!=NULL); g_assert(strchr(xml_module_basename,'/')==NULL); /* a bit of security */ errbool=captive_options_module_load(options_module, captive_printf_alloca("%s/%s", (optarg_module_path ? optarg_module_path : "."),xml_module_basename)); g_assert(errbool==TRUE); switch (options_module->type) { case CAPTIVE_OPTIONS_MODULE_TYPE_PE32: { const gchar *xml_module_md5,*xml_module_length; if (options_module->u.pe32.md5 && (xml_module_md5=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,BAD_CAST "version")))) g_assert(!strcmp(xml_module_md5,options_module->u.pe32.md5)); if ((xml_module_length=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,BAD_CAST "length")))) g_assert(!strcmp(xml_module_length, captive_printf_alloca("%lu",(unsigned long)options_module->u.pe32.length))); } break; case CAPTIVE_OPTIONS_MODULE_TYPE_GMODULE: /* No possibility to check anything. */ break; default: g_assert_not_reached(); } } else if (!xmlStrcmp(xml_name,BAD_CAST "action")) { int xml_action_depth; gboolean action_done,action_preread; g_assert(action_list==NULL); xml_action_depth=xmlTextReaderDepth(xml_reader); action_preread=FALSE; action_done=FALSE; while (!action_done) { if (!action_preread) { errint=xmlTextReaderRead(xml_reader); g_assert(errint==1); } action_preread=FALSE; g_assert(xml_action_depth<=xmlTextReaderDepth(xml_reader)); switch (xmlTextReaderNodeType(xml_reader)) { case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT: break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_SIGNIFICANT_WHITESPACE: break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */ break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: { /* We do not track tag ends. */ if ((action_done=(xml_action_depth==xmlTextReaderDepth(xml_reader)))) { const gchar *xml_name_end; xml_name_end=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader)); g_assert(!strcmp(xml_name_end,"action")); } } break; case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: { xmlNode *xml_node; xml_node=xmlTextReaderExpand(xml_reader); g_assert(xml_node!=NULL); xml_node=xmlCopyNode(xml_node, 1); /* recursive */ errint=xmlTextReaderNext(xml_reader); g_assert(errint==1); action_preread=TRUE; action_list=g_list_prepend(action_list,xml_node); /* we are prepending to be effective */ } break; default: g_assert_not_reached(); } } } else if (!xmlStrcmp(xml_name,BAD_CAST "media")) { const xmlChar *xml_media_end_name,*xml_media_type; g_assert(options.image_iochannel==NULL); options.image_iochannel=(GIOChannel *)captive_giochannel_blind_new_from_xml(xml_reader); g_assert(CAPTIVE_XML_TEXT_READER_NODE_TYPE_END==xmlTextReaderNodeType(xml_reader)); xml_media_end_name=xmlTextReaderName(xml_reader); g_assert(xml_media_end_name!=NULL); g_assert(!xmlStrcmp(xml_media_end_name,BAD_CAST "media")); xmlFree((xmlChar *)xml_media_end_name); xml_media_type=xmlTextReaderGetAttribute(xml_reader,BAD_CAST "type"); g_assert(xml_media_type!=NULL); /**/ if (!xmlStrcmp(xml_media_type,BAD_CAST "cdrom")) options.media=CAPTIVE_OPTION_MEDIA_CDROM; else if (!xmlStrcmp(xml_media_type,BAD_CAST "disk")) options.media=CAPTIVE_OPTION_MEDIA_DISK; else g_assert_not_reached(); xmlFree((xmlChar *)xml_media_type); } else if (!xmlStrcmp(xml_name,BAD_CAST "log")) { errint=xmlTextReaderNext(xml_reader); g_assert(errint==1); preread=TRUE; } else g_error("Unknown START node: %s",xml_name); xmlFree((xmlChar *)xml_name); } break; default: g_assert_not_reached(); } captive_libxml_string_drop_flush(&drop_stack); } xmlFreeTextReader(xml_reader); g_assert(options.image_iochannel!=NULL); action_list=g_list_reverse(action_list); /* we were g_list_prepend()ing for effectivity */ g_assert(options.media!=CAPTIVE_OPTION_MEDIA_UNKNOWN); if (GNOME_VFS_OK!=captive_vfs_new(&captive_vfs_object,&options)) { g_error(_("captive_vfs_new() failed")); return EXIT_FAILURE; } captive_options_free(&options); /* 'cmd_argv' gets cleared by 'poptFreeContext(context);' below */ poptFreeContext(context); object_hash=g_hash_table_new_full( g_str_hash, /* hash_func */ g_str_equal, /* key_equal_func */ (GDestroyNotify)object_hash_key_destroy_func, /* key_destroy_func */ (GDestroyNotify)object_hash_value_destroy_func); /* value_destroy_func */ for ( action_is_first=TRUE; action_list; xmlFreeNode(xml_node),action_is_first=FALSE) { captive_libxml_string_drop_flush(&drop_stack); #define GET_PROP_STRING(prop_name) ({ \ const gchar *_get_prop_string_r=captive_libxml_string_drop(&drop_stack,xmlGetProp(xml_node,BAD_CAST (prop_name))); \ g_assert(_get_prop_string_r!=NULL); \ _get_prop_string_r; \ }) #define GET_PROP_GINT64(prop_name) (captive_libxml_sscanf_gint64(GET_PROP_STRING((prop_name)))) xml_node=action_list->data; g_assert(xml_node!=NULL); action_list=g_list_delete_link(action_list,action_list); g_message("replay action: %s()",xml_node->name); xml_object=GET_PROP_STRING("object"); object=g_hash_table_lookup(object_hash,xml_object); g_assert(object==NULL || G_IS_OBJECT(object)); if (!xmlStrcmp(xml_node->name,BAD_CAST "vfs_new")) { g_assert(action_is_first==TRUE); g_assert(captive_vfs_object!=NULL); g_assert(object==NULL); g_hash_table_insert(object_hash,g_strdup(xml_object),captive_vfs_object); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "vfs_commit")) { g_assert(action_is_first==TRUE); g_assert(captive_vfs_object!=NULL); g_assert(object==NULL); g_hash_table_insert(object_hash,g_strdup(xml_object),captive_vfs_object); continue; } g_assert(action_is_first==FALSE); if (!xmlStrcmp(xml_node->name,BAD_CAST "vfs_close")) { g_assert(captive_vfs_object!=NULL); g_assert(CAPTIVE_VFS_OBJECT(object)==captive_vfs_object); errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */ g_assert(errbool==TRUE); continue; } g_assert(captive_vfs_object!=NULL); if (!xmlStrcmp(xml_node->name,BAD_CAST "vfs_volume_info_get")) { CaptiveVfsVolumeInfo volume_info; errgnomevfsresult=captive_vfs_volume_info_get(captive_vfs_object,&volume_info); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); continue; } /* DIRECTORY */ if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_new_open")) { g_assert(object==NULL); errgnomevfsresult=captive_directory_new_open(&captive_directory_object,captive_vfs_object, GET_PROP_STRING("pathname")); g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result")); if (errgnomevfsresult==GNOME_VFS_OK) g_hash_table_insert(object_hash,g_strdup(xml_object),captive_directory_object); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_new_make")) { g_assert(object==NULL); errgnomevfsresult=captive_directory_new_make(&captive_directory_object,captive_vfs_object, GET_PROP_STRING("pathname"), GET_PROP_GINT64("perm")); g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result")); if (errgnomevfsresult==GNOME_VFS_OK) g_hash_table_insert(object_hash,g_strdup(xml_object),captive_directory_object); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_close")) { captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object); errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */ g_assert(errbool==TRUE); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_read")) { CaptiveFileInfoObject *captive_file_info_object; captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object); errgnomevfsresult=captive_directory_read(captive_directory_object,&captive_file_info_object); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); if (GNOME_VFS_OK==errgnomevfsresult) g_object_unref(captive_file_info_object); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_remove")) { captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object); errgnomevfsresult=captive_directory_remove(captive_directory_object); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); continue; } /* FILE */ if (!xmlStrcmp(xml_node->name,BAD_CAST "file_new_open")) { g_assert(object==NULL); errgnomevfsresult=captive_file_new_open(&captive_file_object,captive_vfs_object, GET_PROP_STRING("pathname"), GET_PROP_GINT64("mode")); g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result")); if (errgnomevfsresult==GNOME_VFS_OK) g_hash_table_insert(object_hash,g_strdup(xml_object),captive_file_object); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_new_create")) { g_assert(object==NULL); errgnomevfsresult=captive_file_new_create(&captive_file_object,captive_vfs_object, GET_PROP_STRING("pathname"), GET_PROP_GINT64("mode"), GET_PROP_GINT64("exclusive"), GET_PROP_GINT64("perm")); g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result")); if (errgnomevfsresult==GNOME_VFS_OK) g_hash_table_insert(object_hash,g_strdup(xml_object),captive_file_object); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_close")) { captive_file_object=CAPTIVE_FILE_OBJECT(object); errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */ g_assert(errbool==TRUE); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_read")) { gpointer buffer; GnomeVFSFileSize num_bytes,bytes_read; captive_file_object=CAPTIVE_FILE_OBJECT(object); num_bytes=GET_PROP_GINT64("num_bytes"); buffer=g_malloc(num_bytes); errgnomevfsresult=captive_file_read(captive_file_object,buffer,num_bytes,&bytes_read); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); if (errgnomevfsresult==GNOME_VFS_OK) g_assert((gint64)bytes_read==GET_PROP_GINT64("bytes_read_return")); g_free(buffer); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_write")) { gpointer buffer; GnomeVFSFileSize num_bytes,bytes_written; captive_file_object=CAPTIVE_FILE_OBJECT(object); num_bytes=GET_PROP_GINT64("num_bytes"); buffer=g_malloc(num_bytes); memset(buffer,'X',num_bytes); /* FIXME: better pattern */ errgnomevfsresult=captive_file_write(captive_file_object,buffer,num_bytes,&bytes_written); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); if (errgnomevfsresult==GNOME_VFS_OK) g_assert((gint64)bytes_written==GET_PROP_GINT64("bytes_written_return")); g_free(buffer); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_seek")) { GnomeVFSSeekPosition whence=GNOME_VFS_SEEK_START; /* Prevent: ... might be used uninitialized in this function */ const gchar *whence_string; captive_file_object=CAPTIVE_FILE_OBJECT(object); whence_string=GET_PROP_STRING("whence"); /**/ if (!strcmp(whence_string,"start" )) whence=GNOME_VFS_SEEK_START; else if (!strcmp(whence_string,"current")) whence=GNOME_VFS_SEEK_CURRENT; else if (!strcmp(whence_string,"end" )) whence=GNOME_VFS_SEEK_END; else g_assert_not_reached(); errgnomevfsresult=captive_file_seek(captive_file_object,whence, GET_PROP_GINT64("offset")); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_tell")) { GnomeVFSFileOffset offset; captive_file_object=CAPTIVE_FILE_OBJECT(object); errgnomevfsresult=captive_file_tell(captive_file_object,&offset); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); if (errgnomevfsresult==GNOME_VFS_OK) g_assert((gint64)offset==GET_PROP_GINT64("offset")); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_remove")) { captive_file_object=CAPTIVE_FILE_OBJECT(object); errgnomevfsresult=captive_file_remove(captive_file_object); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_file_info_get")) { CaptiveFileInfoObject *captive_file_info_object; captive_file_object=CAPTIVE_FILE_OBJECT(object); errgnomevfsresult=captive_file_file_info_get(captive_file_object,&captive_file_info_object); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); if (GNOME_VFS_OK==errgnomevfsresult) g_object_unref(captive_file_info_object); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_file_info_set")) { CaptiveFileInfoObject *captive_file_info_object; captive_file_object=CAPTIVE_FILE_OBJECT(object); /* FIXME: We do not have 'captive_file_info_object' content in the bug file! */ errgnomevfsresult=captive_file_file_info_get(captive_file_object,&captive_file_info_object); g_assert(errgnomevfsresult==GNOME_VFS_OK); errgnomevfsresult=captive_file_file_info_set(captive_file_object,captive_file_info_object, GET_PROP_GINT64("mask")); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); g_object_unref(captive_file_info_object); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_truncate")) { captive_file_object=CAPTIVE_FILE_OBJECT(object); errgnomevfsresult=captive_file_truncate(captive_file_object, GET_PROP_GINT64("file_size")); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); continue; } if (!xmlStrcmp(xml_node->name,BAD_CAST "file_move")) { captive_file_object=CAPTIVE_FILE_OBJECT(object); errgnomevfsresult=captive_file_move(captive_file_object, GET_PROP_STRING("pathname_new"), GET_PROP_GINT64("force_replace")); g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result"))); continue; } g_error("Unknown action: %s()",xml_node->name); #undef GET_PROP_GINT64 #undef GET_PROP_STRING } g_error("All actions were processed, no vfs_close() found"); return EXIT_FAILURE; }