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/client.h>
33 #include <captive/libxml.h>
34 #include <captive/options.h>
35 #include <captive/macros.h>
36 #include "../../libcaptive/client/giochannel-blind.h" /* FIXME: pathname */
37 #include <captive/client-vfs.h>
38 #include <captive/client-directory.h>
39 #include <captive/client-file.h>
42 static gchar *optarg_module_path;
44 static const struct poptOption popt_table[]={
45 #define BUG_REPLAY_POPT(longname,argInfoP,argP,descripP,argDescripP) \
47 longName: (longname), \
49 argInfo: (argInfoP), \
52 descrip: (descripP), \
53 argDescrip: (argDescripP), \
56 BUG_REPLAY_POPT("module-path",POPT_ARG_STRING,&optarg_module_path,N_("Disk path for W32 modules and filesystem"),N_("path")),
58 #undef BUG_REPLAY_POPT
64 static void object_hash_key_destroy_func(const xmlChar *xml_object)
66 g_return_if_fail(xml_object!=NULL);
68 xmlFree((xmlChar *)xml_object);
71 static void object_hash_value_destroy_func(GObject *captive_any_object)
73 g_return_if_fail(G_IS_OBJECT(captive_any_object));
75 g_object_unref(captive_any_object);
79 int main(int argc,char **argv)
83 const char **cmd_argv;
84 struct captive_options options;
85 xmlTextReader *xml_reader;
86 GList *action_list; /* of 'xmlNode *' */
88 gboolean module_is_filesystem=FALSE;
89 CaptiveVfsObject *captive_vfs_object;
90 gboolean action_is_first;
92 GHashTable *object_hash;
95 CaptiveDirectoryObject *captive_directory_object;
96 CaptiveFileObject *captive_file_object;
97 GnomeVFSResult errgnomevfsresult;
98 struct captive_libxml_string_drop_stack *drop_stack=NULL;
99 const gchar *xml_object;
101 captive_standalone_init();
103 captive_options_init(&options);
105 context=poptGetContext(
107 argc,(/*en-const*/const char **)argv, /* argc,argv */
108 popt_table, /* options */
109 POPT_CONTEXT_POSIXMEHARDER); /* flags; && !POPT_CONTEXT_KEEP_FIRST */
111 g_assert_not_reached(); /* argument recognization args_error */
114 errint=poptReadDefaultConfig(context,
117 g_assert_not_reached(); /* argument recognization args_error */
120 errint=poptGetNextOpt(context);
122 g_assert_not_reached(); /* some non-callbacked argument reached */
125 cmd_argv=poptGetArgs(context);
127 if (!cmd_argv || !cmd_argv[0] || cmd_argv[1]) {
128 g_error("Exactly one argument required - pathname to .captivebug.xml.gz bugreport to replay");
132 options.rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
133 options.debug_messages=TRUE;
135 options.image_iochannel=NULL;
137 options.media=CAPTIVE_OPTION_MEDIA_UNKNOWN;
139 xml_reader=xmlNewTextReaderFilename(cmd_argv[0]);
140 g_assert(xml_reader!=NULL);
142 while (preread || 1==xmlTextReaderRead(xml_reader)) {
144 switch (xmlTextReaderNodeType(xml_reader)) {
146 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT:
149 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_SIGNIFICANT_WHITESPACE:
152 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */
155 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: /* We do not track tag ends. */
158 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: {
159 const xmlChar *xml_name;
161 xml_name=xmlTextReaderName(xml_reader);
162 /**/ if (!xmlStrcmp(xml_name,BAD_CAST "bug")) { /* root tag */
164 else if (!xmlStrcmp(xml_name,BAD_CAST "captive")) {
165 const gchar *xml_captive_version;
167 xml_captive_version=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,BAD_CAST "version"));
168 g_assert(xml_captive_version!=NULL);
169 #ifndef MAINTAINER_MODE
170 g_assert(!strcmp(xml_captive_version,VERSION));
173 else if (!xmlStrcmp(xml_name,BAD_CAST "filesystem")) { /* contains <module/> */
174 module_is_filesystem=TRUE;
176 else if (!xmlStrcmp(xml_name,BAD_CAST "load_module")) { /* contains <module/> */
177 module_is_filesystem=FALSE;
179 else if (!xmlStrcmp(xml_name,BAD_CAST "module")) {
180 const gchar *xml_module_basename;
181 struct captive_options_module *options_module;
184 if (module_is_filesystem)
185 options_module=&options.filesystem;
187 captive_new(options_module);
188 options.load_module=g_list_append(options.load_module,options_module);
190 xml_module_basename=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,BAD_CAST "basename"));
191 g_assert(xml_module_basename!=NULL);
192 g_assert(strchr(xml_module_basename,'/')==NULL); /* a bit of security */
193 errbool=captive_options_module_load(options_module,
194 captive_printf_alloca("%s/%s",
195 (optarg_module_path ? optarg_module_path : "."),xml_module_basename));
196 g_assert(errbool==TRUE);
197 switch (options_module->type) {
198 case CAPTIVE_OPTIONS_MODULE_TYPE_PE32: {
199 const gchar *xml_module_md5,*xml_module_length;
201 if (options_module->u.pe32.md5
202 && (xml_module_md5=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,BAD_CAST "version"))))
203 g_assert(!strcmp(xml_module_md5,options_module->u.pe32.md5));
204 if ((xml_module_length=captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,BAD_CAST "length"))))
205 g_assert(!strcmp(xml_module_length,
206 captive_printf_alloca("%lu",(unsigned long)options_module->u.pe32.length)));
208 case CAPTIVE_OPTIONS_MODULE_TYPE_GMODULE:
209 /* No possibility to check anything. */
211 default: g_assert_not_reached();
214 else if (!xmlStrcmp(xml_name,BAD_CAST "action")) {
215 int xml_action_depth;
216 gboolean action_done,action_preread;
218 g_assert(action_list==NULL);
219 xml_action_depth=xmlTextReaderDepth(xml_reader);
220 action_preread=FALSE;
222 while (!action_done) {
223 if (!action_preread) {
224 errint=xmlTextReaderRead(xml_reader);
227 action_preread=FALSE;
228 g_assert(xml_action_depth<=xmlTextReaderDepth(xml_reader));
229 switch (xmlTextReaderNodeType(xml_reader)) {
230 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT:
232 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_SIGNIFICANT_WHITESPACE:
234 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */
236 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: { /* We do not track tag ends. */
237 if ((action_done=(xml_action_depth==xmlTextReaderDepth(xml_reader)))) {
238 const gchar *xml_name_end;
240 xml_name_end=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader));
241 g_assert(!strcmp(xml_name_end,"action"));
244 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: {
247 xml_node=xmlTextReaderExpand(xml_reader);
248 g_assert(xml_node!=NULL);
249 xml_node=xmlCopyNode(xml_node,
251 errint=xmlTextReaderNext(xml_reader);
254 action_list=g_list_prepend(action_list,xml_node); /* we are prepending to be effective */
256 default: g_assert_not_reached();
260 else if (!xmlStrcmp(xml_name,BAD_CAST "media")) {
261 const xmlChar *xml_media_end_name,*xml_media_type;
263 g_assert(options.image_iochannel==NULL);
264 options.image_iochannel=(GIOChannel *)captive_giochannel_blind_new_from_xml(xml_reader);
265 g_assert(CAPTIVE_XML_TEXT_READER_NODE_TYPE_END==xmlTextReaderNodeType(xml_reader));
266 xml_media_end_name=xmlTextReaderName(xml_reader);
267 g_assert(xml_media_end_name!=NULL);
268 g_assert(!xmlStrcmp(xml_media_end_name,BAD_CAST "media"));
269 xmlFree((xmlChar *)xml_media_end_name);
270 xml_media_type=xmlTextReaderGetAttribute(xml_reader,BAD_CAST "type");
271 g_assert(xml_media_type!=NULL);
272 /**/ if (!xmlStrcmp(xml_media_type,BAD_CAST "cdrom"))
273 options.media=CAPTIVE_OPTION_MEDIA_CDROM;
274 else if (!xmlStrcmp(xml_media_type,BAD_CAST "disk"))
275 options.media=CAPTIVE_OPTION_MEDIA_DISK;
276 else g_assert_not_reached();
277 xmlFree((xmlChar *)xml_media_type);
279 else if (!xmlStrcmp(xml_name,BAD_CAST "log")) {
280 errint=xmlTextReaderNext(xml_reader);
284 else g_error("Unknown START node: %s",xml_name);
285 xmlFree((xmlChar *)xml_name);
288 default: g_assert_not_reached();
290 captive_libxml_string_drop_flush(&drop_stack);
292 xmlFreeTextReader(xml_reader);
294 g_assert(options.image_iochannel!=NULL);
295 action_list=g_list_reverse(action_list); /* we were g_list_prepend()ing for effectivity */
296 g_assert(options.media!=CAPTIVE_OPTION_MEDIA_UNKNOWN);
298 if (GNOME_VFS_OK!=captive_vfs_new(&captive_vfs_object,&options)) {
299 g_error(_("captive_vfs_new() failed"));
302 captive_options_free(&options);
304 /* 'cmd_argv' gets cleared by 'poptFreeContext(context);' below */
305 poptFreeContext(context);
307 object_hash=g_hash_table_new_full(
308 g_str_hash, /* hash_func */
309 g_str_equal, /* key_equal_func */
310 (GDestroyNotify)object_hash_key_destroy_func, /* key_destroy_func */
311 (GDestroyNotify)object_hash_value_destroy_func); /* value_destroy_func */
314 action_is_first=TRUE;
316 xmlFreeNode(xml_node),action_is_first=FALSE) {
318 captive_libxml_string_drop_flush(&drop_stack);
320 #define GET_PROP_STRING(prop_name) ({ \
321 const gchar *_get_prop_string_r=captive_libxml_string_drop(&drop_stack,xmlGetProp(xml_node,BAD_CAST (prop_name))); \
322 g_assert(_get_prop_string_r!=NULL); \
323 _get_prop_string_r; \
325 #define GET_PROP_GINT64(prop_name) (captive_libxml_sscanf_gint64(GET_PROP_STRING((prop_name))))
327 xml_node=action_list->data;
328 g_assert(xml_node!=NULL);
329 action_list=g_list_delete_link(action_list,action_list);
330 g_message("replay action: %s()",xml_node->name);
331 xml_object=GET_PROP_STRING("object");
332 object=g_hash_table_lookup(object_hash,xml_object);
333 g_assert(object==NULL || G_IS_OBJECT(object));
335 if (!xmlStrcmp(xml_node->name,BAD_CAST "vfs_new")) {
336 g_assert(action_is_first==TRUE);
337 g_assert(captive_vfs_object!=NULL);
338 g_assert(object==NULL);
339 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_vfs_object);
342 if (!xmlStrcmp(xml_node->name,BAD_CAST "vfs_commit")) {
343 g_assert(action_is_first==TRUE);
344 g_assert(captive_vfs_object!=NULL);
345 g_assert(object==NULL);
346 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_vfs_object);
349 g_assert(action_is_first==FALSE);
350 if (!xmlStrcmp(xml_node->name,BAD_CAST "vfs_close")) {
351 g_assert(captive_vfs_object!=NULL);
352 g_assert(CAPTIVE_VFS_OBJECT(object)==captive_vfs_object);
353 errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */
354 g_assert(errbool==TRUE);
357 g_assert(captive_vfs_object!=NULL);
358 if (!xmlStrcmp(xml_node->name,BAD_CAST "vfs_volume_info_get")) {
359 CaptiveVfsVolumeInfo volume_info;
361 errgnomevfsresult=captive_vfs_volume_info_get(captive_vfs_object,&volume_info);
362 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
367 if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_new_open")) {
368 g_assert(object==NULL);
369 errgnomevfsresult=captive_directory_new_open(&captive_directory_object,captive_vfs_object,
370 GET_PROP_STRING("pathname"));
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,BAD_CAST "directory_new_make")) {
377 g_assert(object==NULL);
378 errgnomevfsresult=captive_directory_new_make(&captive_directory_object,captive_vfs_object,
379 GET_PROP_STRING("pathname"),
380 GET_PROP_GINT64("perm"));
381 g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result"));
382 if (errgnomevfsresult==GNOME_VFS_OK)
383 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_directory_object);
386 if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_close")) {
387 captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object);
388 errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */
389 g_assert(errbool==TRUE);
392 if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_read")) {
393 CaptiveFileInfoObject *captive_file_info_object;
395 captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object);
396 errgnomevfsresult=captive_directory_read(captive_directory_object,&captive_file_info_object);
397 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
398 if (GNOME_VFS_OK==errgnomevfsresult)
399 g_object_unref(captive_file_info_object);
402 if (!xmlStrcmp(xml_node->name,BAD_CAST "directory_remove")) {
403 captive_directory_object=CAPTIVE_DIRECTORY_OBJECT(object);
404 errgnomevfsresult=captive_directory_remove(captive_directory_object);
405 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
410 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_new_open")) {
411 g_assert(object==NULL);
412 errgnomevfsresult=captive_file_new_open(&captive_file_object,captive_vfs_object,
413 GET_PROP_STRING("pathname"),
414 GET_PROP_GINT64("mode"));
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,BAD_CAST "file_new_create")) {
421 g_assert(object==NULL);
422 errgnomevfsresult=captive_file_new_create(&captive_file_object,captive_vfs_object,
423 GET_PROP_STRING("pathname"),
424 GET_PROP_GINT64("mode"),
425 GET_PROP_GINT64("exclusive"),
426 GET_PROP_GINT64("perm"));
427 g_assert((errgnomevfsresult==GNOME_VFS_OK)==GET_PROP_GINT64("result"));
428 if (errgnomevfsresult==GNOME_VFS_OK)
429 g_hash_table_insert(object_hash,g_strdup(xml_object),captive_file_object);
432 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_close")) {
433 captive_file_object=CAPTIVE_FILE_OBJECT(object);
434 errbool=g_hash_table_remove(object_hash,xml_object); /* g_object_unref() by object_hash_value_destroy_func() */
435 g_assert(errbool==TRUE);
438 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_read")) {
440 GnomeVFSFileSize num_bytes,bytes_read;
442 captive_file_object=CAPTIVE_FILE_OBJECT(object);
443 num_bytes=GET_PROP_GINT64("num_bytes");
444 buffer=g_malloc(num_bytes);
445 errgnomevfsresult=captive_file_read(captive_file_object,buffer,num_bytes,&bytes_read);
446 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
447 if (errgnomevfsresult==GNOME_VFS_OK)
448 g_assert((gint64)bytes_read==GET_PROP_GINT64("bytes_read_return"));
452 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_write")) {
454 GnomeVFSFileSize num_bytes,bytes_written;
456 captive_file_object=CAPTIVE_FILE_OBJECT(object);
457 num_bytes=GET_PROP_GINT64("num_bytes");
458 buffer=g_malloc(num_bytes);
459 memset(buffer,'X',num_bytes); /* FIXME: better pattern */
460 errgnomevfsresult=captive_file_write(captive_file_object,buffer,num_bytes,&bytes_written);
461 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
462 if (errgnomevfsresult==GNOME_VFS_OK)
463 g_assert((gint64)bytes_written==GET_PROP_GINT64("bytes_written_return"));
467 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_seek")) {
468 GnomeVFSSeekPosition whence=GNOME_VFS_SEEK_START; /* Prevent: ... might be used uninitialized in this function */
469 const gchar *whence_string;
471 captive_file_object=CAPTIVE_FILE_OBJECT(object);
472 whence_string=GET_PROP_STRING("whence");
473 /**/ if (!strcmp(whence_string,"start" )) whence=GNOME_VFS_SEEK_START;
474 else if (!strcmp(whence_string,"current")) whence=GNOME_VFS_SEEK_CURRENT;
475 else if (!strcmp(whence_string,"end" )) whence=GNOME_VFS_SEEK_END;
476 else g_assert_not_reached();
477 errgnomevfsresult=captive_file_seek(captive_file_object,whence,
478 GET_PROP_GINT64("offset"));
479 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
482 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_tell")) {
483 GnomeVFSFileOffset offset;
485 captive_file_object=CAPTIVE_FILE_OBJECT(object);
486 errgnomevfsresult=captive_file_tell(captive_file_object,&offset);
487 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
488 if (errgnomevfsresult==GNOME_VFS_OK)
489 g_assert((gint64)offset==GET_PROP_GINT64("offset"));
492 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_remove")) {
493 captive_file_object=CAPTIVE_FILE_OBJECT(object);
494 errgnomevfsresult=captive_file_remove(captive_file_object);
495 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
498 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_file_info_get")) {
499 CaptiveFileInfoObject *captive_file_info_object;
501 captive_file_object=CAPTIVE_FILE_OBJECT(object);
502 errgnomevfsresult=captive_file_file_info_get(captive_file_object,&captive_file_info_object);
503 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
504 if (GNOME_VFS_OK==errgnomevfsresult)
505 g_object_unref(captive_file_info_object);
508 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_file_info_set")) {
509 CaptiveFileInfoObject *captive_file_info_object;
511 captive_file_object=CAPTIVE_FILE_OBJECT(object);
512 /* FIXME: We do not have 'captive_file_info_object' content in the bug file! */
513 errgnomevfsresult=captive_file_file_info_get(captive_file_object,&captive_file_info_object);
514 g_assert(errgnomevfsresult==GNOME_VFS_OK);
515 errgnomevfsresult=captive_file_file_info_set(captive_file_object,captive_file_info_object,
516 GET_PROP_GINT64("mask"));
517 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
518 g_object_unref(captive_file_info_object);
521 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_truncate")) {
522 captive_file_object=CAPTIVE_FILE_OBJECT(object);
523 errgnomevfsresult=captive_file_truncate(captive_file_object,
524 GET_PROP_GINT64("file_size"));
525 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
528 if (!xmlStrcmp(xml_node->name,BAD_CAST "file_move")) {
529 captive_file_object=CAPTIVE_FILE_OBJECT(object);
530 errgnomevfsresult=captive_file_move(captive_file_object,
531 GET_PROP_STRING("pathname_new"),
532 GET_PROP_GINT64("force_replace"));
533 g_assert(!strcmp(gnome_vfs_result_to_string(errgnomevfsresult),GET_PROP_STRING("result")));
537 g_error("Unknown action: %s()",xml_node->name);
539 #undef GET_PROP_GINT64
540 #undef GET_PROP_STRING
543 g_error("All actions were processed, no vfs_close() found");