source: MondoRescue/branches/3.2/mondo/src/mondorestore/mondo-rstr-tools.c @ 3205

Last change on this file since 3205 was 3205, checked in by Bruno Cornec, 7 years ago
  • Fix mondoarchive which now works in text mode. GUI mode still not working and crashing on first update for backup. Needs more

investigation in newt-specific.c

  • Property svn:keywords set to Id
File size: 73.8 KB
Line 
1/***************************************************************************
2$Id: mondo-rstr-tools.c 3205 2013-12-06 14:40:47Z bruno $
3***************************************************************************/
4
5#include <pthread.h>
6#include <linux/fd.h>
7#include "my-stuff.h"
8#include "mr_mem.h"
9#include "mr_str.h"
10#include "../common/mondostructures.h"
11#include "../common/libmondo.h"
12#include "mr-externs.h"
13#include "mondo-rstr-tools.h"
14
15/**
16 * The biggielist stub (appended to the directory where all.tar.gz was unpacked).
17 */
18#define BIGGIELIST_TXT_STUB "tmp/biggielist.txt"
19
20/**
21 * The filelist stub (appended to the directory where all.tar.gz was unpacked).
22 */
23#define FILELIST_FULL_STUB "tmp/filelist.full.gz"
24
25/**
26 * The mountlist stub (appended to the directory where all.tar.gz was unpacked).
27 */
28#define MOUNTLIST_FNAME_STUB "tmp/mountlist.txt"
29
30/**
31 * The mondo-restore.cfg stub (appended to the directory where all.tar.gz was unpacked).
32 */
33#define MONDO_CFG_FILE_STUB "tmp/mondo-restore.cfg"
34/**
35 * The i-want-my-lvm stub
36 */
37#define IWANTMYLVM_STUB "tmp/i-want-my-lvm"
38
39extern bool g_ISO_restore_mode; /* are we in Iso Mode? */
40extern bool g_I_have_just_nuked;
41/*
42extern char *g_tmpfs_mountpt;
43*/
44
45/**
46 * The device to mount to get at the ISO images. Ignored unless @p g_ISO_restore_mode.
47 */
48char *g_isodir_device;
49
50
51/**
52 * The format of @p g_isodir_device. Ignored unless @p g_ISO_restore_mode.
53 */
54char *g_isodir_format;
55
56extern long g_current_progress, g_maximum_progress;
57extern char *g_biggielist_txt;  // where 'biggielist.txt' is stored, on ramdisk / tempdir;
58                  // biggielist.txt is the list of big files stored on the
59                  // backup media set in question
60extern char *g_filelist_full;   // filelist.full.gz is the list of all regular files
61                  // (excluding big files) stored on the backup media set
62extern char *g_biggielist_pot;  // list of big files which _could_ be restored, if the
63                  // user chooses them
64extern char *g_filelist_imagedevs;  // list of devices (e.g. /dev/hda1, /dev/sda5) which
65                     // were archived as images, not just /dev entries
66                     // ... e.g. NTFS, BeOS partitions
67extern char *g_imagedevs_restthese; // of the imagedevs listed in FILELIST_IMAGEDEVS,
68                      // restore only these
69extern char *g_mondo_cfg_file;  // where m*ndo-restore.cfg (the config file) is stored
70extern char *g_mountlist_fname; // where mountlist.txt (the mountlist file) is stored
71extern char *g_mondo_home;      // homedir of Mondo; usually /usr/local/share/mondo
72
73extern t_bkptype g_backup_media_type;
74
75extern int g_partition_table_locked_up;
76extern char *MONDO_LOGFILE;
77
78/* Reference to global bkpinfo */
79extern struct s_bkpinfo *bkpinfo;
80
81/* Should we use or not extended attributes and acl when restoring */
82char *g_getfattr = NULL;
83char *g_getfacl = NULL;
84
85extern void kill_anything_like_this(char *str);
86extern int skip_obdr(void);
87extern int mount_media();
88extern int set_tape_block_size_with_mt(long internal_tape_block_size);
89
90/**
91* @addtogroup restoreUtilityGroup
92* @{
93*/
94/**
95* Free the malloc()s for the filename variables.
96*/
97void free_MR_global_filenames(void)
98{
99paranoid_free(g_biggielist_txt);
100paranoid_free(g_filelist_full);
101paranoid_free(g_filelist_imagedevs);
102paranoid_free(g_imagedevs_restthese);
103paranoid_free(g_mondo_cfg_file);
104paranoid_free(g_mountlist_fname);
105paranoid_free(g_mondo_home);
106/*
107paranoid_free(g_tmpfs_mountpt);
108*/
109paranoid_free(g_isodir_device);
110paranoid_free(g_isodir_format);
111
112}
113
114
115
116/**
117* Ask the user which imagedevs from the list contained in @p infname should
118* actually be restored.
119* @param infname The file containing a list of all imagedevs.
120* @param outfname The location of the output file containing the imagedevs the user wanted to restore.
121* @ingroup restoreUtilityGroup
122*/
123void ask_about_these_imagedevs(char *infname, char *outfname) {
124
125FILE *fin;
126FILE *fout;
127char *incoming = NULL;
128char *question = NULL;
129
130assert_string_is_neither_NULL_nor_zerolength(infname);
131assert_string_is_neither_NULL_nor_zerolength(outfname);
132
133if (!(fin = fopen(infname, "r"))) {
134    fatal_error("Cannot openin infname");
135}
136if (!(fout = fopen(outfname, "w"))) {
137    fatal_error("Cannot openin outfname");
138}
139for (mr_getline(incoming, fin); !feof(fin) && (incoming != NULL); mr_getline(incoming, fin)) {
140    mr_strip_spaces(incoming);
141
142    if (incoming[0] == '\0') {
143        mr_free(incoming);
144        continue;
145    }
146
147    mr_asprintf(question, "Should I restore the image of %s ?", incoming);
148   
149    if (ask_me_yes_or_no(question)) {
150        fprintf(fout, "%s\n", incoming);
151    }
152    mr_free(incoming);
153}
154mr_free(incoming);
155mr_free(question);
156paranoid_fclose(fout);
157paranoid_fclose(fin);
158}
159
160/**************************************************************************
161*ASK_ABOUT_THESE_IMAGEDEVS                                               *
162**************************************************************************/
163
164
165
166
167
168
169
170
171/**
172* Extract @c mondo-restore.cfg and @c mountlist.txt from @p ramdisk_fname.
173* @param bkpinfo The backup information structure. @c tmpdir is the only field used.
174* @param ramdisk_fname The filename of the @b compressed ramdisk to look in.
175* @param output_cfg_file Where to put the configuration file extracted.
176* @param output_mountlist_file Where to put the mountlist file extracted.
177* @return 0 for success, nonzero for failure.
178* @ingroup restoreUtilityGroup
179*/
180
181/**
182* Keep trying to get mondo-restore.cfg from the archive, until the user gives up.
183*/
184void get_cfg_file_from_archive_or_bust()
185{
186while (get_cfg_file_from_archive()) {
187if (!ask_me_yes_or_no
188    ("Failed to find config file/archives. Choose another source?"))
189{
190    fatal_error("Could not find config file/archives. Aborting.");
191}
192interactively_obtain_media_parameters_from_user(FALSE);
193}
194}
195
196
197/**
198* Determine whether @p list_fname contains a line containing @p f.
199* @param f The line to search for.
200* @param list_fname The file to search in.
201* @param preamble Ignore this beginning part of @p f ("" to disable).
202* @return TRUE if it's in the list, FALSE if it's not.
203*/
204bool is_file_in_list(char *f, char *list_fname, char *preamble)
205{
206
207char *command = NULL;
208char *file = NULL;
209char *tmp = NULL;
210int res;
211
212assert_string_is_neither_NULL_nor_zerolength(f);
213assert_string_is_neither_NULL_nor_zerolength(list_fname);
214assert(preamble != NULL);
215
216if (strncmp(preamble, f, strlen(preamble)) == 0) {
217    mr_asprintf(file, "%s", f + strlen(preamble));
218} else {
219    mr_asprintf(file, "%s", f);
220}
221if (file[0] == '/' && file[1] == '/') {
222    tmp = file;
223
224    mr_asprintf(file, "%s", tmp + 1);
225    mr_free(tmp);
226}
227log_msg(2, "Checking to see if f=%s, file=%s, is in the list of biggiefiles", f, file);
228mr_asprintf(command, "grep -E '^%s$' %s", file, list_fname);
229mr_free(file);
230
231res = run_program_and_log_output(command, FALSE);
232mr_free(command);
233if (res) {
234    return (FALSE);
235} else {
236    return (TRUE);
237}
238}
239
240/**************************************************************************
241*END_IS_FILE_IN_LIST                                                     *
242**************************************************************************/
243
244/**
245 * Get information about the location of ISO images from the user.
246 * @param isodir_device Where to put the device (e.g. /dev/hda4) the user enters.
247 * @param isodir_format Where to put the format (e.g. ext2) the user enters.
248 * @param nuke_me_please Whether we're planning on nuking or not.
249 * @return TRUE if OK was pressed, FALSE otherwise.
250 */
251bool
252get_isodir_info(char *isodir_device, char *isodir_format,
253                struct s_bkpinfo *bkpinfo, bool nuke_me_please)
254{
255
256    bool HDD = FALSE;
257    /** initialize ********************************************************/
258
259    assert(isodir_device != NULL);
260    assert(isodir_format != NULL);
261    assert(bkpinfo->isodir != NULL);
262
263    log_it("bkpinfo->isodir = %s", bkpinfo->isodir);
264    isodir_format[0] = '\0';
265    if (isodir_device[0] == '\0') {
266        strcpy(isodir_device, "/dev/");
267    }
268    if (bkpinfo->isodir[0] == '\0') {
269        strcpy(bkpinfo->isodir, "/");
270    }
271    if (does_file_exist("/tmp/NETFS-SERVER-PATH")) {
272        strcpy(isodir_device, last_line_of_file("/tmp/NETFS-SERVER-MOUNT"));
273        strcpy(isodir_format, "netfs");
274        strcpy(bkpinfo->isodir, last_line_of_file("/tmp/NETFS-SERVER-PATH"));
275    }
276    if (nuke_me_please) {
277        return (TRUE);
278    }
279
280    if (popup_and_get_string
281        ("ISO Mode - device", "On what device do the ISO files live?",
282         isodir_device, MAX_STR_LEN / 4)) {
283        if (popup_and_get_string
284            ("ISO Mode - format",
285             "What is the disk format of the device? (Hit ENTER if you don't know.)",
286             isodir_format, 16)) {
287            if (popup_with_buttons("Are you restoring from an external hard drive ?", "Yes", "No")) {
288                HDD = TRUE;
289            }
290            if (popup_and_get_string
291                ("ISO Mode - path",
292                "At what path on this device can the ISO files be found ?",
293                bkpinfo->isodir, MAX_STR_LEN / 4)) {
294                strip_spaces(isodir_device);
295                strip_spaces(isodir_format);
296                strip_spaces(bkpinfo->isodir);
297                log_it("isodir_device = %s - isodir_format = %s - bkpinfo->isodir = %s", isodir_device, isodir_format, bkpinfo->isodir);
298                if (HDD) {
299                    /*  We need an additional param */
300                    mr_asprintf(bkpinfo->subdir, "%s", bkpinfo->isodir);
301                    strcpy(bkpinfo->isodir, "/tmp/isodir");
302                    log_it("Now bkpinfo->isodir = %s and subdir = ", bkpinfo->isodir, bkpinfo->subdir);
303                }
304                return (TRUE);
305            }
306        }
307    }
308    return (FALSE);
309}
310
311
312/**
313* Set up an ISO backup.
314* @param bkpinfo The backup information structure. Fields used:
315* - @c bkpinfo->backup_media_type
316* - @c bkpinfo->disaster_recovery
317* - @c bkpinfo->isodir
318* @param nuke_me_please If TRUE, we're in nuke mode; if FALSE we're in interactive mode.
319* @return 0 for success, nonzero for failure.
320*/
321int iso_fiddly_bits(bool nuke_me_please) {
322
323char *mount_isodir_command = NULL;
324char *command = NULL;
325char *mds = NULL;
326int retval = 0, i;
327
328g_ISO_restore_mode = TRUE;
329read_cfg_var(g_mondo_cfg_file, "iso-dev", g_isodir_device);
330if (bkpinfo->disaster_recovery) {
331    /* Patch Conor Daly 26-june-2004
332    * Don't let this clobber an existing bkpinfo->isodir */
333    if (!bkpinfo->isodir[0]) {
334        strcpy(bkpinfo->isodir, "/tmp/isodir");
335    }
336    /* End patch */
337    mr_asprintf(command, "mkdir -p %s", bkpinfo->isodir);
338    run_program_and_log_output(command, 5);
339    mr_free(command);
340    log_msg(2, "Setting isodir to %s", bkpinfo->isodir);
341}
342
343if (!get_isodir_info(g_isodir_device, g_isodir_format, bkpinfo, nuke_me_please)) {
344    return (1);
345}
346paranoid_system("umount -d " MNT_CDROM " 2> /dev/null");    /* just in case */
347
348if (is_this_device_mounted(g_isodir_device)) {
349    log_to_screen("WARNING - isodir is already mounted");
350} else {
351    mr_asprintf(mount_isodir_command, "mount %s", g_isodir_device);
352    if (strlen(g_isodir_format) > 1) {
353        mr_strcat(mount_isodir_command, " -t %s", g_isodir_format);
354    }
355
356    mr_strcat(mount_isodir_command, " -o ro %s", bkpinfo->isodir);
357    run_program_and_log_output("df -m -P", FALSE);
358    log_msg(1, "The 'mount' command is '%s'. PLEASE report this command to be if you have problems, ok?", mount_isodir_command);
359    if (run_program_and_log_output(mount_isodir_command, FALSE)) {
360        popup_and_OK
361            ("Cannot mount the device where the ISO files are stored.");
362        return (1);
363    }
364    paranoid_free(mount_isodir_command);
365    log_to_screen
366    ("I have mounted the device where the ISO files are stored.");
367}
368if (!IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
369    mount_media();
370}
371i = what_number_cd_is_this();   /* has the side-effect of calling mount_media() */
372mds = media_descriptor_string(bkpinfo->backup_media_type);
373mr_free(mds);
374
375log_msg(1, "%s #%d has been mounted via loopback mount", mds, i);
376if (i < 0) {
377    popup_and_OK("Cannot find ISO images in the directory you specified.");
378    retval = 1;
379}
380log_msg(2, "bkpinfo->isodir is now %s", bkpinfo->isodir);
381return (retval);
382}
383
384
385
386
387/**
388* Kill all Petris processes.
389*/
390void kill_petris(void) {
391    kill_anything_like_this("petris");
392}
393
394/**************************************************************************
395*END_KILL_PETRIS                                                         *
396**************************************************************************/
397
398
399/**
400 * Mount @p device at @p mpt as @p format.
401 * @param device The device (/dev entry) to mount.
402 * @param mpt The directory to mount it on.
403 * @param format The filesystem type of @p device.
404 * @param writeable If TRUE, mount read-write; if FALSE, mount read-only.
405 * @return 0 for success, nonzero for failure.
406 */
407int mount_device(char *device, char *mpt, char *format, bool writeable)
408{
409int res = 0;
410
411/** malloc **/
412char *tmp = NULL;
413char *command = NULL;
414char *mountdir = NULL;
415char *mountpoint = NULL;
416char *additional_parameters = NULL;
417
418assert_string_is_neither_NULL_nor_zerolength(device);
419assert_string_is_neither_NULL_nor_zerolength(mpt);
420assert(format != NULL);
421
422    if (!strcmp(mpt, "/1")) {
423        mr_asprintf(mountpoint, "/");
424        log_msg(3, "Mommm! SME is being a dildo!");
425    } else {
426        mr_asprintf(mountpoint, "%s", mpt);
427    }
428
429    if (!strcmp(mountpoint, "lvm")) {
430        return (0);
431    }
432    if (!strcmp(mountpoint, "image")) {
433        return (0);
434    }
435    mr_asprintf(tmp, "Mounting device %s   ", device);
436    log_msg(1, tmp);
437    /* Deal with additional params only if not /proc or /sys */
438    mr_asprintf(additional_parameters, "");
439    if (strcmp(format, "proc") && strcmp(format, "sys")) {
440        if (writeable) {
441            mr_strcat(additional_parameters, "-o rw");
442        } else {
443            mr_strcat(additional_parameters, "-o ro");
444        }
445        if (find_home_of_exe("setfattr")) {
446            mr_strcat(additional_parameters, ",user_xattr");
447        }
448        if (find_home_of_exe("setfacl")) {
449            mr_strcat(additional_parameters, ",acl");
450        }
451    }
452
453    if (!strcmp(mountpoint, "swap")) {
454        mr_asprintf(command, "swapon %s", device);
455        mr_asprintf(mountdir, "swap");
456    } else {
457        if (!strcmp(mountpoint, "/")) {
458            mr_asprintf(mountdir, "%s", MNT_RESTORING);
459        } else {
460            mr_asprintf(mountdir, "%s%s", MNT_RESTORING, mountpoint);
461        }
462        mr_asprintf(command, "mkdir -p %s", mountdir);
463        run_program_and_log_output(command, FALSE);
464        mr_free(command);
465
466        mr_asprintf(command, "mount -t %s %s %s %s 2>> %s", format, device, additional_parameters, mountdir, MONDO_LOGFILE);
467        log_msg(2, "command='%s'", command);
468    }
469    mr_free(additional_parameters);
470
471    res = run_program_and_log_output(command, TRUE);
472    if (res && (strstr(command, "xattr") || strstr(command, "acl"))) {
473        log_msg(1, "Re-trying without the fancy extra parameters");
474        mr_free(command);
475        mr_asprintf(command, "mount -t %s %s %s 2>> %s", format, device, mountdir, MONDO_LOGFILE);
476        res = run_program_and_log_output(command, TRUE);
477    }
478    if (res) {
479        log_msg(1, "Unable to mount device %s (type %s) at %s", device, format, mountdir);
480        log_msg(1, "command was '%s'", command);
481        if (!strcmp(mountpoint, "swap")) {
482            log_to_screen(tmp);
483        } else {
484            log_msg(2, "Retrying w/o the '-t' switch");
485            mr_free(command);
486            mr_asprintf(command, "mount %s %s 2>> %s", device, mountdir, MONDO_LOGFILE);
487            log_msg(2, "2nd command = '%s'", command);
488            res = run_program_and_log_output(command, TRUE);
489            if (res == 0) {
490                log_msg(1,
491                    "That's OK. I called mount w/o a filesystem type and it worked fine in the end.");
492            } else {
493                log_to_screen(tmp);
494            }
495        }
496    }
497
498    if (res && !strcmp(mountpoint, "swap")) {
499        log_msg(2, "That's ok. It's just a swap partition.");
500        log_msg(2, "Non-fatal error. Returning 0.");
501        res = 0;
502    }
503
504mr_free(tmp);
505mr_free(command);
506mr_free(mountdir);
507mr_free(mountpoint);
508
509return (res);
510}
511/**************************************************************************
512 *END_MOUNT_DEVICE                                                        *
513**************************************************************************/
514
515
516/**
517 * Mount all devices in @p p_external_copy_of_mountlist on @p MNT_RESTORING.
518 * @param p_external_copy_of_mountlist The mountlist containing devices to be mounted.
519 * @param writeable If TRUE, then mount read-write; if FALSE mount read-only.
520 * @return The number of errors encountered (0 for success).
521 */
522int mount_all_devices(struct mountlist_itself *p_external_copy_of_mountlist, bool writeable)
523{
524int retval = 0, lino, res;
525char *tmp = NULL;
526char *these_failed = NULL;
527struct mountlist_itself *mountlist = NULL;
528
529assert(p_external_copy_of_mountlist != NULL);
530mountlist = malloc(sizeof(struct mountlist_itself));
531memcpy((void *) mountlist, (void *) p_external_copy_of_mountlist,
532   sizeof(struct mountlist_itself));
533    sort_mountlist_by_mountpoint(mountlist, 0);
534
535
536    mvaddstr_and_log_it(g_currentY, 0, "Mounting devices         ");
537    open_progress_form("Mounting devices", "I am now mounting all the drives.", "This should not take long.", "", mountlist->entries);
538
539    mr_asprintf(these_failed, "");
540    for (lino = 0; lino < mountlist->entries; lino++) {
541        if (!strcmp(mountlist->el[lino].device, "/proc")) {
542            log_msg(1, "Again with the /proc - why is this in your mountlist?");
543        } else if (is_this_device_mounted(mountlist->el[lino].device)) {
544            log_to_screen("%s is already mounted", mountlist->el[lino].device);
545        } else if (strcmp(mountlist->el[lino].mountpoint, "none")
546           && strcmp(mountlist->el[lino].mountpoint, "lvm")
547           && strcmp(mountlist->el[lino].mountpoint, "raid")
548           && strcmp(mountlist->el[lino].mountpoint, "image")) {
549            mr_asprintf(tmp, "Mounting %s", mountlist->el[lino].device);
550            update_progress_form(tmp);
551            mr_free(tmp);
552            res = mount_device(mountlist->el[lino].device, mountlist->el[lino].mountpoint, mountlist->el[lino].format, writeable);
553            retval += res;
554            if (res) {
555                mr_strcat(these_failed, "%s ",mountlist->el[lino].device);
556            }
557        }
558        g_current_progress++;
559    }
560    close_progress_form();
561    if (retval) {
562        if (g_partition_table_locked_up > 0) {
563            log_to_screen("fdisk's ictol() call to refresh its copy of the partition table causes the kernel to");
564            log_to_screen("lock up the partition table. You might have to reboot and use Interactive Mode to");
565            log_to_screen("format and restore *without* partitioning first. Sorry for the inconvenience.");
566        }
567        mr_asprintf(tmp, "Could not mount device(s) %s- shall I abort?", these_failed);
568
569        if (!ask_me_yes_or_no(tmp)) {
570            retval = 0;
571            log_to_screen("Continuing, although some device(s) failed to be mounted");
572            mvaddstr_and_log_it(g_currentY++, 74, "Done.");
573        } else {
574            mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
575            log_to_screen("Unable to mount some or all of your partitions.");
576        }
577        mr_free(tmp);
578    } else {
579        log_to_screen("All partitions were mounted OK.");
580        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
581    }
582    paranoid_free(these_failed);
583
584    /* Also mounting under MNT_RESTORING  special FS */
585    (void)mount_device("/proc","/proc","proc",TRUE);
586    (void)mount_device("/sys","/sys","sysfs",TRUE);
587    run_program_and_log_output("df -m -P", 3);
588    paranoid_free(mountlist);
589    return (retval);
590}
591/**************************************************************************
592*END_MOUNT_ALL_DEVICES                                                   *
593**************************************************************************/
594
595
596/**
597* Fix some miscellaneous things in the filesystem so the system will come
598* up correctly on the first boot.
599*/
600void protect_against_braindead_sysadmins()
601{
602run_program_and_log_output("touch " MNT_RESTORING "/var/log/pacct",
603                       FALSE);
604run_program_and_log_output("touch " MNT_RESTORING "/var/account/pacct",
605                       FALSE);
606if (run_program_and_log_output("ls " MNT_RESTORING " /tmp", FALSE)) {
607run_program_and_log_output("chmod 1777 " MNT_RESTORING "/tmp",
608                           FALSE);
609}
610run_program_and_log_output("mkdir -p " MNT_RESTORING
611                       "/var/run/console", FALSE);
612run_program_and_log_output("chmod 777 " MNT_RESTORING "/dev/null",
613                       FALSE);
614run_program_and_log_output("cd " MNT_RESTORING
615                       "; for i in `ls home/`; do echo \"Moving $i's spurious files to $i/.disabled\"; mkdir \"$i\"/.disabled ; mv -f \"$i\"/.DCOP* \"$i\"/.MCOP* \"$i\"/.*authority \"$i\"/.kde/tmp* \"$i\"/.kde/socket* \"$i\"/.disabled/ ; done",
616                       TRUE);
617run_program_and_log_output("rm -f " MNT_RESTORING "/var/run/*.pid",
618                       TRUE);
619run_program_and_log_output("rm -f " MNT_RESTORING "/var/lock/subsys/*",
620                       TRUE);
621}
622
623/**************************************************************************
624*END_PROTECT_AGAINST_BRAINDEAD_SYSADMINS                                 *
625**************************************************************************/
626
627
628
629
630/**
631* Fill out @p bkpinfo based on @p cfg_file.
632* @param cfg_file The mondo-restore.cfg file to read into @p bkpinfo.
633* @param bkpinfo The backup information structure to fill out with information
634* from @p cfg_file.
635* @return 0 for success, nonzero for failure.
636*/
637int read_cfg_file_into_bkpinfo(char *cfgf)
638{
639/** add mallocs **/
640char value[MAX_STR_LEN];
641char *tmp1 = NULL;
642char *envtmp1 = NULL;
643char *envtmp2 = NULL;
644char *command = NULL;
645char iso_mnt[MAX_STR_LEN];
646char iso_path[MAX_STR_LEN];
647char *old_isodir = NULL;
648char *cfg_file = NULL;
649t_bkptype media_specified_by_user;
650
651//  assert_string_is_neither_NULL_nor_zerolength(cfg_file);
652assert(bkpinfo != NULL);
653assert(cfgf != NULL);
654log_it("Entering read_cfg_file_into_bkpinfo");
655
656if (!cfgf) {
657    strcpy(cfg_file, g_mondo_cfg_file);
658} else {
659    strcpy(cfg_file, cfgf);
660}
661
662media_specified_by_user = bkpinfo->backup_media_type;   // or 'none', if not specified
663
664if (0 == read_cfg_var(cfg_file, "backup-media-type", value)) {
665    if (!strcmp(value, "cdstream")) {
666        bkpinfo->backup_media_type = cdstream;
667    } else if (!strcmp(value, "cdr")) {
668        bkpinfo->backup_media_type = cdr;
669    } else if (!strcmp(value, "cdrw")) {
670        bkpinfo->backup_media_type = cdrw;
671    } else if (!strcmp(value, "dvd")) {
672        bkpinfo->backup_media_type = dvd;
673    } else if (!strcmp(value, "usb")) {
674        bkpinfo->backup_media_type = usb;
675        bkpinfo->please_dont_eject = TRUE;
676    } else if (!strcmp(value, "iso")) {
677        bkpinfo->backup_media_type = iso;
678        if (am_I_in_disaster_recovery_mode()) {
679            /* Check to see if CD is already mounted before mounting it... */
680            if (!is_this_device_mounted("/dev/cdrom")) {
681                log_msg(2, "NB: CDROM device not mounted, mounting...");
682                run_program_and_log_output("mount /dev/cdrom "MNT_CDROM, 1);
683            }
684            if (does_file_exist(MNT_CDROM"/archives/filelist.0")) {
685                bkpinfo->backup_media_type = cdr;
686                run_program_and_log_output("umount -d "MNT_CDROM, 1);
687                log_it("Re-jigging configuration AGAIN. CD-R, not ISO.");
688            }
689        }
690        if (read_cfg_var(cfg_file, "iso-prefix", value) == 0) {
691            strcpy(bkpinfo->prefix,value);
692        } else {
693            strcpy(bkpinfo->prefix,STD_PREFIX);
694        }
695        log_it("Setting Prefix to %s", bkpinfo->prefix);
696    } else if ((!strcmp(value, "netfs")) || (!strcmp(value, "nfs"))) {
697        /* Stay compatible with previous versions by allowing nfs as an entry here */
698        bkpinfo->backup_media_type = netfs;
699        bkpinfo->please_dont_eject = TRUE;
700        if (read_cfg_var(cfg_file, "netfs-proto", value) == 0) {
701            mr_asprintf(bkpinfo->netfs_proto,"%s", value);
702        } else {
703            /* For compatibility, force protocol in old nfs case to be transparent */
704            mr_asprintf(bkpinfo->netfs_proto, "nfs");
705        }
706        if (read_cfg_var(cfg_file, "netfs-server-user", value) == 0) {
707            mr_asprintf(bkpinfo->netfs_user,"%s", value);
708        }
709        if (read_cfg_var(cfg_file, "iso-prefix", value) == 0) {
710                strcpy(bkpinfo->prefix,value);
711        } else {
712                strcpy(bkpinfo->prefix,STD_PREFIX);
713        }
714        if (strstr(call_program_and_get_last_line_of_output("cat /proc/cmdline"), "pxe")) {
715            /* We need to override prefix value in PXE mode as it's
716            * already done in start-netfs */
717            envtmp1 = getenv("imgname");
718            if (envtmp1 == NULL) {
719                fatal_error("no imgname variable in environment");
720            }
721            strcpy(bkpinfo->prefix,envtmp1);
722        }   
723    } else if (!strcmp(value, "tape")) {
724        bkpinfo->backup_media_type = tape;
725    } else if (!strcmp(value, "udev")) {
726        bkpinfo->backup_media_type = udev;
727    } else {
728        fatal_error("UNKNOWN bkp-media-type");
729    }
730} else {
731    fatal_error("backup-media-type not specified!");
732}
733
734if (bkpinfo->disaster_recovery) {
735    if (bkpinfo->backup_media_type == cdstream) {
736        sprintf(bkpinfo->media_device, "/dev/cdrom");
737        bkpinfo->media_size = 650;  /* good guess */
738    } else if (bkpinfo->backup_media_type == usb) {
739        envtmp1 = getenv("MRUSBDEV");
740        if (envtmp1 == NULL) {
741            if (read_cfg_var(cfg_file, "usb-dev", value)) {
742                fatal_error("Cannot get USB device name from cfg file");
743            }
744        } else {
745            strcpy(value,envtmp1);
746        }
747        sprintf(bkpinfo->media_device, "%s1", value);
748        log_msg(2, "Backup medium is USB --- dev=%s", bkpinfo->media_device);
749    } else if (bkpinfo->backup_media_type == tape || bkpinfo->backup_media_type == udev) {
750        if (read_cfg_var(cfg_file, "media-dev", value)) {
751            fatal_error("Cannot get tape device name from cfg file");
752        }
753        strcpy(bkpinfo->media_device, value);
754        read_cfg_var(cfg_file, "media-size", value);
755        bkpinfo->media_size = atol(value);
756        log_msg(2, "Backup medium is TAPE --- dev=%s", bkpinfo->media_device);
757    } else {
758        strcpy(bkpinfo->media_device, "/dev/cdrom");    /* we don't really need this var */
759        bkpinfo->media_size = 1999 * 1024;  /* 650, probably, but we don't need this var anyway */
760        log_msg(2, "Backup medium is similar to CD-R[W]");
761    }
762} else {
763    log_msg(2, "Not in Disaster Recovery Mode. No need to derive device name from config file.");
764}
765
766read_cfg_var(cfg_file, "use-star", value);
767if (strstr(value, "yes")) {
768    bkpinfo->use_star = TRUE;
769    log_msg(1, "Goody! ... bkpinfo->use_star is now true.");
770}
771
772read_cfg_var(cfg_file, "obdr", value);
773if (strstr(value, "TRUE")) {
774    bkpinfo->use_obdr = TRUE;
775    log_msg(1, "OBDR mode activated");
776}
777
778read_cfg_var(cfg_file, "acl", value);
779if (strstr(value, "TRUE")) {
780    mr_asprintf(g_getfacl,"setfacl");
781    log_msg(1, "We will restore ACLs");
782    if (! find_home_of_exe("setfacl")) {
783        log_msg(1, "Unable to restore ACLs as no setfacl found");
784    }
785}
786read_cfg_var(cfg_file, "xattr", value);
787if (strstr(value, "TRUE")) {
788    mr_asprintf(g_getfattr,"setfattr");
789    log_msg(1, "We will restore XATTRs");
790    if (! find_home_of_exe("setfattr")) {
791        log_msg(1, "Unable to restore XATTRs as no setfattr found");
792    }
793}
794
795if (0 == read_cfg_var(cfg_file, "internal-tape-block-size", value)) {
796    bkpinfo->internal_tape_block_size = atol(value);
797} else {
798    bkpinfo->internal_tape_block_size = DEFAULT_INTERNAL_TAPE_BLOCK_SIZE;
799}
800log_msg(1, "Internal tape block size set to %ld", bkpinfo->internal_tape_block_size);
801
802read_cfg_var(cfg_file, "use-lzma", value);
803if (strstr(value, "yes")) {
804    bkpinfo->use_lzma = TRUE;
805    bkpinfo->use_lzo = FALSE;
806    bkpinfo->use_gzip = FALSE;
807    strcpy(bkpinfo->zip_exe, "lzma");
808    strcpy(bkpinfo->zip_suffix, "lzma");
809}
810
811read_cfg_var(cfg_file, "use-lzo", value);
812if (strstr(value, "yes")) {
813    bkpinfo->use_lzma = FALSE;
814    bkpinfo->use_lzo = TRUE;
815    bkpinfo->use_gzip = FALSE;
816    strcpy(bkpinfo->zip_exe, "lzop");
817    strcpy(bkpinfo->zip_suffix, "lzo");
818}
819
820read_cfg_var(cfg_file, "use-gzip", value);
821if (strstr(value, "yes")) {
822    bkpinfo->use_lzma = FALSE;
823    bkpinfo->use_lzo = FALSE;
824    bkpinfo->use_gzip = TRUE;
825    strcpy(bkpinfo->zip_exe, "gzip");
826    strcpy(bkpinfo->zip_suffix, "gz");
827}
828
829read_cfg_var(cfg_file, "use-comp", value);
830if (strstr(value, "yes")) {
831    bkpinfo->use_lzma = FALSE;
832    bkpinfo->use_lzo = FALSE;
833    bkpinfo->use_gzip = FALSE;
834    strcpy(bkpinfo->zip_exe, "bzip2");
835    strcpy(bkpinfo->zip_suffix, "bz2");
836}
837
838value[0] = '\0';
839read_cfg_var(cfg_file, "differential", value);
840if (!strcmp(value, "yes") || !strcmp(value, "1")) {
841    bkpinfo->differential = TRUE;
842}
843log_msg(2, "differential var = '%s'", value);
844if (bkpinfo->differential) {
845    log_msg(2, "THIS IS A DIFFERENTIAL BACKUP");
846} else {
847    log_msg(2, "This is a regular (full) backup");
848}
849
850read_cfg_var(g_mondo_cfg_file, "please-dont-eject", value);
851if (value[0] || strstr(call_program_and_get_last_line_of_output("cat /proc/cmdline"), "donteject")) {
852    bkpinfo->please_dont_eject = TRUE;
853    log_msg(2, "Ok, I shan't eject when restoring! Groovy.");
854}
855
856if (bkpinfo->backup_media_type == netfs) {
857    if (!cfgf) {
858        if (bkpinfo->netfs_mount) {
859            log_msg(2, "netfs_mount remains %s", bkpinfo->netfs_mount);
860        }
861        if (bkpinfo->netfs_remote_dir) {
862            log_msg(2, "netfs_remote_dir remains %s", bkpinfo->netfs_remote_dir);
863        }
864        log_msg(2, "...cos it wouldn't make sense to abandon the values that GOT ME to this config file in the first place");
865    } else {
866        read_cfg_var(g_mondo_cfg_file, "netfs-server-mount", value);
867        mr_free(bkpinfo->netfs_mount);
868        mr_asprintf(bkpinfo->netfs_mount, "%s", value);
869
870        read_cfg_var(g_mondo_cfg_file, "netfs-server-path", value);
871        mr_free(bkpinfo->netfs_remote_dir);
872        mr_asprintf(bkpinfo->netfs_remote_dir, "%s", value);
873
874        if (bkpinfo->netfs_mount != NULL) {
875            log_msg(2, "netfs_mount is %s", bkpinfo->netfs_mount);
876        }
877        if (bkpinfo->netfs_remote_dir != NULL) {
878            log_msg(2, "netfs_remote_dir is %s", bkpinfo->netfs_remote_dir);
879        }
880        if (bkpinfo->netfs_proto != NULL) {
881            log_msg(2, "netfs_proto is %s", bkpinfo->netfs_proto);
882        }
883        if (bkpinfo->netfs_user != NULL) {
884            log_msg(2, "netfs_user is %s", bkpinfo->netfs_user);
885        }
886    }
887    if (strstr(call_program_and_get_last_line_of_output("cat /proc/cmdline"), "pxe")) {
888        /* We need to override values in PXE mode as it's
889        * already done in start-netfs */
890        envtmp1 = getenv("netfsmount");
891        if (envtmp1 == NULL) {
892            fatal_error("no netfsmount variable in environment");
893        }
894        envtmp2 = getenv("dirimg");
895        if (envtmp2 == NULL) {
896            fatal_error("no dirimg variable in environment");
897        }
898        mr_free(bkpinfo->netfs_mount);
899        mr_asprintf(bkpinfo->netfs_mount, "%s", envtmp1);
900
901        mr_free(bkpinfo->netfs_remote_dir);
902        mr_asprintf(bkpinfo->netfs_remote_dir, "%s", envtmp2);
903    }
904} else if (bkpinfo->backup_media_type == iso) {
905    /* Patch by Conor Daly 23-june-2004
906    * to correctly mount iso-dev and set a sensible
907    * isodir in disaster recovery mode
908    */
909    mr_asprintf(old_isodir, "%s", bkpinfo->isodir);
910    read_cfg_var(g_mondo_cfg_file, "iso-mnt", iso_mnt);
911    read_cfg_var(g_mondo_cfg_file, "isodir", iso_path);
912    sprintf(bkpinfo->isodir, "%s%s", iso_mnt, iso_path);
913    if (!bkpinfo->isodir[0]) {
914        strcpy(bkpinfo->isodir, old_isodir);
915    }
916    if ((!bkpinfo->disaster_recovery) || (g_isodir_device[0] != '\0')) {
917        if (strcmp(old_isodir, bkpinfo->isodir)) {
918            log_it("user nominated isodir %s differs from archive, keeping user's choice: %s\n", bkpinfo->isodir, old_isodir );
919            strcpy(bkpinfo->isodir, old_isodir);
920        }
921    }
922    if (g_isodir_device[0] == '\0') {
923        /*  We don't want to loose our conf made earlier in iso_fiddly_bits
924         *  so go here only if job not done already */
925        read_cfg_var(g_mondo_cfg_file, "iso-dev", g_isodir_device);
926    }
927
928    log_msg(2, "isodir=%s; iso-dev=%s", bkpinfo->isodir, g_isodir_device);
929
930    if (bkpinfo->disaster_recovery) {
931        if (is_this_device_mounted(g_isodir_device)) {
932            log_msg(2, "NB: isodir is already mounted");
933            /* Find out where it's mounted */
934            mr_asprintf(command, "mount | grep -E '^%s' | tail -n1 | cut -d' ' -f3", g_isodir_device);
935            log_it("command = %s", command);
936            log_it("res of it = %s", call_program_and_get_last_line_of_output(command));
937            sprintf(iso_mnt, "%s", call_program_and_get_last_line_of_output(command));
938            mr_free(command);
939        } else {
940            sprintf(iso_mnt, "/tmp/isodir");
941            mr_asprintf(tmp1, "mkdir -p %s", iso_mnt);
942            run_program_and_log_output(tmp1, 5);
943            mr_free(tmp1);
944
945            mr_asprintf(tmp1, "mount %s %s", g_isodir_device, iso_mnt);
946            if (run_program_and_log_output(tmp1, 3)) {
947                log_msg(1, "Unable to mount isodir. Perhaps this is really a CD backup?");
948                bkpinfo->backup_media_type = cdr;
949                strcpy(bkpinfo->media_device, "/dev/cdrom");    /* superfluous */
950                bkpinfo->isodir[0] = iso_mnt[0] = iso_path[0] = '\0';
951                if (mount_media()) {
952                    mr_free(tmp1);
953                    fatal_error("Unable to mount isodir. Failed to mount CD-ROM as well.");
954                } else {
955                    log_msg(1, "You backed up to disk, then burned some CDs.");
956                }
957            }
958            mr_free(tmp1);
959        }
960        /* bkpinfo->isodir should now be the true path to prefix-1.iso etc... */
961        /*  except when already done in iso_fiddly_bits */
962        if ((bkpinfo->backup_media_type == iso) && (g_isodir_device[0] == '\0')) {
963            sprintf(bkpinfo->isodir, "%s%s", iso_mnt, iso_path);
964        }
965    }
966}
967
968if (media_specified_by_user != none) {
969    if (g_restoring_live_from_cd) {
970        if (bkpinfo->backup_media_type != media_specified_by_user) {
971            log_msg(2, "bkpinfo->backup_media_type != media_specified_by_user, so I'd better ask :)");
972            interactively_obtain_media_parameters_from_user(FALSE);
973            media_specified_by_user = bkpinfo->backup_media_type;
974            get_cfg_file_from_archive();
975        }
976    }
977    bkpinfo->backup_media_type = media_specified_by_user;
978}
979g_backup_media_type = bkpinfo->backup_media_type;
980return (0);
981
982}
983
984/**************************************************************************
985*END_READ_CFG_FILE_INTO_BKPINFO                                          *
986**************************************************************************/
987
988
989
990
991/**
992 * Allow the user to edit the filelist and biggielist.
993 * The filelist is unlinked after it is read.
994 * @param bkpinfo The backup information structure. Fields used:
995 * - @c bkpinfo->backup_media_type
996 * - @c bkpinfo->isodir
997 * - @c bkpinfo->media_device
998 * - @c bkpinfo->tmpdir
999 * @return The filelist structure containing the information read from disk.
1000 */
1001struct
1002s_node *process_filelist_and_biggielist()
1003{
1004struct s_node *filelist;
1005
1006char *command = NULL;
1007char *tmp;
1008char *q;
1009int res = 0;
1010pid_t pid;
1011bool extract_mountlist_stub = FALSE;
1012
1013assert(bkpinfo != NULL);
1014malloc_string(tmp);
1015
1016/* If those files already exist, do not overwrite them later on */
1017if (does_file_exist("/"MOUNTLIST_FNAME_STUB)) {
1018    extract_mountlist_stub = FALSE;
1019} else {
1020    extract_mountlist_stub = TRUE;
1021}
1022
1023if (does_file_exist(g_filelist_full)
1024&& does_file_exist(g_biggielist_txt)) {
1025    log_msg(1, "%s exists", g_filelist_full);
1026    log_msg(1, "%s exists", g_biggielist_txt);
1027    log_msg(2,
1028        "Filelist and biggielist already recovered from media. Yay!");
1029} else {
1030    if (getcwd(tmp, MAX_STR_LEN) == NULL) {
1031        // FIXME
1032    }
1033    if (chdir(bkpinfo->tmpdir)) {
1034        // FIXME
1035    }
1036    log_msg(1, "chdir(%s)", bkpinfo->tmpdir);
1037    log_to_screen("Extracting filelist and biggielist from media...");
1038    unlink("/tmp/filelist.full");
1039    unlink(FILELIST_FULL_STUB);
1040    if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
1041        mr_asprintf(command, "tar -b %ld -zxf %s ./%s ./%s ./%s ./%s ./%s", bkpinfo->internal_tape_block_size, bkpinfo->media_device, MOUNTLIST_FNAME_STUB, BIGGIELIST_TXT_STUB, FILELIST_FULL_STUB, IWANTMYLVM_STUB, MONDO_CFG_FILE_STUB);
1042        log_msg(1, "tarcommand = %s", command);
1043        run_program_and_log_output(command, 1);
1044        mr_free(command);
1045
1046        if (!does_file_exist(FILELIST_FULL_STUB)) {
1047            /* Doing that allow us to remain compatible with pre-2.2.5 versions */
1048            log_msg(2, "pre-2.2.4 compatible mode on");
1049            mr_asprintf(command, "tar -b %ld -zxf %s %s %s %s %s %s", bkpinfo->internal_tape_block_size, bkpinfo->media_device, MOUNTLIST_FNAME_STUB, BIGGIELIST_TXT_STUB, FILELIST_FULL_STUB, IWANTMYLVM_STUB, MONDO_CFG_FILE_STUB);
1050            log_msg(1, "tarcommand = %s", command);
1051            run_program_and_log_output(command, 1);
1052            mr_free(command);
1053        }
1054    } else {
1055        log_msg(2, "Calling insist_on_this_cd_number; bkpinfo->isodir=%s", bkpinfo->isodir);
1056        insist_on_this_cd_number(1);
1057        log_msg(2, "Back from iotcn");
1058        run_program_and_log_output("mount", 1);
1059        mr_asprintf(command, "tar -zxf %s/images/all.tar.gz ./%s ./%s ./%s ./%s ./%s", MNT_CDROM, MOUNTLIST_FNAME_STUB, BIGGIELIST_TXT_STUB, FILELIST_FULL_STUB, IWANTMYLVM_STUB, MONDO_CFG_FILE_STUB);
1060
1061        log_msg(1, "tarcommand = %s", command);
1062        run_program_and_log_output(command, 1);
1063        mr_free(command);
1064
1065        if (!does_file_exist(FILELIST_FULL_STUB)) {
1066            /* Doing that allow us to remain compatible with pre-2.2.5 versions */
1067            log_msg(2, "pre-2.2.4 compatible mode on");
1068            mr_asprintf(command, "tar -zxf %s/images/all.tar.gz %s %s %s %s %s", MNT_CDROM, MOUNTLIST_FNAME_STUB, BIGGIELIST_TXT_STUB, FILELIST_FULL_STUB, IWANTMYLVM_STUB, MONDO_CFG_FILE_STUB);
1069
1070            log_msg(1, "tarcommand = %s", command);
1071            run_program_and_log_output(command, 1);
1072            mr_free(command);
1073        }
1074        if (!does_file_exist(BIGGIELIST_TXT_STUB)) {
1075            fatal_error("all.tar.gz did not include " BIGGIELIST_TXT_STUB);
1076        }
1077        if (!does_file_exist(FILELIST_FULL_STUB)) {
1078            fatal_error("all.tar.gz did not include " FILELIST_FULL_STUB);
1079        }
1080    }
1081    mr_asprintf(command, "cp -f %s %s", MONDO_CFG_FILE_STUB, g_mondo_cfg_file);
1082    run_program_and_log_output(command, FALSE);
1083    mr_free(command);
1084
1085    mr_asprintf(command, "cp -f %s/%s %s", bkpinfo->tmpdir, BIGGIELIST_TXT_STUB, g_biggielist_txt);
1086    log_msg(1, "command = %s", command);
1087    paranoid_system(command);
1088    mr_free(command);
1089
1090    mr_asprintf(command, "ln -sf %s/%s %s", bkpinfo->tmpdir, FILELIST_FULL_STUB, g_filelist_full);
1091    log_msg(1, "command = %s", command);
1092    paranoid_system(command);
1093    mr_free(command);
1094    }
1095
1096    if (am_I_in_disaster_recovery_mode()
1097    &&
1098    /* If it was there, do not overwrite it */
1099    (extract_mountlist_stub) 
1100    &&
1101    ask_me_yes_or_no("Do you want to retrieve the mountlist as well?"))
1102    {
1103        mr_asprintf(command, "ln -sf %s/%s /tmp", MOUNTLIST_FNAME_STUB, bkpinfo->tmpdir);
1104        paranoid_system(command);
1105        mr_free(command);
1106    }
1107
1108    if (chdir(tmp)) {
1109        // FIXME
1110    }
1111
1112    if (!does_file_exist(g_biggielist_txt)) {
1113        log_msg(1, "Warning - %s not found", g_biggielist_txt);
1114    }
1115    if (!does_file_exist(g_filelist_full)) {
1116        log_msg(1, "Warning - %s does not exist", g_filelist_full);
1117    }
1118
1119    log_msg(2, "Forking");
1120    pid = fork();
1121    switch (pid) {
1122    case -1:
1123        fatal_error("Forking error");
1124        break;
1125
1126    case 0:
1127        log_to_screen("Pre-processing filelist");
1128        if (!does_file_exist(g_biggielist_txt)) {
1129            mr_asprintf(command, "echo -n > %s", g_biggielist_txt);
1130            paranoid_system(command);
1131            mr_free(command);
1132        }
1133        mr_asprintf(command, "grep -E '^/dev/.*' %s > %s", g_biggielist_txt, g_filelist_imagedevs);
1134        paranoid_system(command);
1135        mr_free(command);
1136        exit(0);
1137        break;
1138
1139    default:
1140        open_evalcall_form("Pre-processing filelist");
1141        while (!waitpid(pid, (int *) 0, WNOHANG)) {
1142            usleep(100000);
1143            update_evalcall_form(0);
1144        }
1145    }
1146    close_evalcall_form();
1147
1148    log_msg(3, "loading filelist");
1149    filelist = load_filelist(g_filelist_full);
1150    log_msg(3, "deleting original filelist");
1151    unlink(g_filelist_full);
1152    if (g_text_mode) {
1153        q = NULL;
1154        while (q == NULL) {
1155            printf("Restore which directory? --> ");
1156            mr_getline(q, stdin);
1157        }
1158        toggle_path_selection(filelist, q, TRUE);
1159        if (strlen(q) == 0) {
1160            res = 1;
1161        } else {
1162            res = 0;
1163        }
1164        mr_free(q);
1165    } else {
1166        res = edit_filelist(filelist);
1167    }
1168    if (res) {
1169        log_msg(2, "User hit 'cancel'. Freeing filelist and aborting.");
1170        free_filelist(filelist);
1171        return (NULL);
1172    }
1173    ask_about_these_imagedevs(g_filelist_imagedevs, g_imagedevs_restthese);
1174    close_evalcall_form();
1175
1176    // NB: It's not necessary to add g_biggielist_txt to the filelist.full
1177    // file. The filelist.full file already contains the filename of EVERY
1178    // file backed up - regular and biggie files.
1179
1180    // However, we do want to make sure the imagedevs selected by the user
1181    // are flagged for restoring.
1182    if (length_of_file(g_imagedevs_restthese) > 2) {
1183        add_list_of_files_to_filelist(filelist, g_imagedevs_restthese,
1184                                      TRUE);
1185    }
1186
1187    paranoid_free(tmp);
1188    return (filelist);
1189}
1190
1191/**************************************************************************
1192 *END_ PROCESS_FILELIST_AND_BIGGIELIST                                    *
1193 **************************************************************************/
1194
1195
1196
1197
1198/**
1199 * Make a backup copy of <tt>path_root</tt>/<tt>filename</tt>.
1200 * The backup filename is the filename of the original with ".pristine" added.
1201 * @param path_root The place where the filesystem is mounted (e.g. MNT_RESTORING).
1202 * @param filename The filename (absolute path) within @p path_root.
1203 * @return 0 for success, nonzero for failure.
1204 */
1205int backup_crucial_file(char *path_root, char *filename)
1206{
1207    char *tmp = NULL;
1208    char *command = NULL;
1209    int res;
1210
1211    assert(path_root != NULL);
1212    assert_string_is_neither_NULL_nor_zerolength(filename);
1213
1214    mr_asprintf(tmp, "%s/%s", path_root, filename);
1215    mr_asprintf(command, "cp -f %s %s.pristine", tmp, tmp);
1216    mr_free(tmp);
1217
1218    res = run_program_and_log_output(command, 5);
1219    mr_free(command);
1220    return (res);
1221}
1222
1223void offer_to_make_initrd() {
1224
1225if (bkpinfo->restore_mode != nuke) {
1226    if (ask_me_yes_or_no
1227        ("You will now be able to re-generate your initrd.\nThis is especially useful if you changed of hardware configuration, cloned, made P2V, used multipath...\nDo you need to do it ?")) {
1228        log_msg(1,"Launching shell for manual initrd recreation");
1229        if (does_file_exist(MNT_RESTORING"/etc/arch-release")) {
1230            popup_and_OK("You'll now be chrooted under your future / partition.\nEdit /etc/mkinitcpio.conf if needed and rebuild your initrd with the kernel preset name e.g.\nmkinitcpio -p kernel26\nThen type exit to finish.\n");
1231        } else {
1232            popup_and_OK("You'll now be chrooted under your future / partition.\nGo under /boot and rebuild your init(rd|ramfs) with\nmkinitrd -f -v initrd-2.x.y.img 2.x.y e.g.\nor initramfs, dracut, ... Then type exit to finish.");
1233        }
1234        mvaddstr_and_log_it(g_currentY, 0, "Modifying initrd...");
1235        if (!g_text_mode) {
1236            newtSuspend();
1237        }
1238        paranoid_system("chroot " MNT_RESTORING);
1239        if (!g_text_mode) {
1240            newtResume();
1241        }
1242        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1243    } else {
1244        return;
1245    }
1246} else {
1247    log_to_screen("Non-interactive mode: no way to give you the keyboard so that you re-generate your initrd. Hope it's OK");
1248    log_msg(1,"Non-interactive mode: no way to give you the keyboard so that you re-generate your initrd. Hope it's OK");
1249}
1250}
1251
1252
1253/**
1254 * Install the user's boot loader in the MBR.
1255 * Currently LILO, ELILO, GRUB, RAW (dd of MBR), and the FreeBSD bootloader are supported.
1256 * @param offer_to_hack_scripts If TRUE, then offer to hack the user's fstab for them.
1257 * @return 0 for success, nonzero for failure.
1258 */
1259int run_boot_loader(bool offer_to_hack_scripts)
1260{
1261    int res;
1262    int retval = 0;
1263
1264  /** malloc *******/
1265    char *device = NULL;
1266    char *name = NULL;
1267    char *cmd = NULL;
1268
1269    malloc_string(device);
1270    malloc_string(name);
1271
1272    /* In order to have a working bootloader, we need to have all devices
1273     * ready in the chroot. If they are not there (udev) then copy them from
1274     * the current /dev location
1275     */
1276    mr_asprintf(cmd,"tar cf - /dev | ( cd %s ; tar xf - )",MNT_RESTORING);
1277    run_program_and_log_output(cmd, 3);
1278    paranoid_free(cmd);
1279
1280    backup_crucial_file(MNT_RESTORING, "/etc/fstab");
1281    backup_crucial_file(MNT_RESTORING, "/boot/grub/menu.lst");
1282    backup_crucial_file(MNT_RESTORING, "/boot/grub/grub.cfg");
1283    backup_crucial_file(MNT_RESTORING, "/boot/grub2/grub.cfg");
1284    backup_crucial_file(MNT_RESTORING, "/etc/lilo.conf");
1285    backup_crucial_file(MNT_RESTORING, "/etc/elilo.conf");
1286    backup_crucial_file(MNT_RESTORING, "/boot/grub/device.map");
1287    backup_crucial_file(MNT_RESTORING, "/boot/grub2/device.map");
1288    backup_crucial_file(MNT_RESTORING, "/etc/mtab");
1289    read_cfg_var(g_mondo_cfg_file, "bootloader.device", device);
1290    read_cfg_var(g_mondo_cfg_file, "bootloader.name", name);
1291    log_msg(2, "run_boot_loader: device='%s', name='%s'", device, name);
1292    sync();
1293
1294    offer_to_make_initrd();
1295    if (!strcmp(name, "LILO")) {
1296        res = run_lilo(offer_to_hack_scripts);
1297    } else if (!strcmp(name, "ELILO")) {
1298        res = run_elilo(offer_to_hack_scripts);
1299    } else if (!strcmp(name, "GRUB")) {
1300        res = run_grub(offer_to_hack_scripts, device);
1301    } else if (!strcmp(name, "RAW")) {
1302        res = run_raw_mbr(offer_to_hack_scripts, device);
1303    }
1304#ifdef __FreeBSD__
1305    else if (!strcmp(name, "BOOT0")) {
1306        mr_asprintf(tmp, "boot0cfg -B %s", device);
1307        res = run_program_and_log_output(tmp, FALSE);
1308        paranoid_free(tmp);
1309    } else {
1310        mr_asprintf(tmp, "ls /dev | grep -Eq '^%ss[1-4].*'", device);
1311        if (!system(tmp)) {
1312            mr_free(tmp);
1313            mr_asprintf(tmp, MNT_RESTORING "/sbin/fdisk -B %s", device);
1314            res = run_program_and_log_output(tmp, 3);
1315        } else {
1316            log_msg(1, "I'm not running any boot loader. You have a DD boot drive. It's already loaded up.");
1317        }
1318        mr_free(tmp);
1319    }
1320#else
1321    else {
1322        log_to_screen
1323            ("Unable to determine type of boot loader. Defaulting to LILO.");
1324        res = run_lilo(offer_to_hack_scripts);
1325    }
1326#endif
1327    retval += res;
1328    if (res) {
1329        log_to_screen("Your boot loader returned an error");
1330    } else {
1331        log_to_screen("Your boot loader ran OK");
1332    }
1333    paranoid_free(device);
1334    paranoid_free(name);
1335    return (retval);
1336}
1337
1338/**************************************************************************
1339 *END_ RUN_BOOT_LOADER                                                    *
1340 **************************************************************************/
1341
1342
1343
1344/**
1345 * Attempt to find the user's editor.
1346 * @return The editor found ("vi" if none could be found).
1347 * @note The returned string points to static storage that will be overwritten with each call.
1348 */
1349char *find_my_editor(void) {
1350
1351    static char output[MAX_STR_LEN];
1352    if (find_home_of_exe("pico")) {
1353        strcpy(output, "pico");
1354    } else if (find_home_of_exe("nano")) {
1355        strcpy(output, "nano");
1356    } else if (find_home_of_exe("e3em")) {
1357        strcpy(output, "e3em");
1358    } else if (find_home_of_exe("e3vi")) {
1359        strcpy(output, "e3vi");
1360    } else {
1361        strcpy(output, "vi");
1362    }
1363    if (!find_home_of_exe(output)) {
1364        log_msg(2, " (find_my_editor) --- warning - %s not found", output);
1365    }
1366    return (output);
1367}
1368
1369
1370/**
1371 * Install GRUB on @p bd.
1372 * @param offer_to_run_stabgrub If TRUE, then offer to hack the user's fstab for them.
1373 * @param bd The boot device where GRUB is installed.
1374 * @return 0 for success, nonzero for failure.
1375 */
1376int run_grub(bool offer_to_run_stabgrub, char *bd)
1377{
1378  /** malloc **/
1379    char *command = NULL;
1380    char *boot_device = NULL;
1381    char *tmp = NULL;
1382    char *editor = NULL;
1383
1384    bool done;
1385    int res = 0;        /*  FALSE */
1386    bool mntlistchg = FALSE;
1387    FILE *fin = NULL;
1388
1389    malloc_string(boot_device);
1390
1391    assert_string_is_neither_NULL_nor_zerolength(bd);
1392    strcpy(boot_device, bd);
1393
1394    if (offer_to_run_stabgrub
1395        && ask_me_yes_or_no("Did you change the mountlist or cloned the system ?")) {
1396        /* interactive mode */
1397        mvaddstr_and_log_it(g_currentY,
1398                            0,
1399                            "Modifying fstab, mtab, device.map and menu.lst/grub.cfg, and running GRUB...                             ");
1400        /*  Did we changed the mountlist ? If yes, then force editing conf files */
1401        if ((fin = fopen(MONDO_MNTLISTCHG, "r")) != NULL) {
1402            mntlistchg = TRUE;
1403        }
1404        for (done = FALSE; !done;) {
1405            popup_and_get_string("Boot device", "Please confirm/enter the boot device. If in doubt, try /dev/hda", boot_device, MAX_STR_LEN / 4);
1406            /*  Only try to adapt grub here first if the mountlist wasn't changed before */
1407            if (! mntlistchg) {
1408                mr_asprintf(command, "stabgrub-me %s", boot_device);
1409                res = run_program_and_log_output(command, 1);
1410                mr_free(command);
1411            }
1412
1413            if ((res) || (mntlistchg)) {
1414                if (res) {
1415                    popup_and_OK("GRUB installation failed. You will now edit fstab, mtab, device.map and menu.lst/grub.cfg in order to fix grub install");
1416                } else {
1417                    popup_and_OK("The mountlist was changed. You will now edit fstab, mtab, device.map and menu.lst/grub.cfg in order to fix grub install");
1418                }
1419                if (!g_text_mode) {
1420                    newtSuspend();
1421                }
1422                mr_asprintf(editor, "%s", find_my_editor());
1423                mr_asprintf(tmp, "chroot %s %s /etc/fstab", MNT_RESTORING, editor);
1424                paranoid_system(tmp);
1425                mr_free(tmp);
1426
1427                mr_asprintf(tmp, "chroot %s %s /etc/mtab", MNT_RESTORING, editor);
1428                paranoid_system(tmp);
1429                mr_free(tmp);
1430   
1431                if (does_file_exist(MNT_RESTORING"/boot/grub/menu.lst")) {
1432                    mr_asprintf(tmp, "chroot %s %s /boot/grub/menu.lst", MNT_RESTORING, editor);
1433                } else if (does_file_exist(MNT_RESTORING"/boot/grub/grub.cfg")) {
1434                    mr_asprintf(tmp, "chroot %s %s /boot/grub/grub.cfg", MNT_RESTORING, editor);
1435                } else if (does_file_exist(MNT_RESTORING"/boot/grub2/grub.cfg")) {
1436                    mr_asprintf(tmp, "chroot %s %s /boot/grub2/grub.cfg", MNT_RESTORING, editor);
1437                }
1438                paranoid_system(tmp);
1439                mr_free(tmp);
1440   
1441                if (does_file_exist(MNT_RESTORING"/boot/grub/device.map")) {
1442                    mr_asprintf(tmp, "chroot %s %s /boot/grub/device.map", MNT_RESTORING, editor);
1443                } else if (does_file_exist(MNT_RESTORING"/boot/grub2/device.map")) {
1444                    mr_asprintf(tmp, "chroot %s %s /boot/grub2/device.map", MNT_RESTORING, editor);
1445                }
1446                paranoid_system(tmp);
1447                mr_free(tmp);
1448                mr_free(editor);
1449
1450                if (!g_text_mode) {
1451                    newtResume();
1452                }
1453                mr_asprintf(command, "stabgrub-me %s", boot_device);
1454                res = run_program_and_log_output(command, 1);
1455                mr_free(command);
1456
1457                if (res) {
1458                    popup_and_OK("GRUB installation failed. Please fix the conf files so that a manual install using 'grub-install' or similar command works. You are now chroot()'ed to your restored system. Please type 'exit' when you are done.");
1459                    newtSuspend();
1460                    paranoid_system("chroot " MNT_RESTORING);
1461                    newtResume();
1462                    popup_and_OK("Thank you.");
1463                } else {
1464                    popup_and_OK("GRUB is now installed correctly");
1465                    done = TRUE;
1466                }
1467            } else {
1468                done = TRUE;
1469            }
1470        }
1471    } else {
1472        /* nuke mode */
1473        if (!run_program_and_log_output("which grub-MR", FALSE)) {
1474            log_msg(1, "Yay! grub-MR found...");
1475            mr_asprintf(command, "grub-MR %s "MINDI_CACHE"/mountlist.txt", boot_device);
1476            log_msg(1, "command = %s", command);
1477        } else {
1478            mr_asprintf(command, "chroot " MNT_RESTORING " grub-install %s", boot_device);
1479            log_msg(1, "WARNING - grub-MR not found; using grub-install");
1480        }
1481        mvaddstr_and_log_it(g_currentY, 0, "Running GRUB...                                                 ");
1482        log_it("%s",command);
1483        res = run_program_and_log_output(command, 1);
1484        mr_free(command);
1485
1486        if (res) {
1487            popup_and_OK
1488                ("Because of bugs in GRUB's own installer, GRUB was not installed properly. Please install the boot loader manually now, using this chroot()'ed shell prompt. Type 'exit' when you have finished.");
1489            newtSuspend();
1490            paranoid_system("chroot " MNT_RESTORING);
1491            newtResume();
1492            popup_and_OK("Thank you.");
1493        }
1494    }
1495    if (res) {
1496        mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
1497        log_to_screen
1498            ("GRUB ran w/error(s). See %s for more info.", MONDO_LOGFILE);
1499        log_msg(1, "Type:-");
1500        log_msg(1, "    mount-me");
1501        log_msg(1, "    chroot " MNT_RESTORING);
1502        log_msg(1, "    mount /boot");
1503        log_msg(1, "    grub-install '(hd0)'");
1504        log_msg(1, "    exit");
1505        log_msg(1, "    unmount-me");
1506        log_msg(1,
1507                "If you're really stuck, please e-mail the mailing list.");
1508    } else {
1509        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1510    }
1511    paranoid_free(boot_device);
1512
1513    return (res);
1514}
1515
1516/**************************************************************************
1517 *END_RUN_GRUB                                                            *
1518 **************************************************************************/
1519
1520
1521/**
1522 * Install ELILO on the user's boot drive (determined by elilo.conf).
1523 * @param offer_to_run_stabelilo If TRUE, then offer to hack the user's fstab for them.
1524 * @return 0 for success, nonzero for failure.
1525 */
1526int run_elilo(bool offer_to_run_stabelilo)
1527{
1528  /** malloc **/
1529    char *command = NULL;
1530    char *tmp = NULL;
1531    char *editor = NULL;
1532
1533    int res;
1534    int done;
1535
1536    if (offer_to_run_stabelilo
1537        && ask_me_yes_or_no("Did you change the mountlist or cloned the system ?"))
1538
1539        /* interactive mode */
1540    {
1541        mvaddstr_and_log_it(g_currentY,
1542                            0,
1543                            "Modifying fstab and elilo.conf...                             ");
1544        mr_asprintf(command, "stabelilo-me");
1545        res = run_program_and_log_output(command, 3);
1546        mr_free(command);
1547
1548        if (res) {
1549            popup_and_OK
1550                ("You will now edit fstab and elilo.conf, to make sure they match your new mountlist.");
1551            for (done = FALSE; !done;) {
1552                if (!g_text_mode) {
1553                    newtSuspend();
1554                }
1555                mr_asprintf(editor, "%s", find_my_editor());
1556
1557                mr_asprintf(tmp, "chroot %s %s /etc/fstab", MNT_RESTORING, editor);
1558                paranoid_system(tmp);
1559                mr_free(tmp);
1560
1561                mr_asprintf(tmp, "chroot %s %s /etc/elilo.conf", MNT_RESTORING, editor);
1562                paranoid_system(tmp);
1563                mr_free(tmp);
1564                mr_free(editor);
1565
1566                if (!g_text_mode) {
1567                    newtResume();
1568                }
1569//              newtCls();
1570                if (ask_me_yes_or_no("Edit them again?")) {
1571                    continue;
1572                }
1573                done = TRUE;
1574            }
1575        } else {
1576            log_to_screen("elilo.conf and fstab were modified OK");
1577        }
1578    } else
1579        /* nuke mode */
1580    {
1581        res = TRUE;
1582    }
1583    return (res);
1584}
1585
1586/**************************************************************************
1587 *END_RUN_ELILO                                                            *
1588 **************************************************************************/
1589
1590
1591/**
1592 * Install LILO on the user's boot drive (determined by /etc/lilo.conf).
1593 * @param offer_to_run_stablilo If TRUE, then offer to hack the user's fstab for them.
1594 * @return 0 for success, nonzero for failure.
1595 */
1596int run_lilo(bool offer_to_run_stablilo)
1597{
1598  /** malloc **/
1599    char *command = NULL;
1600    char *tmp = NULL;
1601    char *editor = NULL;
1602
1603    int res;
1604    int done;
1605    bool run_lilo_M = FALSE;
1606
1607    if (!run_program_and_log_output
1608        ("grep \"boot.*=.*/dev/md\" " MNT_RESTORING "/etc/lilo.conf", 1)) {
1609        run_lilo_M = TRUE;
1610    }
1611
1612    if (offer_to_run_stablilo
1613        && ask_me_yes_or_no("Did you change the mountlist or cloned the system ?"))
1614
1615        /* interactive mode */
1616    {
1617        mvaddstr_and_log_it(g_currentY,
1618                            0,
1619                            "Modifying fstab and lilo.conf, and running LILO...                             ");
1620        mr_asprintf(command, "stablilo-me");
1621        res = run_program_and_log_output(command, 3);
1622        mr_free(command);
1623
1624        if (res) {
1625            popup_and_OK
1626                ("You will now edit fstab and lilo.conf, to make sure they match your new mountlist.");
1627            for (done = FALSE; !done;) {
1628                if (!g_text_mode) {
1629                    newtSuspend();
1630                }
1631                mr_asprintf(editor, "%s", find_my_editor());
1632
1633                mr_asprintf(tmp, "%s " MNT_RESTORING "/etc/fstab", editor);
1634                paranoid_system(tmp);
1635                mr_free(tmp);
1636
1637                mr_asprintf(tmp, "%s " MNT_RESTORING "/etc/lilo.conf", editor);
1638                paranoid_system(tmp);
1639                mr_free(tmp);
1640                mr_free(editor);
1641
1642                if (!g_text_mode) {
1643                    newtResume();
1644                }
1645//              newtCls();
1646                if (ask_me_yes_or_no("Edit them again?")) {
1647                    continue;
1648                }
1649                res =
1650                    run_program_and_log_output("chroot " MNT_RESTORING
1651                                               " lilo -L", 3);
1652                if (res) {
1653                    res =
1654                        run_program_and_log_output("chroot " MNT_RESTORING
1655                                                   " lilo", 3);
1656                }
1657                if (res) {
1658                    done =
1659                        ask_me_yes_or_no
1660                        ("LILO failed. Re-edit system files?");
1661                } else {
1662                    done = TRUE;
1663                }
1664            }
1665        } else {
1666            log_to_screen("lilo.conf and fstab were modified OK");
1667        }
1668    } else
1669        /* nuke mode */
1670    {
1671        mvaddstr_and_log_it(g_currentY,
1672                            0,
1673                            "Running LILO...                                                 ");
1674        res =
1675            run_program_and_log_output("chroot " MNT_RESTORING " lilo -L",
1676                                       3);
1677        if (res) {
1678            res =
1679                run_program_and_log_output("chroot " MNT_RESTORING " lilo",
1680                                           3);
1681        }
1682        if (res) {
1683            mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
1684            log_to_screen
1685                ("Failed to re-jig fstab and/or lilo. Edit/run manually, please.");
1686        } else {
1687            mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1688        }
1689    }
1690    if (run_lilo_M) {
1691        run_program_and_log_output("chroot " MNT_RESTORING
1692                                   " lilo -M /dev/hda", 3);
1693        run_program_and_log_output("chroot " MNT_RESTORING
1694                                   " lilo -M /dev/sda", 3);
1695    }
1696    return (res);
1697}
1698
1699/**************************************************************************
1700 *END_RUN_LILO                                                            *
1701 **************************************************************************/
1702
1703
1704/**
1705 * Install a raw MBR onto @p bd.
1706 * @param offer_to_hack_scripts If TRUE, then offer to hack the user's fstab for them.
1707 * @param bd The device to copy the stored MBR to.
1708 * @return 0 for success, nonzero for failure.
1709 */
1710int run_raw_mbr(bool offer_to_hack_scripts, char *bd)
1711{
1712  /** malloc **/
1713    char *command = NULL;
1714    char *boot_device = NULL;
1715    char *tmp = NULL;
1716    char *editor;
1717    int res;
1718    int done;
1719
1720    malloc_string(boot_device);
1721    assert_string_is_neither_NULL_nor_zerolength(bd);
1722
1723    strcpy(boot_device, bd);
1724
1725    if (offer_to_hack_scripts
1726        && ask_me_yes_or_no("Did you change the mountlist or cloned the system ?")) {
1727        /* interactive mode */
1728        mvaddstr_and_log_it(g_currentY, 0,
1729                            "Modifying fstab and restoring MBR...                           ");
1730        for (done = FALSE; !done;) {
1731            if (!run_program_and_log_output("which vi", FALSE)) {
1732                popup_and_OK("You will now edit fstab");
1733                if (!g_text_mode) {
1734                    newtSuspend();
1735                }
1736                mr_asprintf(editor, "%s", find_my_editor());
1737                mr_asprintf(tmp, "%s " MNT_RESTORING "/etc/fstab", editor);
1738                mr_free(editor);
1739
1740                paranoid_system(tmp);
1741                mr_free(tmp);
1742                if (!g_text_mode) {
1743                    newtResume();
1744                }
1745            }
1746            popup_and_get_string("Boot device", "Please confirm/enter the boot device. If in doubt, try /dev/hda", boot_device, MAX_STR_LEN / 4);
1747            mr_asprintf(command, "stabraw-me %s", boot_device);
1748            res = run_program_and_log_output(command, 3);
1749            mr_free(command);
1750
1751            if (res) {
1752                done = ask_me_yes_or_no("Modifications failed. Re-try?");
1753            } else {
1754                done = TRUE;
1755            }
1756        }
1757    } else {
1758        /* nuke mode */
1759        mr_asprintf(command, "raw-MR %s "MINDI_CACHE"/mountlist.txt", boot_device);
1760        log_msg(2, "run_raw_mbr() --- command='%s'", command);
1761
1762        mvaddstr_and_log_it(g_currentY, 0,
1763                            "Restoring MBR...                                               ");
1764        res = run_program_and_log_output(command, 3);
1765        mr_free(command);
1766    }
1767    if (res) {
1768        mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
1769        log_to_screen
1770            ("MBR+fstab processed w/error(s). See %s for more info.", MONDO_LOGFILE);
1771    } else {
1772        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1773    }
1774    paranoid_free(boot_device);
1775    return (res);
1776}
1777
1778/**************************************************************************
1779 *END_RUN_RAW_MBR                                                         *
1780 **************************************************************************/
1781
1782
1783
1784/**
1785 * malloc() and set sensible defaults for the mondorestore filename variables.
1786 * @param bkpinfo The backup information structure. Fields used:
1787 * - @c bkpinfo->tmpdir
1788 * - @c bkpinfo->disaster_recovery
1789 */
1790void setup_MR_global_filenames()
1791{
1792    char *temppath;
1793
1794    assert(bkpinfo != NULL);
1795
1796    malloc_string(g_biggielist_txt);
1797    malloc_string(g_filelist_full);
1798    malloc_string(g_filelist_imagedevs);
1799    malloc_string(g_imagedevs_restthese);
1800    malloc_string(g_mondo_cfg_file);
1801    malloc_string(g_mountlist_fname);
1802    malloc_string(g_mondo_home);
1803    malloc_string(g_isodir_device);
1804    malloc_string(g_isodir_format);
1805
1806    temppath = bkpinfo->tmpdir;
1807
1808    sprintf(g_biggielist_txt, "%s/%s", temppath, BIGGIELIST_TXT_STUB);
1809    sprintf(g_filelist_full, "%s/%s", temppath, FILELIST_FULL_STUB);
1810    sprintf(g_filelist_imagedevs, "%s/tmp/filelist.imagedevs", temppath);
1811//  sprintf(g_imagedevs_pot, "%s/tmp/imagedevs.pot", temppath);
1812    sprintf(g_imagedevs_restthese, "%s/tmp/imagedevs.restore-these",
1813            temppath);
1814    if (bkpinfo->disaster_recovery) {
1815        sprintf(g_mondo_cfg_file, "/%s", MONDO_CFG_FILE_STUB);
1816        sprintf(g_mountlist_fname, "/%s", MOUNTLIST_FNAME_STUB);
1817    } else {
1818        sprintf(g_mondo_cfg_file, "%s/%s", temppath, MONDO_CFG_FILE_STUB);
1819        sprintf(g_mountlist_fname, "%s/%s", temppath,
1820                MOUNTLIST_FNAME_STUB);
1821    }
1822}
1823
1824/**************************************************************************
1825 *END_SET_GLOBAL_FILENAME                                                 *
1826 **************************************************************************/
1827
1828
1829/**
1830 * Copy @p input_file (containing the result of a compare) to @p output_file,
1831 * deleting spurious "changes" along the way.
1832 * @param output_file The output file to write with spurious changes removed.
1833 * @param input_file The input file, a list of changed files created by a compare.
1834 */
1835void streamline_changes_file(char *output_file, char *input_file)
1836{
1837    FILE *fin;
1838    FILE *fout;
1839    char *incoming = NULL;
1840
1841    assert_string_is_neither_NULL_nor_zerolength(output_file);
1842    assert_string_is_neither_NULL_nor_zerolength(input_file);
1843
1844    if (!(fin = fopen(input_file, "r"))) {
1845        log_OS_error(input_file);
1846        return;
1847    }
1848    if (!(fout = fopen(output_file, "w"))) {
1849        fatal_error("cannot open output_file");
1850    }
1851    for (mr_getline(incoming, fin); !feof(fin); mr_getline(incoming, fin)) {
1852        if (strncmp(incoming, "etc/adjtime", 11)
1853            && strncmp(incoming, "etc/mtab", 8)
1854            && strncmp(incoming, "tmp/", 4)
1855            && strncmp(incoming, "boot/map", 8)
1856            && !strstr(incoming, "incheckentry")
1857            && strncmp(incoming, "etc/mail/statistics", 19)
1858            && strncmp(incoming, "var/", 4))
1859            fprintf(fout, "%s", incoming);  /* don't need \n here, for some reason.. */
1860        mr_free(incoming);
1861    }
1862    mr_free(incoming);
1863    paranoid_fclose(fout);
1864    paranoid_fclose(fin);
1865}
1866
1867/**************************************************************************
1868 *END_STREAMLINE_CHANGES_FILE                                             *
1869 **************************************************************************/
1870
1871
1872/**
1873 * Give the user twenty seconds to press Ctrl-Alt-Del before we nuke their drives.
1874 */
1875void twenty_seconds_til_yikes()
1876{
1877    int i;
1878    /* MALLOC * */
1879    char *tmp = NULL;
1880
1881    if (does_file_exist("/tmp/NOPAUSE")) {
1882        return;
1883    }
1884    open_progress_form("CAUTION",
1885                       "Be advised: I am about to ERASE your hard disk(s)!",
1886                       "You may press Ctrl+Alt+Del to abort safely.",
1887                       "", 20);
1888    for (i = 0; i < 20; i++) {
1889        g_current_progress = i;
1890        mr_asprintf(tmp, "You have %d seconds left to abort.", 20 - i);
1891        update_progress_form(tmp);
1892        mr_free(tmp);
1893        sleep(1);
1894    }
1895    close_progress_form();
1896}
1897
1898/**************************************************************************
1899 *END_TWENTY_SECONDS_TIL_YIKES                                            *
1900 **************************************************************************/
1901
1902
1903/**
1904 * Unmount all devices in @p p_external_copy_of_mountlist.
1905 * @param p_external_copy_of_mountlist The mountlist to guide the devices to unmount.
1906 * @return 0 for success, nonzero for failure.
1907 */
1908int unmount_all_devices(struct mountlist_itself
1909                        *p_external_copy_of_mountlist)
1910{
1911    struct mountlist_itself *mountlist;
1912    int retval = 0, lino, res = 0, i;
1913    char *command = NULL;
1914    char *tmp = NULL;
1915
1916    assert(p_external_copy_of_mountlist != NULL);
1917
1918    mountlist = malloc(sizeof(struct mountlist_itself));
1919    memcpy((void *) mountlist, (void *) p_external_copy_of_mountlist,
1920           sizeof(struct mountlist_itself));
1921    sort_mountlist_by_mountpoint(mountlist, 0);
1922
1923    run_program_and_log_output("df -m -P", 3);
1924    mvaddstr_and_log_it(g_currentY, 0, "Unmounting devices      ");
1925    open_progress_form("Unmounting devices",
1926                       "Unmounting all devices that were mounted,",
1927                       "in preparation for the post-restoration reboot.",
1928                       "", mountlist->entries);
1929    if (chdir("/")) {
1930        // FIXME
1931    }
1932    for (i = 0;
1933         i < 10
1934         &&
1935         run_program_and_log_output
1936         ("ps | grep buffer | grep -v \"grep buffer\"", TRUE) == 0;
1937         i++) {
1938        sleep(1);
1939        log_msg(2, "Waiting for buffer() to finish");
1940    }
1941
1942    sync();
1943
1944    mr_asprintf(tmp, "cp -f %s " MNT_RESTORING "/var/log", MONDO_LOGFILE);
1945    if (run_program_and_log_output(tmp, FALSE)) {
1946        log_msg(1,
1947                "Error. Failed to copy log to PC's /var/log dir. (Mounted read-only?)");
1948    }
1949    paranoid_free(tmp);
1950    if (does_file_exist("/tmp/DUMBASS-GENTOO")) {
1951        run_program_and_log_output("mkdir -p " MNT_RESTORING
1952                                   "/mnt/.boot.d", 5);
1953    }
1954
1955    /* Unmounting the local /proc and /sys first */
1956    run_program_and_log_output("umount -d " MNT_RESTORING "/proc",3);
1957    run_program_and_log_output("umount -d " MNT_RESTORING "/sys",3);
1958
1959    for (lino = mountlist->entries - 1; lino >= 0; lino--) {
1960        if (!strcmp(mountlist->el[lino].mountpoint, "lvm")) {
1961            continue;
1962        }
1963        mr_asprintf(tmp, "Unmounting device %s  ", mountlist->el[lino].device);
1964        update_progress_form(tmp);
1965
1966        if (is_this_device_mounted(mountlist->el[lino].device)) {
1967            if (!strcmp(mountlist->el[lino].mountpoint, "swap")) {
1968                mr_asprintf(command, "swapoff %s", mountlist->el[lino].device);
1969            } else {
1970                if (!strcmp(mountlist->el[lino].mountpoint, "/1")) {
1971                    mr_asprintf(command, "umount -d %s/", MNT_RESTORING);
1972                    log_msg(3,
1973                            "Well, I know a certain kitty-kitty who'll be sleeping with Mommy tonight...");
1974                } else {
1975                    mr_asprintf(command, "umount -d " MNT_RESTORING "%s", mountlist->el[lino].mountpoint);
1976
1977                    /* To support latest Ubuntu where /var is a separate FS
1978                     * Cf: http://linux.derkeiler.com/Mailing-Lists/Ubuntu/2007-04/msg01319.html
1979                     * we need to create some dirs under the real / before unmounting it */
1980                    if (!strcmp(mountlist->el[lino].mountpoint, "/")) {
1981                        run_program_and_log_output("mkdir -p " MNT_RESTORING "/var/lock", FALSE);
1982                        run_program_and_log_output("mkdir -p " MNT_RESTORING "/var/run", FALSE);
1983                    }
1984                }
1985            }
1986            log_msg(10, "The 'umount' command is '%s'", command);
1987            res = run_program_and_log_output(command, 3);
1988            mr_free(command);
1989        } else {
1990            mr_strcat(tmp, "...not mounted anyway :-) OK");
1991            res = 0;
1992        }
1993        g_current_progress++;
1994        if (res) {
1995            mr_strcat(tmp, "...Failed");
1996            retval++;
1997            log_to_screen(tmp);
1998        } else {
1999            log_msg(2, tmp);
2000        }
2001        paranoid_free(tmp);
2002    }
2003    close_progress_form();
2004    if (retval) {
2005        mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
2006    } else {
2007        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
2008    }
2009    if (retval) {
2010        log_to_screen("Unable to unmount some of your partitions.");
2011    } else {
2012        log_to_screen("All partitions were unmounted OK.");
2013    }
2014    free(mountlist);
2015    return (retval);
2016}
2017
2018/**************************************************************************
2019 *END_UNMOUNT_ALL_DEVICES                                                 *
2020 **************************************************************************/
2021
2022/**
2023 * Extract mondo-restore.cfg and the mountlist from the tape inserted
2024 * to the ./tmp/ directory.
2025 * @param dev The tape device to read from.
2026 * @return 0 for success, nonzero for failure.
2027 */
2028int extract_cfg_file_and_mountlist_from_tape_dev(char *dev)
2029{
2030    char *command = NULL;
2031    int res = 0;
2032
2033    if (bkpinfo->use_obdr) {
2034        skip_obdr();
2035    } else {
2036        // BCO: below 32KB seems to block at least on RHAS 2.1 and MDK 10.0
2037        set_tape_block_size_with_mt(bkpinfo->internal_tape_block_size);
2038    }
2039
2040    mr_asprintf(command, "dd if=%s bs=%ld count=%ld 2> /dev/null | tar -zx ./%s ./%s ./%s ./%s ./%s", dev, bkpinfo->internal_tape_block_size, 1024L * 1024 * 32 / bkpinfo->internal_tape_block_size, MOUNTLIST_FNAME_STUB, MONDO_CFG_FILE_STUB, BIGGIELIST_TXT_STUB, FILELIST_FULL_STUB, IWANTMYLVM_STUB);
2041    log_msg(2, "command = '%s'", command);
2042    res = run_program_and_log_output(command, -1);
2043    mr_free(command);
2044
2045    if (res != 0) {
2046        if (does_file_exist(MONDO_CFG_FILE_STUB)) {
2047            res = 0;
2048        } else {
2049            /* Doing that allow us to remain compatible with pre-2.2.5 versions */
2050            log_msg(2, "pre-2.2.4 compatible mode on");
2051            mr_asprintf(command,    "dd if=%s bs=%ld count=%ld 2> /dev/null | tar -zx %s %s %s %s %s", dev, bkpinfo->internal_tape_block_size, 1024L * 1024 * 32 / bkpinfo->internal_tape_block_size, MOUNTLIST_FNAME_STUB, MONDO_CFG_FILE_STUB, BIGGIELIST_TXT_STUB, FILELIST_FULL_STUB, IWANTMYLVM_STUB);
2052            log_msg(2, "command = '%s'", command);
2053            res = run_program_and_log_output(command, -1);
2054            mr_free(command);
2055            if ((res != 0) && (does_file_exist(MONDO_CFG_FILE_STUB))) {
2056                res = 0;
2057            }
2058        }
2059    }
2060    paranoid_free(command);
2061    return (res);
2062}
2063
2064
2065/**
2066 * Get the configuration file from the floppy, tape, or CD.
2067 * @param bkpinfo The backup information structure. Fields used:
2068 * - @c bkpinfo->backup_media_type
2069 * - @c bkpinfo->media_device
2070 * - @c bkpinfo->tmpdir
2071 * @return 0 for success, nonzero for failure.
2072 */
2073int get_cfg_file_from_archive()
2074{
2075    int retval = 0;
2076
2077   /** malloc *****/
2078    char *command = NULL;
2079    char *cfg_file = NULL;
2080    char *tmp = NULL;
2081    char *mountpt = NULL;
2082    char *mountlist_file = NULL;
2083    bool extract_mountlist_stub = FALSE;
2084    bool extract_i_want_my_lvm = FALSE;
2085
2086    bool try_plan_B;
2087
2088    assert(bkpinfo != NULL);
2089    log_msg(2, "gcffa --- starting");
2090    log_to_screen("I'm thinking...");
2091    mr_asprintf(mountpt, "%s/mount.bootdisk", bkpinfo->tmpdir);
2092    if (chdir(bkpinfo->tmpdir)) {
2093        // FIXME
2094    }
2095    mr_asprintf(cfg_file, "%s", MONDO_CFG_FILE_STUB);
2096    unlink(cfg_file);           // cfg_file[] is missing the '/' at the start, FYI, by intent
2097    mr_free(cfg_file);
2098
2099    unlink(FILELIST_FULL_STUB);
2100    unlink(BIGGIELIST_TXT_STUB);
2101    mr_asprintf(command, "mkdir -p %s", mountpt);
2102    run_program_and_log_output(command, FALSE);
2103    mr_free(command);
2104
2105    mr_asprintf(cfg_file, "%s/%s", bkpinfo->tmpdir, MONDO_CFG_FILE_STUB);
2106    mr_asprintf(mountlist_file, "%s/%s", bkpinfo->tmpdir, MOUNTLIST_FNAME_STUB);
2107    log_msg(2, "mountpt = %s; cfg_file=%s", mountpt, cfg_file);
2108    mr_free(mountpt);
2109
2110    if (!does_file_exist(cfg_file)) {
2111        log_msg(2, "gcffa --- we don't have cfg file yet.");
2112        if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
2113            try_plan_B = TRUE;
2114        } else {
2115            log_msg(2, "gcffa --- calling mount_media now :)");
2116            if (!mount_media()) {
2117                log_msg(2,
2118                        "gcffa --- managed to mount CD; so, no need for Plan B");
2119                try_plan_B = FALSE;
2120            } else {
2121                try_plan_B = TRUE;
2122            }
2123            if (what_number_cd_is_this() > 1) {
2124                insist_on_this_cd_number((g_current_media_number = 1));
2125            }
2126        }
2127        if (try_plan_B) {
2128            log_msg(2, "gcffa --- OK, switching to Plan B");
2129            if (chdir(bkpinfo->tmpdir)) {
2130                // FIXME
2131            }
2132            run_program_and_log_output("mkdir -p tmp", FALSE);
2133
2134            if (strlen(bkpinfo->media_device) == 0) {
2135                strcpy(bkpinfo->media_device, "/dev/st0");
2136                log_msg(2, "media_device is blank; assuming %s");
2137            }
2138            mr_asprintf(tmp, "%s", bkpinfo->media_device);
2139            if (extract_cfg_file_and_mountlist_from_tape_dev
2140                (bkpinfo->media_device)) {
2141                strcpy(bkpinfo->media_device, "/dev/st0");
2142                if (extract_cfg_file_and_mountlist_from_tape_dev
2143                    (bkpinfo->media_device)) {
2144                    strcpy(bkpinfo->media_device, "/dev/osst0");
2145                    if (extract_cfg_file_and_mountlist_from_tape_dev
2146                        (bkpinfo->media_device)) {
2147                        strcpy(bkpinfo->media_device, "/dev/ht0");
2148                        if (extract_cfg_file_and_mountlist_from_tape_dev
2149                            (bkpinfo->media_device)) {
2150                            log_msg(3,
2151                                    "I tried lots of devices but none worked.");
2152                            strcpy(bkpinfo->media_device, tmp);
2153                        }
2154                    }
2155                }
2156            }
2157            mr_free(tmp);
2158
2159            if (!does_file_exist("tmp/mondo-restore.cfg")) {
2160                log_to_screen("Cannot find config info on media");
2161                return (1);
2162            }
2163        } else {
2164                if (does_file_exist("/"MOUNTLIST_FNAME_STUB)) {
2165                    extract_mountlist_stub = FALSE;
2166                } else {
2167                    extract_mountlist_stub = TRUE;
2168                }
2169                if (does_file_exist("/"IWANTMYLVM_STUB)) {
2170                    extract_i_want_my_lvm = FALSE;
2171                } else {
2172                    extract_i_want_my_lvm = TRUE;
2173                }
2174
2175                log_msg(2,
2176                        "gcffa --- Plan B, a.k.a. untarring some file from all.tar.gz");
2177                mr_asprintf(command, "tar -zxvf " MNT_CDROM "/images/all.tar.gz ./%s ./%s ./%s ./%s ./%s", MOUNTLIST_FNAME_STUB, MONDO_CFG_FILE_STUB, BIGGIELIST_TXT_STUB, FILELIST_FULL_STUB, IWANTMYLVM_STUB);    // add -b TAPE_BLOCK_SIZE if you _really_ think it's necessary
2178                run_program_and_log_output(command, TRUE);
2179                mr_free(command);
2180
2181                if (!does_file_exist(MONDO_CFG_FILE_STUB)) {
2182                    /* Doing that allow us to remain compatible with pre-2.2.5 versions */
2183                    log_msg(2, "pre-2.2.4 compatible mode on");
2184                    mr_asprintf(command, "tar -zxvf " MNT_CDROM "/images/all.tar.gz %s %s %s %s %s", MOUNTLIST_FNAME_STUB, MONDO_CFG_FILE_STUB, BIGGIELIST_TXT_STUB, FILELIST_FULL_STUB, IWANTMYLVM_STUB);  // add -b TAPE_BLOCK_SIZE if you _really_ think it's necessary
2185                    run_program_and_log_output(command, TRUE);
2186                    mr_free(command);
2187                    if (!does_file_exist(MONDO_CFG_FILE_STUB)) {
2188                        fatal_error
2189                            ("Please reinsert the disk/CD and try again.");
2190                    }
2191                }
2192        }
2193    }
2194    if (does_file_exist(MONDO_CFG_FILE_STUB)) {
2195        log_msg(1, "gcffa --- great! We've got the config file");
2196        mr_asprintf(tmp, "%s/%s", call_program_and_get_last_line_of_output("pwd"), MONDO_CFG_FILE_STUB);
2197        mr_asprintf(command, "cp -f %s %s", tmp, cfg_file);
2198        log_it("%s",command);
2199        if (strcmp(tmp, cfg_file)
2200            && run_program_and_log_output(command, 1)) {
2201            log_msg(1,
2202                    "... but an error occurred when I tried to move it to %s",
2203                    cfg_file);
2204        } else {
2205            log_msg(1, "... and I moved it successfully to %s", cfg_file);
2206        }
2207        mr_free(command);
2208
2209        mr_asprintf(command, "cp -f %s/%s %s", call_program_and_get_last_line_of_output("pwd"), MOUNTLIST_FNAME_STUB, mountlist_file);
2210        log_it("%s",command);
2211        if (extract_mountlist_stub) {
2212            if (strcmp(tmp, cfg_file)
2213                && run_program_and_log_output(command, 1)) {
2214                log_msg(1, "Failed to get mountlist");
2215            } else {
2216                log_msg(1, "Got mountlist too");
2217                mr_free(command);
2218                mr_asprintf(command, "cp -f %s %s", mountlist_file, g_mountlist_fname);
2219                if (run_program_and_log_output(command, 1)) {
2220                    log_msg(1, "Failed to copy mountlist to /tmp");
2221                } else {
2222                    log_msg(1, "Copied mountlist to /tmp as well OK");
2223                    mr_free(command);
2224                    mr_asprintf(command, "cp -f %s /tmp/",IWANTMYLVM_STUB);
2225                    run_program_and_log_output(command, 1);
2226                }
2227            }
2228        }
2229        mr_free(command);
2230        mr_free(tmp);
2231    }
2232
2233    run_program_and_log_output("umount -d " MNT_CDROM, FALSE);
2234    if (!does_file_exist(cfg_file)) {
2235        log_it("%s",cfg_file);
2236        log_msg(1, "%s not found", cfg_file);
2237        log_to_screen
2238            ("Oh dear. Unable to recover configuration file from boot disk");
2239        return (1);
2240    }
2241
2242    log_to_screen("Recovered mondo-restore.cfg");
2243    if (!does_file_exist(MOUNTLIST_FNAME_STUB)) {
2244        log_to_screen("...but not mountlist.txt - a pity, really...");
2245    }
2246    else {
2247            /* Is this code really useful ??? */
2248        if (extract_mountlist_stub) {
2249            mr_asprintf(command, "cp -f %s %s/%s", MOUNTLIST_FNAME_STUB, bkpinfo->tmpdir, MOUNTLIST_FNAME_STUB);
2250            run_program_and_log_output(command, FALSE);
2251            mr_free(command);
2252        }
2253    }
2254
2255    mr_asprintf(command, "cp -f %s /%s", cfg_file, MONDO_CFG_FILE_STUB);
2256    mr_free(cfg_file);
2257
2258    run_program_and_log_output(command, FALSE);
2259    mr_free(command);
2260
2261    if (extract_mountlist_stub) {
2262        mr_asprintf(command, "cp -f %s /%s", mountlist_file, MOUNTLIST_FNAME_STUB);
2263        run_program_and_log_output(command, FALSE);
2264        mr_free(command);
2265    }
2266    mr_free(mountlist_file);
2267
2268    mr_asprintf(command, "cp -f etc/raidtab /etc/");
2269    run_program_and_log_output(command, FALSE);
2270    mr_free(command);
2271
2272    if (extract_i_want_my_lvm) {
2273        mr_asprintf(command, "cp -f %s /tmp/",IWANTMYLVM_STUB);
2274        run_program_and_log_output(command, FALSE);
2275        mr_free(command);
2276    }
2277    g_backup_media_type = bkpinfo->backup_media_type;
2278    return (retval);
2279}
2280
2281/**************************************************************************
2282 *END_GET_CFG_FILE_FROM_ARCHIVE                                           *
2283 **************************************************************************/
2284
2285/* @} - end restoreUtilityGroup */
2286
2287void wait_until_software_raids_are_prepped(char *mdstat_file, int wait_for_percentage)
2288{
2289    struct raidlist_itself *raidlist;
2290    int unfinished_mdstat_devices = 9999;
2291    int i = 0;
2292    char *screen_message = NULL;
2293
2294    raidlist = malloc(sizeof(struct raidlist_itself));
2295
2296    assert(wait_for_percentage <= 100);
2297    log_it("wait_until_software_raids_are_prepped");
2298    while (unfinished_mdstat_devices > 0) {
2299            // FIXME: Prefix '/dev/' should really be dynamic!
2300        if (parse_mdstat(MDSTAT_FILE, raidlist, "/dev/")) {
2301            log_to_screen("Sorry, cannot read %s", MDSTAT_FILE);
2302            log_msg(1,"Sorry, cannot read %s", MDSTAT_FILE);
2303            return;
2304        }
2305        for (unfinished_mdstat_devices = 0; i <= raidlist->entries; i++) {
2306            if (raidlist->el[i].progress < wait_for_percentage) {
2307                unfinished_mdstat_devices++;
2308                if (raidlist->el[i].progress == -1) // delayed while another partition inits
2309                {
2310                    continue;
2311                }
2312                log_msg(1,"Sync'ing %s (i=%d)", raidlist->el[i].raid_device, i);
2313                mr_asprintf(screen_message, "Sync'ing %s", raidlist->el[i].raid_device);
2314                open_evalcall_form(screen_message);
2315                mr_free(screen_message);
2316
2317                while (raidlist->el[i].progress < wait_for_percentage) {
2318                    log_msg(1,"Percentage sync'ed: %d", raidlist->el[i].progress);
2319                    update_evalcall_form(raidlist->el[i].progress);
2320                    sleep(2);
2321                    // FIXME: Prefix '/dev/' should really be dynamic!
2322                    if (parse_mdstat(MDSTAT_FILE, raidlist, "/dev/")) {
2323                        break;
2324                    }
2325                }
2326                close_evalcall_form();
2327            }
2328        }
2329    }
2330    paranoid_free(raidlist);
2331}
Note: See TracBrowser for help on using the repository browser.