2 * developer replaying of bugreport snapshots
3 * Copyright (C) 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 <glib/gmessages.h>
24 #include <glib/giochannel.h>
28 #include <libxml/xmlreader.h>
29 #include <glib-object.h> /* for g_type_init() */
30 #include <glib/ghash.h>
32 #include <captive/libxml.h>
33 #include <captive/options.h>
34 #include <captive/macros.h>
35 #include "../../libcaptive/client/giochannel-blind.h" /* FIXME: pathname */
36 #include <captive/client-vfs.h>
37 #include <captive/client-directory.h>
38 #include <captive/client-file.h>
41 static gchar *optarg_module_path;
43 static const struct poptOption popt_table[]={
44 #define BUG_REPLAY_POPT(longname,argInfoP,argP,descripP,argDescripP) \
46 longName: (longname), \
48 argInfo: (argInfoP), \
51 descrip: (descripP), \
52 argDescrip: (argDescripP), \
55 BUG_REPLAY_POPT("module-path",POPT_ARG_STRING,&optarg_module_path,N_("Disk path for W32 modules and filesystem"),N_("path")),
57 #undef BUG_REPLAY_POPT
63 static void object_hash_key_destroy_func(const xmlChar *xml_object)
65 g_return_if_fail(xml_object!=NULL);
67 xmlFree((xmlChar *)xml_object);
70 static void object_hash_value_destroy_func(GObject *captive_any_object)
72 g_return_if_fail(G_IS_OBJECT(captive_any_object));
74 g_object_unref(captive_any_object);
78 int main(int argc,char **argv)
82 const char **cmd_argv;
83 struct captive_options options;
84 xmlTextReader *xml_reader;
85 GList *action_list; /* of 'xmlNode *' */
87 gboolean module_is_filesystem=FALSE;
88 CaptiveVfsObject *captive_vfs_object;
89 gboolean action_is_first;
91 GHashTable *object_hash;
94 CaptiveDirectoryObject *captive_directory_object;
95 CaptiveFileObject *captive_file_object;
96 GnomeVFSResult errgnomevfsresult;
97 struct captive_libxml_string_drop_stack *drop_stack=NULL;
98 const xmlChar *xml_object;
100 /* Prevent output block buffering if redirecting stdout to file. */
101 setvbuf(stdout,(char *)NULL,_IONBF,0);
102 setvbuf(stderr,(char *)NULL,_IONBF,0);
104 /* Initialize GObject subsystem of GLib. */
107 captive_options_init(&options);
109 context=poptGetContext(
111 argc,(/*en-const*/const char **)argv, /* argc,argv */
112 popt_table, /* options */
113 POPT_CONTEXT_POSIXMEHARDER); /* flags; && !POPT_CONTEXT_KEEP_FIRST */
115 g_assert_not_reached(); /* argument recognization args_error */
118 errint=poptReadDefaultConfig(context,
121 g_assert_not_reached(); /* argument recognization args_error */
124 errint=poptGetNextOpt(context);
126 g_assert_not_reached(); /* some non-callbacked argument reached */
129 cmd_argv=poptGetArgs(context);
131 if (!cmd_argv || !cmd_argv[0] || cmd_argv[1]) {
132 g_error("Exactly one argument required - pathname to .captivebug.xml.gz bugreport to replay");
136 options.rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
137 options.debug_messages=TRUE;
139 options.image_iochannel=NULL;
141 options.media=CAPTIVE_OPTION_MEDIA_UNKNOWN;
143 xml_reader=xmlNewTextReaderFilename(cmd_argv[0]);
144 g_assert(xml_reader!=NULL);
146 while (preread || 1==xmlTextReaderRead(xml_reader)) {
148 switch (xmlTextReaderNodeType(xml_reader)) {
150 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT:
153 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */
156 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: /* We do not track tag ends. */
159 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: {
160 const xmlChar *xml_name;
162 xml_name=xmlTextReaderName(xml_reader);
163 /**/ if (!xmlStrcmp(xml_name,"bug")) { /* root tag */
165 else if (!xmlStrcmp(xml_name,"captive")) {
166 const xmlChar *xml_captive_version;
168 xml_captive_version=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,"version"));
169 g_assert(xml_captive_version!=NULL);
170 g_assert(!xmlStrcmp(xml_captive_version,VERSION));
172 else if (!xmlStrcmp(xml_name,"filesystem")) { /* contains <module/> */
173 module_is_filesystem=TRUE;
175 else if (!xmlStrcmp(xml_name,"load_module")) { /* contains <module/> */
176 module_is_filesystem=FALSE;
178 else if (!xmlStrcmp(xml_name,"module")) {
179 const xmlChar *xml_module_basename;
180 struct captive_options_module *options_module;
183 if (module_is_filesystem)
184 options_module=&options.filesystem;
186 captive_new(options_module);
187 options.load_module=g_list_append(options.load_module,options_module);
189 xml_module_basename=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,"basename"));
190 g_assert(xml_module_basename!=NULL);
191 g_assert(strchr(xml_module_basename,'/')==NULL); /* a bit of security */
192 errbool=captive_options_module_load(options_module,
193 captive_printf_alloca("%s/%s",
194 (optarg_module_path ? optarg_module_path : "."),xml_module_basename));
195 g_assert(errbool==TRUE);
196 switch (options_module->type) {
197 case CAPTIVE_OPTIONS_MODULE_TYPE_PE32: {
198 const xmlChar *xml_module_md5,*xml_module_length;
200 if (options_module->u.pe32.md5
201 && (xml_module_md5=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,"version"))))
202 g_assert(!xmlStrcmp(xml_module_md5,options_module->u.pe32.md5));
203 if ((xml_module_length=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,"length"))))
204 g_assert(!xmlStrcmp(xml_module_length,
205 captive_printf_alloca("%lu",(unsigned long)options_module->u.pe32.length)));
207 case CAPTIVE_OPTIONS_MODULE_TYPE_GMODULE:
208 /* No possibility to check anything. */
210 default: g_assert_not_reached();
213 else if (!xmlStrcmp(xml_name,"action")) {
214 int xml_action_depth;
215 gboolean action_done,action_preread;
217 g_assert(action_list==NULL);
218 xml_action_depth=xmlTextReaderDepth(xml_reader);
219 action_preread=FALSE;
221 while (!action_done) {
222 if (!action_preread) {
223 errint=xmlTextReaderRead(xml_reader);
226 action_preread=FALSE;
227 g_assert(xml_action_depth<=xmlTextReaderDepth(xml_reader));
228 switch (xmlTextReaderNodeType(xml_reader)) {
229 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT:
231 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */
233 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: { /* We do not track tag ends. */
234 if ((action_done=(xml_action_depth==xmlTextReaderDepth(xml_reader)))) {
235 const xmlChar *xml_name_end;
237 xml_name_end=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader));
238 g_assert(!xmlStrcmp(xml_name_end,"action"));
241 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: {
244 xml_node=xmlTextReaderExpand(xml_reader);
245 g_assert(xml_node!=NULL);
246 xml_node=xmlCopyNode(xml_node,
248 errint=xmlTextReaderNext(xml_reader);
251 action_list=g_list_prepend(action_list,xml_node); /* we are prepending to be effective */
253 default: g_assert_not_reached();
257 else if (!xmlStrcmp(xml_name,"media")) {
258 const xmlChar *xml_media_end_name,*xml_media_type;
260 g_assert(options.image_iochannel==NULL);
261 options.image_iochannel=(GIOChannel *)captive_giochannel_blind_new_from_xml(xml_reader);
262 g_assert(CAPTIVE_XML_TEXT_READER_NODE_TYPE_END==xmlTextReaderNodeType(xml_reader));
263 xml_media_end_name=xmlTextReaderName(xml_reader);
264 g_assert(xml_media_end_name!=NULL);
265 g_assert(!xmlStrcmp(xml_media_end_name,"media"));
266 xmlFree((xmlChar *)xml_media_end_name);
267 xml_media_type=xmlTextReaderGetAttribute(xml_reader,"type");
268 g_assert(xml_media_type!=NULL);
269 /**/ if (!xmlStrcmp(xml_media_type,"cdrom"))
270 options.media=CAPTIVE_OPTION_MEDIA_CDROM;
271 else if (!xmlStrcmp(xml_media_type,"disk"))
272 options.media=CAPTIVE_OPTION_MEDIA_DISK;
273 else g_assert_not_reached();
274 xmlFree((xmlChar *)xml_media_type);
276 else if (!xmlStrcmp(xml_name,"log")) {
277 errint=xmlTextReaderNext(xml_reader);
281 else g_error("Unknown START node: %s",xml_name);
282 xmlFree((xmlChar *)xml_name);
285 default: g_assert_not_reached();
287 captive_libxml_string_drop_flush(&drop_stack);
289 xmlFreeTextReader(xml_reader);
291 g_assert(options.image_iochannel!=NULL);
292 action_list=g_list_reverse(action_list); /* we were g_list_prepend()ing for effectivity */
293 g_assert(options.media!=CAPTIVE_OPTION_MEDIA_UNKNOWN);
295 if (GNOME_VFS_OK!=captive_vfs_new(&captive_vfs_object,&options)) {
296 g_error(_("captive_vfs_new() failed"));
299 captive_options_free(&options);
301 /* 'cmd_argv' gets cleared by 'poptFreeContext(context);' below */
302 poptFreeContext(context);
304 object_hash=g_hash_table_new_full(
305 g_str_hash, /* hash_func */
306 g_str_equal, /* key_equal_func */
307 (GDestroyNotify)object_hash_key_destroy_func, /* key_destroy_func */
308 (GDestroyNotify)object_hash_value_destroy_func); /* value_destroy_func */
311 action_is_first=TRUE;
313 xmlFreeNode(xml_node),action_is_first=FALSE) {
315 captive_libxml_string_drop_flush(&drop_stack);
317 #define GET_PROP_STRING(prop_name) ({ \
318 const xmlChar *_get_prop_string_r=captive_libxml_string_drop(&drop_stack,xmlGetProp(xml_node,(prop_name))); \
319 g_assert(_get_prop_string_r!=NULL); \
320 _get_prop_string_r; \
322 #define GET_PROP_GINT64(prop_name) (captive_libxml_sscanf_gint64(GET_PROP_STRING((prop_name))))
324 xml_node=action_list->data;
325 g_assert(xml_node!=NULL);
326 action_list=g_list_delete_link(action_list,action_list);
327 g_message("replay action: %s()",xml_node->name);
328 xml_object=GET_PROP_STRING("object");
329 object=g_hash_table_lookup(object_hash,xml_object);
330 g_assert(object==NULL || G_IS_OBJECT(object));
332 if (!xmlStrcmp(xml_node->name,"vfs_new")) {
333 g_assert(action_is_first==TRUE);
334 g_assert(captive_vfs_object!=NULL);
335 g_assert(object==NULL);
336 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_vfs_object);
339 if (!xmlStrcmp(xml_node->name,"vfs_commit")) {
340 g_assert(action_is_first==TRUE);
341 g_assert(captive_vfs_object!=NULL);
342 g_assert(object==NULL);
343 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_vfs_object);
346 g_assert(action_is_first==FALSE);
347 if (!xmlStrcmp(xml_node->name,"vfs_close")) {
348 g_assert(captive_vfs_object!=NULL);
349 g_assert(CAPTIVE_VFS_OBJECT(object)==captive_vfs_object);
350 errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */
351 g_assert(errbool==TRUE);
354 g_assert(captive_vfs_object!=NULL);
357 if (!xmlStrcmp(xml_node->name,"directory_new_open")) {
358 g_assert(object==NULL);
359 errgnomevfsresult=captive_directory_new_open(&captive_directory_object,captive_vfs_object,
360 GET_PROP_STRING("pathname"));
361 g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result"));
362 if (errgnomevfsresult==GNOME_VFS_OK)
363 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_directory_object);
366 if (!xmlStrcmp(xml_node->name,"directory_new_make")) {
367 g_assert(object==NULL);
368 errgnomevfsresult=captive_directory_new_make(&captive_directory_object,captive_vfs_object,
369 GET_PROP_STRING("pathname"),
370 GET_PROP_GINT64("perm"));
371 g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result"));
372 if (errgnomevfsresult==GNOME_VFS_OK)
373 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_directory_object);
376 if (!xmlStrcmp(xml_node->name,"directory_close")) {
377 captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object);
378 errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */
379 g_assert(errbool==TRUE);
382 if (!xmlStrcmp(xml_node->name,"directory_read")) {
383 GnomeVFSFileInfo file_info;
385 captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object);
386 errgnomevfsresult=captive_directory_read(captive_directory_object,&file_info);
387 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
390 if (!xmlStrcmp(xml_node->name,"directory_remove")) {
391 captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object);
392 errgnomevfsresult=captive_directory_remove(captive_directory_object);
393 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
398 if (!xmlStrcmp(xml_node->name,"file_new_open")) {
399 g_assert(object==NULL);
400 errgnomevfsresult=captive_file_new_open(&captive_file_object,captive_vfs_object,
401 GET_PROP_STRING("pathname"),
402 GET_PROP_GINT64("mode"));
403 g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result"));
404 if (errgnomevfsresult==GNOME_VFS_OK)
405 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_file_object);
408 if (!xmlStrcmp(xml_node->name,"file_new_create")) {
409 g_assert(object==NULL);
410 errgnomevfsresult=captive_file_new_create(&captive_file_object,captive_vfs_object,
411 GET_PROP_STRING("pathname"),
412 GET_PROP_GINT64("mode"),
413 GET_PROP_GINT64("exclusive"),
414 GET_PROP_GINT64("perm"));
415 g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result"));
416 if (errgnomevfsresult==GNOME_VFS_OK)
417 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_file_object);
420 if (!xmlStrcmp(xml_node->name,"file_close")) {
421 captive_file_object=CAPTIVE_FILE_OBJECT(object);
422 errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */
423 g_assert(errbool==TRUE);
426 if (!xmlStrcmp(xml_node->name,"file_read")) {
428 GnomeVFSFileSize num_bytes,bytes_read;
430 captive_file_object=CAPTIVE_FILE_OBJECT(object);
431 num_bytes=GET_PROP_GINT64("num_bytes");
432 buffer=g_malloc(num_bytes);
433 errgnomevfsresult=captive_file_read(captive_file_object,buffer,num_bytes,&bytes_read);
434 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
435 if (errgnomevfsresult==GNOME_VFS_OK)
436 g_assert((gint64)bytes_read==GET_PROP_GINT64("bytes_read_return"));
440 if (!xmlStrcmp(xml_node->name,"file_write")) {
442 GnomeVFSFileSize num_bytes,bytes_written;
444 captive_file_object=CAPTIVE_FILE_OBJECT(object);
445 num_bytes=GET_PROP_GINT64("num_bytes");
446 buffer=g_malloc(num_bytes);
447 memset(buffer,'X',num_bytes); /* FIXME: better pattern */
448 errgnomevfsresult=captive_file_write(captive_file_object,buffer,num_bytes,&bytes_written);
449 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
450 if (errgnomevfsresult==GNOME_VFS_OK)
451 g_assert((gint64)bytes_written==GET_PROP_GINT64("bytes_written_return"));
455 if (!xmlStrcmp(xml_node->name,"file_seek")) {
456 GnomeVFSSeekPosition whence;
457 const xmlChar *whence_string;
459 captive_file_object=CAPTIVE_FILE_OBJECT(object);
460 whence_string=GET_PROP_STRING("whence");
461 /**/ if (!xmlStrcmp(whence_string,"start" )) whence=GNOME_VFS_SEEK_START;
462 else if (!xmlStrcmp(whence_string,"current")) whence=GNOME_VFS_SEEK_CURRENT;
463 else if (!xmlStrcmp(whence_string,"end" )) whence=GNOME_VFS_SEEK_END;
464 else g_assert_not_reached();
465 errgnomevfsresult=captive_file_seek(captive_file_object,whence,
466 GET_PROP_GINT64("offset"));
467 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
470 if (!xmlStrcmp(xml_node->name,"file_tell")) {
471 GnomeVFSFileOffset offset;
473 captive_file_object=CAPTIVE_FILE_OBJECT(object);
474 errgnomevfsresult=captive_file_tell(captive_file_object,&offset);
475 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
476 if (errgnomevfsresult==GNOME_VFS_OK)
477 g_assert((gint64)offset==GET_PROP_GINT64("offset"));
480 if (!xmlStrcmp(xml_node->name,"file_remove")) {
481 captive_file_object=CAPTIVE_FILE_OBJECT(object);
482 errgnomevfsresult=captive_file_remove(captive_file_object);
483 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
486 if (!xmlStrcmp(xml_node->name,"file_file_info_get")) {
487 GnomeVFSFileInfo file_info;
489 captive_file_object=CAPTIVE_FILE_OBJECT(object);
490 errgnomevfsresult=captive_file_file_info_get(captive_file_object,&file_info);
491 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
494 if (!xmlStrcmp(xml_node->name,"file_file_info_set")) {
495 GnomeVFSFileInfo file_info;
497 captive_file_object=CAPTIVE_FILE_OBJECT(object);
498 /* FIXME: We do not have 'file_info' content in the bug file! */
499 errgnomevfsresult=captive_file_file_info_get(captive_file_object,&file_info);
500 g_assert(errgnomevfsresult==GNOME_VFS_OK);
501 errgnomevfsresult=captive_file_file_info_set(captive_file_object,&file_info,
502 GET_PROP_GINT64("mask"));
503 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
506 if (!xmlStrcmp(xml_node->name,"file_truncate")) {
507 captive_file_object=CAPTIVE_FILE_OBJECT(object);
508 errgnomevfsresult=captive_file_truncate(captive_file_object,
509 GET_PROP_GINT64("file_size"));
510 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
513 if (!xmlStrcmp(xml_node->name,"file_move")) {
514 captive_file_object=CAPTIVE_FILE_OBJECT(object);
515 errgnomevfsresult=captive_file_move(captive_file_object,
516 GET_PROP_STRING("pathname_new"),
517 GET_PROP_GINT64("force_replace"));
518 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
522 g_error("Unknown action: %s()",xml_node->name);
524 #undef GET_PROP_GINT64
525 #undef GET_PROP_STRING
528 g_error("All actions were processed, no vfs_close() found");