source: branches/2.2.9/mondo/src/common/libmondo-devices.c @ 2187

Last change on this file since 2187 was 2187, checked in by Bruno Cornec, 11 years ago

Cleanup of code to remove duplication

  • Property svn:keywords set to Id
File size: 70.8 KB
Line 
1/* libmondo-devices.c                 Subroutines for handling devices
2   $Id: libmondo-devices.c 2187 2009-04-30 16:00:14Z bruno $
3*/
4
5/**
6 * @file
7 * Functions to handle interactions with backup devices.
8 */
9
10#include "my-stuff.h"
11#include "mondostructures.h"
12#include "libmondo-files-EXT.h"
13#include "libmondo-devices.h"
14#include "lib-common-externs.h"
15#include "libmondo-string-EXT.h"
16#include "libmondo-tools-EXT.h"
17#include "libmondo-gui-EXT.h"
18#include "libmondo-fork-EXT.h"
19#include "libmondo-stream-EXT.h"
20
21#include <sys/types.h>
22#ifdef __FreeBSD__
23#define DKTYPENAMES
24#define FSTYPENAMES
25#include <sys/disklabel.h>
26#include <sys/disk.h>
27#elif linux
28#define u64 unsigned long long
29#include <linux/fs.h>           /* for BLKGETSIZE64 */
30#include <linux/hdreg.h>
31#endif
32
33/*@unused@*/
34//static char cvsid[] = "$Id: libmondo-devices.c 2187 2009-04-30 16:00:14Z bruno $";
35
36extern int g_current_media_number;
37extern double g_kernel_version;
38
39extern bool g_ISO_restore_mode;
40extern char *g_selfmounted_isodir;
41extern char *MONDO_LOGFILE;
42
43extern void setup_tmpdir(char *path);
44
45static char g_cdrw_drive_is_here[MAX_STR_LEN / 4] = "";
46static char g_cdrom_drive_is_here[MAX_STR_LEN / 4] = "";
47static char g_dvd_drive_is_here[MAX_STR_LEN / 4] = "";
48
49
50/**
51 * ????? @bug ?????
52 * @ingroup globalGroup
53 */
54bool g_restoring_live_from_cd = FALSE;
55bool g_restoring_live_from_nfs = FALSE;
56
57extern t_bkptype g_backup_media_type;   // set by main()
58
59/* Reference to global bkpinfo */
60extern struct s_bkpinfo *bkpinfo;
61
62
63
64
65void set_g_cdrom_and_g_dvd_to_bkpinfo_value()
66{
67    strcpy(g_cdrom_drive_is_here, bkpinfo->media_device);   // just in case
68    strcpy(g_dvd_drive_is_here, bkpinfo->media_device); // just in case
69}
70
71
72
73/**
74 * Retract all CD trays and wait for autorun to complete.
75 * @ingroup deviceGroup
76 */
77void retract_CD_tray_and_defeat_autorun(void)
78{
79//  log_it("rctada: Retracting all CD trays", __LINE__);
80    if (strlen(g_cdrom_drive_is_here) > 0) {
81        inject_device(g_cdrom_drive_is_here);
82    }
83    if (strlen(g_dvd_drive_is_here) > 0) {
84        inject_device(g_dvd_drive_is_here);
85    }
86    if (strlen(g_cdrw_drive_is_here) > 0) {
87        inject_device(g_cdrw_drive_is_here);
88    }
89//  log_it("rctada: killing autorun");
90//  run_program_and_log_output("killall autorun", TRUE);
91    if (!run_program_and_log_output("ps | grep autorun | grep -v grep", 5)) {
92        log_it("autorun detected; sleeping for 2 seconds");
93        sleep(2);
94    }
95    log_it("rctada: Unmounting all CD drives", __LINE__);
96    run_program_and_log_output("umount /dev/cdr* /dev/dvd*", 5);
97}
98
99
100
101/**
102 * Determine whether we're booted off a ramdisk.
103 * @return @c TRUE (we are) or @c FALSE (we aren't).
104 * @ingroup utilityGroup
105 */
106bool am_I_in_disaster_recovery_mode(void)
107{
108    char *tmp, *comment;
109    bool is_this_a_ramdisk = FALSE;
110
111    malloc_string(tmp);
112    malloc_string(comment);
113    strcpy(tmp, where_is_root_mounted());
114    sprintf(comment, "root is mounted at %s\n", tmp);
115    log_msg(0, comment);
116    log_msg(0,
117            "No, Schlomo, that doesn't mean %s is the root partition. It's just a debugging message. Relax. It's part of am_I_in_disaster_recovery_mode().",
118            tmp);
119
120#ifdef __FreeBSD__
121    if (strstr(tmp, "/dev/md")) {
122        is_this_a_ramdisk = TRUE;
123    }
124#else
125    if (!strncmp(tmp, "/dev/ram", 8)
126        || (!strncmp(tmp, "/dev/rd", 7) && !strcmp(tmp, "/dev/rd/")
127            && strncmp(tmp, "/dev/rd/cd", 10)) || strstr(tmp, "rootfs")
128        || !strcmp(tmp, "/dev/root")) {
129        is_this_a_ramdisk = TRUE;
130    } else {
131        is_this_a_ramdisk = FALSE;
132    }
133#endif
134
135    if (is_this_a_ramdisk) {
136        if (!does_file_exist("/THIS-IS-A-RAMDISK")) {
137            log_to_screen
138                ("Using /dev/root is stupid of you but I'll forgive you.");
139            is_this_a_ramdisk = FALSE;
140        }
141    }
142    if (does_file_exist("/THIS-IS-A-RAMDISK")) {
143        is_this_a_ramdisk = TRUE;
144    }
145    paranoid_free(tmp);
146    paranoid_free(comment);
147    log_msg(1, "Is this a ramdisk? result = %d", is_this_a_ramdisk);
148    return (is_this_a_ramdisk);
149}
150
151
152
153
154
155/**
156 * Turn @c bkpinfo->backup_media_type into a human-readable string.
157 * @return The human readable string (e.g. @c cdr becomes <tt>"cdr"</tt>).
158 * @note The returned string points to static storage that will be overwritten with each call.
159 * @ingroup stringGroup
160 */
161static char *bkptype_to_string(t_bkptype bt)
162{
163    static char output[MAX_STR_LEN / 4];
164    switch (bt) {
165    case none:
166        strcpy(output, "none");
167        break;
168    case iso:
169        strcpy(output, "iso");
170        break;
171    case cdr:
172        strcpy(output, "cdr");
173        break;
174    case cdrw:
175        strcpy(output, "cdrw");
176        break;
177    case cdstream:
178        strcpy(output, "cdstream");
179        break;
180    case nfs:
181        strcpy(output, "nfs");
182        break;
183    case tape:
184        strcpy(output, "tape");
185        break;
186    case udev:
187        strcpy(output, "udev");
188        break;
189    case usb:
190        strcpy(output, "usb");
191        break;
192    default:
193        strcpy(output, "default");
194    }
195    return (output);
196}
197
198
199
200/**
201 * @addtogroup deviceGroup
202 * @{
203 */
204/**
205 * Eject the tray of the specified CD device.
206 * @param dev The device to eject.
207 * @return the return value of the @c eject command. (0=success, nonzero=failure)
208 */
209int eject_device(char *dev)
210{
211    char *command;
212    int res1 = 0, res2 = 0;
213
214    malloc_string(command);
215
216    if (IS_THIS_A_STREAMING_BACKUP(g_backup_media_type)
217        && g_backup_media_type != udev) {
218        sprintf(command, "mt -f %s offline", dev);
219        res1 = run_program_and_log_output(command, 1);
220    } else {
221        res1 = 0;
222    }
223
224#ifdef __FreeBSD__
225    if (strstr(dev, "acd")) {
226        sprintf(command, "cdcontrol -f %s eject", dev);
227    } else {
228        sprintf(command, "camcontrol eject `echo %s | sed 's|/dev/||'`",
229                dev);
230    }
231#else
232    sprintf(command, "eject %s", dev);
233#endif
234
235    log_msg(3, "Ejecting %s", dev);
236    res2 = run_program_and_log_output(command, 1);
237    paranoid_free(command);
238    if (res1 && res2) {
239        return (1);
240    } else {
241        return (0);
242    }
243}
244
245/**
246 * Load (inject) the tray of the specified CD device.
247 * @param dev The device to load/inject.
248 * @return 0 for success, nonzero for failure.
249 */
250int inject_device(char *dev)
251{
252    char *command;
253    int i;
254
255    malloc_string(command);
256
257
258#ifdef __FreeBSD__
259    if (strstr(dev, "acd")) {
260        sprintf(command, "cdcontrol -f %s close", dev);
261    } else {
262        sprintf(command, "camcontrol load `echo %s | sed 's|/dev/||'`",
263                dev);
264    }
265#else
266    sprintf(command, "eject -t %s", dev);
267#endif
268    i = run_program_and_log_output(command, FALSE);
269    paranoid_free(command);
270    return (i);
271}
272
273
274/**
275 * Determine whether the specified @p device (really, you can use any file)
276 * exists.
277 * @return TRUE if it exists, FALSE if it doesn't.
278 */
279bool does_device_exist(char *device)
280{
281
282    /*@ buffers *********************************************************** */
283    char *tmp;
284    bool ret;
285
286    malloc_string(tmp);
287    assert_string_is_neither_NULL_nor_zerolength(device);
288
289    sprintf(tmp, "ls %s > /dev/null 2> /dev/null", device);
290
291    if (system(tmp)) {
292        ret = FALSE;
293    } else {
294        ret = TRUE;
295    }
296    paranoid_free(tmp);
297    return (ret);
298}
299
300
301/**
302 * Determine whether a non-Microsoft partition exists on any connected hard drive.
303 * @return TRUE (there's a Linux/FreeBSD partition) or FALSE (Microsoft has taken over yet another innocent PC).
304 */
305bool does_nonMS_partition_exist(void)
306{
307#if __FreeBSD__
308    return
309        !system
310        ("for drive in /dev/ad? /dev/da?; do fdisk $drive | grep -q FreeBSD && exit 0; done; false");
311#else
312    return
313        !system
314        ("parted2fdisk -l 2>/dev/null | grep '^/dev/' | grep -Eqv '(MS|DOS|FAT|NTFS)'");
315#endif
316}
317
318/**
319 * Determine whether the specified @p partno exists on the specified @p drive.
320 * @param drive The drive to search for the partition in.
321 * @param partno The partition number to look for.
322 * @return 0 if it exists, nonzero otherwise.
323 */
324int does_partition_exist(const char *drive, int partno)
325{
326    /*@ buffers **************************************************** */
327    char *program;
328    char *incoming;
329    char *searchstr;
330    char *tmp;
331
332    /*@ ints ******************************************************* */
333    int res = 0;
334
335    /*@ pointers *************************************************** */
336    FILE *fin;
337
338
339    /*@ end vars *************************************************** */
340    assert_string_is_neither_NULL_nor_zerolength(drive);
341    assert(partno >= 0 && partno < 999);
342
343    malloc_string(program);
344    malloc_string(incoming);
345    malloc_string(searchstr);
346    malloc_string(tmp);
347
348#ifdef __FreeBSD__
349    // We assume here that this is running from mondorestore. (It is.)
350    sprintf(program, "ls %s %s >/dev/null 2>&1", drive,
351            build_partition_name(tmp, drive, partno));
352    return system(program);
353#else
354    tmp[0] = '\0';
355#endif
356
357    sprintf(program, "parted2fdisk -l %s 2> /dev/null", drive);
358    fin = popen(program, "r");
359    if (!fin) {
360        log_it("program=%s", program);
361        log_OS_error("Cannot popen-in program");
362        return (0);
363    }
364    (void) build_partition_name(searchstr, drive, partno);
365    strcat(searchstr, " ");
366    for (res = 0; !res && fgets(incoming, MAX_STR_LEN - 1, fin);) {
367        if (strstr(incoming, searchstr)) {
368            res = 1;
369        }
370    }
371    if (pclose(fin)) {
372        log_OS_error("Cannot pclose fin");
373    }
374    paranoid_free(program);
375    paranoid_free(incoming);
376    paranoid_free(searchstr);
377    paranoid_free(tmp);
378    return (res);
379}
380
381
382
383
384
385/**
386 * Determine whether given NULL-terminated @p str exists in the MBR of @p dev.
387 * @param dev The device to look in.
388 * @param str The string to look for.
389 * @return TRUE if it exists, FALSE if it doesn't.
390 */
391bool does_string_exist_in_boot_block(char *dev, char *str)
392{
393    /*@ buffers **************************************************** */
394    char *command;
395
396    /*@ end vars *************************************************** */
397    int i;
398
399    assert_string_is_neither_NULL_nor_zerolength(dev);
400    assert_string_is_neither_NULL_nor_zerolength(str);
401
402    malloc_string(command);
403    sprintf(command,
404            "dd if=%s bs=446 count=1 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null",
405            dev, str);
406    i = system(command);
407    paranoid_free(command);
408    if (i) {
409        return (FALSE);
410    } else {
411        return (TRUE);
412    }
413}
414
415/**
416 * Determine whether specified @p str exists in the first @p n sectors of
417 * @p dev.
418 * @param dev The device to look in.
419 * @param str The string to look for.
420 * @param n The number of 512-byte sectors to search.
421 */
422bool does_string_exist_in_first_N_blocks(char *dev, char *str, int n)
423{
424    /*@ buffers **************************************************** */
425    char *command;
426    /*@ end vars *************************************************** */
427    int i;
428
429    malloc_string(command);
430    sprintf(command,
431            "dd if=%s bs=512 count=%i 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null",
432            dev, n, str);
433    i = system(command);
434    paranoid_free(command);
435    if (i) {
436        return (FALSE);
437    } else {
438        return (TRUE);
439    }
440}
441
442
443
444/**
445 * Try to mount CD-ROM at @p mountpoint. If the CD-ROM is not found or has
446 * not been specified, call find_cdrom_device() to find it.
447 * @param bkpinfo The backup information structure. The only field used is @c bkpinfo->media_device.
448 * @param mountpoint Where to mount the CD-ROM.
449 * @return 0 for success, nonzero for failure.
450 * @see mount_CDROM_here
451 */
452int find_and_mount_actual_cd(char *mountpoint)
453{
454    /*@ buffers ***************************************************** */
455
456    /*@ int's  ****************************************************** */
457    int res;
458    char *dev;
459
460    /*@ end vars **************************************************** */
461
462    malloc_string(dev);
463    assert(bkpinfo != NULL);
464    assert_string_is_neither_NULL_nor_zerolength(mountpoint);
465
466    if (g_backup_media_type == dvd) {
467        strcpy(dev, g_dvd_drive_is_here);
468        if (!dev[0]) {
469            find_dvd_device(dev, FALSE);
470        }
471    } else {
472        strcpy(dev, g_cdrom_drive_is_here);
473        if (!dev[0]) {
474            find_cdrom_device(dev, FALSE);
475        }
476    }
477
478    if (bkpinfo->backup_media_type != iso) {
479        retract_CD_tray_and_defeat_autorun();
480    }
481
482    if (!dev[0] || (res = mount_CDROM_here(dev, mountpoint))) {
483        if (!popup_and_get_string
484            ("CD-ROM device", "Please enter your CD-ROM's /dev device",
485             dev, MAX_STR_LEN / 4)) {
486            res = 1;
487        } else {
488            res = mount_CDROM_here(dev, mountpoint);
489        }
490    }
491    if (res) {
492        log_msg(1, "mount failed");
493    } else {
494        log_msg(1, "mount succeeded with %s", dev);
495    }
496    paranoid_free(dev);
497    return (res);
498}
499
500
501
502
503
504
505/**
506 * Locate a CD-R/W writer's SCSI node.
507 * @param cdrw_device SCSI node will be placed here.
508 * @return 0 for success, nonzero for failure.
509 */
510int find_cdrw_device(char *cdrw_device)
511{
512    /*@ buffers ************************ */
513    char *comment;
514    char *tmp;
515    char *cdr_exe;
516    char *command;
517
518    malloc_string(comment);
519    malloc_string(tmp);
520    malloc_string(cdr_exe);
521    malloc_string(command);
522    if (g_cdrw_drive_is_here[0]) {
523        strcpy(cdrw_device, g_cdrw_drive_is_here);
524        log_msg(3, "Been there, done that. Returning %s", cdrw_device);
525        paranoid_free(comment);
526        paranoid_free(tmp);
527        paranoid_free(cdr_exe);
528        paranoid_free(command);
529        return (0);
530    }
531    if (g_backup_media_type == dvd) {
532        log_msg(1,
533                "This is dumb. You're calling find_cdrw_device() but you're backing up to DVD. WTF?");
534        paranoid_free(comment);
535        paranoid_free(tmp);
536        paranoid_free(cdr_exe);
537        paranoid_free(command);
538        return (1);
539    }
540    run_program_and_log_output("insmod ide-scsi", -1);
541    if (find_home_of_exe("cdrecord")) {
542        strcpy(cdr_exe, "cdrecord");
543    } else {
544        strcpy(cdr_exe, "dvdrecord");
545    }
546    tmp[0] = '\0';
547    if (find_home_of_exe(cdr_exe)) {
548        sprintf(command,
549                "%s -scanbus 2> /dev/null | tr -s '\t' ' ' | grep \"[0-9]*,[0-9]*,[0-9]*\" | grep -v \"[0-9]*) \\*\" | grep CD | cut -d' ' -f2 | head -n1",
550                cdr_exe);
551        strcpy(tmp, call_program_and_get_last_line_of_output(command));
552    }
553    if (strlen(tmp) < 2) {
554        paranoid_free(comment);
555        paranoid_free(tmp);
556        paranoid_free(cdr_exe);
557        paranoid_free(command);
558        return 1;
559    } else {
560        strcpy(cdrw_device, tmp);
561        sprintf(comment, "Found CDRW device - %s", cdrw_device);
562        log_it(comment);
563        strcpy(g_cdrw_drive_is_here, cdrw_device);
564        paranoid_free(comment);
565        paranoid_free(tmp);
566        paranoid_free(cdr_exe);
567        paranoid_free(command);
568        return (0);
569    }
570}
571
572
573
574
575/**
576 * Attempt to locate a CD-ROM device's /dev entry.
577 * Several different methods may be used to find the device, including
578 * calling @c cdrecord, searching @c dmesg, and trial-and-error.
579 * @param output Where to put the located /dev entry.
580 * @param try_to_mount Whether to mount the CD as part of the test; if mount
581 * fails then return failure.
582 * @return 0 for success, nonzero for failure.
583 */
584int find_cdrom_device(char *output, bool try_to_mount)
585{
586    /*@ pointers **************************************************** */
587    FILE *fin;
588    char *p;
589    char *q;
590    char *r;
591    int retval = 0;
592
593    /*@ bool's ****************************************************** */
594    bool found_it = FALSE;
595
596    /*@ buffers ***************************************************** */
597    char *tmp;
598    char *cdr_exe;
599    char *phrase_one;
600    char *phrase_two;
601    char *command;
602    char *dvd_last_resort;
603    char *mountpoint;
604    static char the_last_place_i_found_it[MAX_STR_LEN] = "";
605
606    /*@ intialize *************************************************** */
607    malloc_string(tmp);
608    malloc_string(cdr_exe);
609    malloc_string(phrase_one);
610    malloc_string(phrase_two);
611    malloc_string(command);
612    malloc_string(dvd_last_resort);
613    malloc_string(mountpoint);
614
615    output[0] = '\0';
616    phrase_one[0] = '\0';
617    phrase_two[0] = '\0';
618    dvd_last_resort[0] = '\0';
619
620    /*@ end vars **************************************************** */
621
622    if (g_cdrom_drive_is_here[0] && !isdigit(g_cdrom_drive_is_here[0])) {
623        strcpy(output, g_cdrom_drive_is_here);
624        log_msg(3, "Been there, done that. Returning %s", output);
625        retval = 0;
626        goto end_of_find_cdrom_device;
627    }
628    if (the_last_place_i_found_it[0] != '\0' && !try_to_mount) {
629        strcpy(output, the_last_place_i_found_it);
630        log_msg(3,
631                "find_cdrom_device() --- returning last found location - '%s'",
632                output);
633        retval = 0;
634        goto end_of_find_cdrom_device;
635    }
636
637    sprintf(mountpoint, "%s/cd.mnt", bkpinfo->tmpdir);
638    make_hole_for_dir(mountpoint);
639
640    if (find_home_of_exe("cdrecord")) {
641        strcpy(cdr_exe, "cdrecord");
642    } else {
643        strcpy(cdr_exe, "dvdrecord");
644    }
645    tmp[0] = '\0';
646    if (!find_home_of_exe(cdr_exe)) {
647        strcpy(output, "/dev/cdrom");
648        log_msg(4, "Can't find cdrecord; assuming %s", output);
649        if (!does_device_exist(output)) {
650            log_msg(4, "That didn't work. Sorry.");
651            retval = 1;
652            goto end_of_find_cdrom_device;
653        } else {
654            retval = 0;
655            goto end_of_find_cdrom_device;
656        }
657    }
658
659    sprintf(command, "%s -scanbus 2> /dev/null", cdr_exe);
660    fin = popen(command, "r");
661    if (!fin) {
662        log_msg(4, "command=%s", command);
663        log_OS_error("Cannot popen command");
664        return (1);
665    }
666    for (fgets(tmp, MAX_STR_LEN, fin); !feof(fin);
667         fgets(tmp, MAX_STR_LEN, fin)) {
668        p = strchr(tmp, '\'');
669        if (p) {
670            q = strchr(++p, '\'');
671            if (q) {
672                for (r = q; *(r - 1) == ' '; r--);
673                *r = '\0';
674                strcpy(phrase_one, p);
675                p = strchr(++q, '\'');
676                if (p) {
677                    q = strchr(++p, '\'');
678                    if (q) {
679                        while (*(q - 1) == ' ') {
680                            q--;
681                        }
682                        *q = '\0';
683                        strcpy(phrase_two, p);
684                    }
685                }
686            }
687        }
688    }
689    paranoid_pclose(fin);
690
691#ifndef __FreeBSD__
692    if (strlen(phrase_two) == 0) {
693        log_msg(4, "Not running phase two. String is empty.");
694    } else {
695        sprintf(command, "dmesg | grep \"%s\" 2> /dev/null", phrase_two);
696        fin = popen(command, "r");
697        if (!fin) {
698            log_msg(4, "Cannot run 2nd command - non-fatal, fortunately");
699        } else {
700            for (fgets(tmp, MAX_STR_LEN, fin); !feof(fin);
701                 fgets(tmp, MAX_STR_LEN, fin)) {
702                log_msg(5, "--> '%s'", tmp);
703                if (tmp[0] != ' ' && tmp[1] != ' ') {
704                    p = strchr(tmp, ':');
705                    if (p) {
706                        *p = '\0';
707                        if (strstr(tmp, "DVD")) {
708                            sprintf(dvd_last_resort, "/dev/%s", tmp);
709                            log_msg(4,
710                                    "Ignoring '%s' because it's a DVD drive",
711                                    tmp);
712                        } else {
713                            sprintf(output, "/dev/%s", tmp);
714                            found_it = TRUE;
715                        }
716                    }
717                }
718            }
719            paranoid_pclose(fin);
720        }
721    }
722
723#endif
724#ifdef __FreeBSD__
725    if (!found_it) {
726        log_msg(4, "OK, approach 2");
727        if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/cdrom"))) {
728            if (!
729                (found_it =
730                 set_dev_to_this_if_rx_OK(output, "/dev/cdrom1"))) {
731                if (!
732                    (found_it =
733                     set_dev_to_this_if_rx_OK(output, "/dev/dvd"))) {
734                    if (!
735                        (found_it =
736                         set_dev_to_this_if_rx_OK(output, "/dev/acd0"))) {
737                        if (!
738                            (found_it =
739                             set_dev_to_this_if_rx_OK(output,
740                                                      "/dev/cd01"))) {
741                            if (!
742                                (found_it =
743                                 set_dev_to_this_if_rx_OK(output,
744                                                          "/dev/acd1"))) {
745                                if (!
746                                    (found_it =
747                                     set_dev_to_this_if_rx_OK(output,
748                                                              "/dev/cd1")))
749                                {
750                                    retval = 1;
751                                    goto end_of_find_cdrom_device;
752                                }
753                            }
754                        }
755                    }
756                }
757            }
758        }
759    }
760#else
761    if (!found_it && strlen(dvd_last_resort) > 0) {
762        log_msg(4, "Well, I'll use the DVD - %s - as a last resort",
763                dvd_last_resort);
764        strcpy(output, dvd_last_resort);
765        found_it = TRUE;
766    }
767    if (found_it) {
768        sprintf(tmp, "grep \"%s=ide-scsi\" /proc/cmdline &> /dev/null",
769                strrchr(output, '/') + 1);
770        if (system(tmp) == 0) {
771            log_msg(4,
772                    "%s is not right. It's being SCSI-emulated. Continuing.",
773                    output);
774            found_it = FALSE;
775            output[0] = '\0';
776        }
777    }
778
779    if (found_it) {
780        log_msg(4, "(find_cdrom_device) --> '%s'", output);
781        if (!does_device_exist(output)) {
782            found_it = FALSE;
783            log_msg(4, "OK, I was wrong, I haven't found it... yet.");
784        }
785    }
786
787    if (!found_it) {
788        log_msg(4, "OK, approach 2");
789        if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/scd0"))) {
790            if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/sr0"))) {
791                if (!
792                    (found_it =
793                     set_dev_to_this_if_rx_OK(output, "/dev/cdrom"))) {
794                    if (!
795                        (found_it =
796                         set_dev_to_this_if_rx_OK(output,
797                                                  "/dev/cdrom0"))) {
798                        if (!
799                            (found_it =
800                             set_dev_to_this_if_rx_OK(output,
801                                                      "/dev/cdrom1"))) {
802                            if (!
803                                (found_it =
804                                 set_dev_to_this_if_rx_OK(output,
805                                                          "/dev/sr1"))) {
806                                if (!
807                                    (found_it =
808                                     set_dev_to_this_if_rx_OK(output,
809                                                              "/dev/dvd")))
810                                {
811                                    if (!
812                                        (found_it =
813                                         set_dev_to_this_if_rx_OK(output,
814                                                                  g_cdrw_drive_is_here)))
815                                    {
816                                        retval = 1;
817                                        goto end_of_find_cdrom_device;
818                                    }
819                                }
820                            }
821                        }
822                    }
823                }
824            }
825        }
826    }
827#endif
828
829    if (found_it && try_to_mount) {
830        if (mount_CDROM_here(output, mountpoint)) {
831            log_msg(4, "[Cardigans] I've changed my mind");
832            found_it = FALSE;
833        } else {
834            sprintf(tmp, "%s/archives", mountpoint);
835            if (!does_file_exist(tmp)) {
836                log_msg(4, "[Cardigans] I'll take it back");
837                found_it = FALSE;
838            } else {
839                sprintf(command, "umount %s", output);
840                paranoid_system(command);
841                log_msg(4, "I'm confident the Mondo CD is in %s", output);
842            }
843        }
844    }
845    unlink(mountpoint);
846
847    if (found_it) {
848        if (!does_file_exist(output)) {
849            log_msg(3, "I still haven't found it.");
850            return (1);
851        }
852        log_msg(3, "(find_cdrom_device) --> '%s'", output);
853        strcpy(the_last_place_i_found_it, output);
854        strcpy(g_cdrom_drive_is_here, output);
855        retval = 0;
856        goto end_of_find_cdrom_device;
857    }
858
859    sprintf(command,
860            "%s -scanbus | grep \"[0-9],[0-9],[0-9]\" | grep \"[D|C][V|D]\" | grep -n \"\" | grep \"%s\" | cut -d':' -f2",
861            cdr_exe, g_cdrw_drive_is_here);
862    log_msg(1, "command=%s", command);
863    strcpy(tmp, call_program_and_get_last_line_of_output(command));
864    if (tmp[0]) {
865        strcpy(output, tmp);
866        log_msg(4, "Finally found it at %s", output);
867        retval = 0;
868        goto end_of_find_cdrom_device;
869    } else {
870        log_msg(4, "Still couldn't find it.");
871        retval = 1;
872        goto end_of_find_cdrom_device;
873    }
874  end_of_find_cdrom_device:
875    paranoid_free(tmp);
876    paranoid_free(cdr_exe);
877    paranoid_free(phrase_one);
878    paranoid_free(phrase_two);
879    paranoid_free(command);
880    paranoid_free(dvd_last_resort);
881    paranoid_free(mountpoint);
882    return (retval);
883}
884
885
886
887
888
889int find_dvd_device(char *output, bool try_to_mount)
890{
891    char *command;
892    char *tmp;
893    int retval = 0, devno = -1;
894
895    malloc_string(command);
896    malloc_string(tmp);
897
898    if (g_dvd_drive_is_here[0]) {
899        strcpy(output, g_dvd_drive_is_here);
900        log_msg(3, "Been there, done that. Returning %s", output);
901        return (0);
902    }
903
904    sprintf(tmp, call_program_and_get_last_line_of_output
905            ("dvdrecord -scanbus 2> /dev/null | grep \") '\" | grep -n \"\" | grep DVD | cut -d':' -f1")
906        );
907    log_msg(5, "tmp = '%s'", tmp);
908    if (!tmp[0])
909        sprintf(tmp, call_program_and_get_last_line_of_output
910                ("cdrecord -scanbus 2> /dev/null | grep \") '\" | grep -n \"\" | grep DVD | cut -d':' -f1")
911            );
912    if (tmp[0]) {
913        devno = atoi(tmp) - 1;
914    }
915    if (devno >= 0) {
916        retval = 0;
917        sprintf(output, "/dev/scd%d", devno);
918        strcpy(g_dvd_drive_is_here, output);
919        log_msg(2, "I think DVD is at %s", output);
920    } else {
921        log_msg(2, "I cannot find DVD");
922        retval = 1;
923    }
924
925    if (try_to_mount) {
926        log_msg(1, "Ignoring the fact that try_to_mount==TRUE");
927    }
928    return (retval);
929}
930
931
932
933
934
935#include <sys/ioctl.h>
936
937/**
938 * Find the size of the specified @p drive, in megabytes. Uses @c ioctl calls
939 * and @c dmesg.
940 * @param drive The device to find the size of.
941 * @return size in megabytes.
942 */
943long get_phys_size_of_drive(char *drive)
944{
945    int fd;
946#if linux
947    unsigned long long s = 0;
948    int fileid, cylinders = 0, cylindersleft = 0;
949    int cylindersize = 0;
950    int gotgeo = 0;
951
952
953    struct hd_geometry hdgeo;
954#elif __FreeBSD__
955    off_t s;
956#endif
957
958    long outvalA = -1;
959    long outvalB = -1;
960    long outvalC = -1;
961
962    if ((fd = open(drive, O_RDONLY)) != -1) {
963        if (ioctl(fd,
964#if linux
965#ifdef BLKGETSIZE64
966                  BLKGETSIZE64,
967#else
968                  BLKGETSIZE,
969#endif
970#elif __FreeBSD__
971                  DIOCGMEDIASIZE,
972#endif
973                  &s) != -1) {
974            close(fd);
975            // s>>11 works for older disks but not for newer ones
976            outvalB =
977#if linux
978#ifdef BLKGETSIZE64
979                s >> 20
980#else
981                s >> 11
982#endif
983#else
984                s >> 20
985#endif
986                ;
987        }
988    }
989
990    if (outvalB <= 0) {
991        log_msg(1, "Error getting size of %s: %s", drive, strerror(errno));
992#if linux
993        fileid = open(drive, O_RDONLY);
994        if (fileid) {
995            if (ioctl(fileid, HDIO_GETGEO, &hdgeo) != -1) {
996                if (hdgeo.cylinders && hdgeo.heads && hdgeo.sectors) {
997                    cylindersleft = cylinders = hdgeo.cylinders;
998                    cylindersize = hdgeo.heads * hdgeo.sectors / 2;
999                    outvalA = cylindersize * cylinders / 1024;
1000                    log_msg(2, "Got Harddisk geometry, C:%d, H:%d, S:%d",
1001                            hdgeo.cylinders, hdgeo.heads, hdgeo.sectors);
1002                    gotgeo = 1;
1003                } else {
1004                    log_msg(1, "Harddisk geometry wrong");
1005                }
1006            } else {
1007                log_msg(1,
1008                        "Error in ioctl() getting new hard disk geometry (%s), resizing in unsafe mode",
1009                        strerror(errno));
1010            }
1011            close(fileid);
1012        } else {
1013            log_msg(1, "Failed to open %s for reading: %s", drive,
1014                    strerror(errno));
1015        }
1016        if (!gotgeo) {
1017            log_msg(1, "Failed to get harddisk geometry, using old mode");
1018        }
1019#endif
1020    }
1021// OLDER DISKS will give ridiculously low value for outvalB (so outvalA is returned) :)
1022// NEWER DISKS will give sane value for outvalB (close to outvalA, in other words) :)
1023
1024    outvalC = (outvalA > outvalB) ? outvalA : outvalB;
1025
1026//  log_msg (5, "drive = %s, error = %s", drive, strerror (errno));
1027//  fatal_error ("GPSOD: Unable to get size of drive");
1028    log_msg(1, "%s --> %ld or %ld --> %ld", drive, outvalA, outvalB,
1029            outvalC);
1030
1031    return (outvalC);
1032}
1033
1034/**
1035 * Determine whether @p format is supported by the kernel. Uses /proc/filesystems
1036 * under Linux and @c lsvfs under FreeBSD.
1037 * @param format The format to test.
1038 * @return TRUE if the format is supported, FALSE if not.
1039 */
1040bool is_this_a_valid_disk_format(char *format)
1041{
1042    char *good_formats;
1043    char *command;
1044    char *format_sz;
1045
1046    FILE *pin;
1047    int retval;
1048    malloc_string(good_formats);
1049    malloc_string(command);
1050    malloc_string(format_sz);
1051
1052    assert_string_is_neither_NULL_nor_zerolength(format);
1053
1054    sprintf(format_sz, "%s ", format);
1055
1056#ifdef __FreeBSD__
1057    sprintf(command,
1058            "lsvfs | tr -s '\t' ' ' | grep -v Filesys | grep -v -- -- | cut -d' ' -f1 | tr -s '\n' ' '");
1059#else
1060    sprintf(command,
1061            "grep -v nodev /proc/filesystems | tr -s '\t' ' ' | cut -d' ' -f2 | tr -s '\n' ' '");
1062#endif
1063
1064    pin = popen(command, "r");
1065    if (!pin) {
1066        log_OS_error("Unable to read good formats");
1067        retval = 0;
1068    } else {
1069        strcpy(good_formats, " ");
1070        (void) fgets(good_formats + 1, MAX_STR_LEN - 1, pin);
1071        if (pclose(pin)) {
1072            log_OS_error("Cannot pclose good formats");
1073        }
1074        strip_spaces(good_formats);
1075        strcat(good_formats, " swap lvm raid ntfs-3g ntfs 7 "); // " ntfs 7 " -- um, cheating much? :)
1076        if (strstr(good_formats, format_sz)) {
1077            retval = 1;
1078        } else {
1079            retval = 0;
1080        }
1081    }
1082    paranoid_free(good_formats);
1083    paranoid_free(command);
1084    paranoid_free(format_sz);
1085    return (retval);
1086}
1087
1088
1089/** @def SWAPLIST_COMMAND The command to list the swap files/partitions in use. */
1090
1091/**
1092 * Determine whether @p device_raw is currently mounted.
1093 * @param device_raw The device to check.
1094 * @return TRUE if it's mounted, FALSE if not.
1095 */
1096bool is_this_device_mounted(char *device_raw)
1097{
1098
1099    /*@ pointers **************************************************** */
1100    FILE *fin;
1101
1102    /*@ buffers ***************************************************** */
1103    char *incoming;
1104    char *device_with_tab;
1105    char *device_with_space;
1106    char *tmp;
1107    int retval = 0;
1108
1109#ifdef __FreeBSD__
1110#define SWAPLIST_COMMAND "swapinfo"
1111#else
1112#define SWAPLIST_COMMAND "cat /proc/swaps"
1113#endif
1114
1115    /*@ end vars **************************************************** */
1116
1117    malloc_string(incoming);
1118    malloc_string(device_with_tab);
1119    malloc_string(device_with_space);
1120    malloc_string(tmp);
1121    assert(device_raw != NULL);
1122//  assert_string_is_neither_NULL_nor_zerolength(device_raw);
1123    if (device_raw[0] != '/' && !strstr(device_raw, ":/")) {
1124        log_msg(1, "%s needs to have a '/' prefixed - I'll do it",
1125                device_raw);
1126        sprintf(tmp, "/%s", device_raw);
1127    } else {
1128        strcpy(tmp, device_raw);
1129    }
1130    log_msg(1, "Is %s mounted?", tmp);
1131    if (!strcmp(tmp, "/proc") || !strcmp(tmp, "proc")) {
1132        log_msg(1,
1133                "I don't know how the heck /proc made it into the mountlist. I'll ignore it.");
1134        return (0);
1135    }
1136    sprintf(device_with_tab, "%s\t", tmp);
1137    sprintf(device_with_space, "%s ", tmp);
1138
1139    if (!(fin = popen("mount", "r"))) {
1140        log_OS_error("Cannot popen 'mount'");
1141        return (FALSE);
1142    }
1143    for (fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin);
1144         fgets(incoming, MAX_STR_LEN - 1, fin)) {
1145        if (strstr(incoming, device_with_space) //> incoming
1146            || strstr(incoming, device_with_tab))   // > incoming)
1147        {
1148            paranoid_pclose(fin);
1149            retval = 1;
1150            goto end_of_func;
1151        }
1152    }
1153    paranoid_pclose(fin);
1154    sprintf(tmp, "%s | grep -E \"^%s\" > /dev/null 2> /dev/null",
1155            SWAPLIST_COMMAND, device_with_space);
1156    log_msg(4, "tmp (command) = '%s'", tmp);
1157    if (!system(tmp)) {
1158        retval = 1;
1159        goto end_of_func;
1160    }
1161  end_of_func:
1162    paranoid_free(incoming);
1163    paranoid_free(device_with_tab);
1164    paranoid_free(device_with_space);
1165    paranoid_free(tmp);
1166    return (retval);
1167}
1168
1169#ifdef __FreeBSD__
1170//                       CODE IS FREEBSD-SPECIFIC
1171/**
1172 * Create a loopback device for specified @p fname.
1173 * @param fname The file to associate with a device.
1174 * @return /dev entry for the device, or NULL if it couldn't be allocated.
1175 */
1176char *make_vn(char *fname)
1177{
1178    char *device = (char *) malloc(MAX_STR_LEN);
1179    char *mddevice = (char *) malloc(32);
1180    char command[MAX_STR_LEN];
1181    int vndev = 2;
1182    if (atoi
1183        (call_program_and_get_last_line_of_output
1184         ("/sbin/sysctl -n kern.osreldate")) < 500000) {
1185        do {
1186            sprintf(mddevice, "vn%ic", vndev++);
1187            sprintf(command, "vnconfig %s %s", mddevice, fname);
1188            if (vndev > 10) {
1189                return NULL;
1190            }
1191        }
1192        while (system(command));
1193    } else {
1194        sprintf(command, "mdconfig -a -t vnode -f %s", fname);
1195        mddevice = call_program_and_get_last_line_of_output(command);
1196        if (!strstr(mddevice, "md")) {
1197            return NULL;
1198        }
1199    }
1200    sprintf(device, "/dev/%s", mddevice);
1201    return device;
1202}
1203
1204
1205
1206//                       CODE IS FREEBSD-SPECIFIC
1207/**
1208 * Deallocate specified @p dname.
1209 * This should be called when you are done with the device created by make_vn(),
1210 * so the system does not run out of @c vn devices.
1211 * @param dname The device to deallocate.
1212 * @return 0 for success, nonzero for failure.
1213 */
1214int kick_vn(char *dname)
1215{
1216    char command[MAX_STR_LEN];
1217
1218    if (strncmp(dname, "/dev/", 5) == 0) {
1219        dname += 5;
1220    }
1221
1222    if (atoi
1223        (call_program_and_get_last_line_of_output
1224         ("/sbin/sysctl -n kern.osreldate")) < 500000) {
1225        sprintf(command, "vnconfig -d %s", dname);
1226        return system(command);
1227    } else {
1228        sprintf(command, "mdconfig -d -u %s", dname);
1229        return system(command);
1230    }
1231     /*NOTREACHED*/ return 255;
1232}
1233#endif
1234
1235
1236/**
1237 * Mount the CD-ROM at @p mountpoint.
1238 * @param device The device (or file if g_ISO_restore_mode) to mount.
1239 * @param mountpoint The place to mount it.
1240 * @return 0 for success, nonzero for failure.
1241 */
1242int mount_USB_here(char *device, char *mountpoint)
1243{
1244    /*@ buffer ****************************************************** */
1245    char *command;
1246    char *dev;
1247    int retval;
1248
1249    malloc_string(command);
1250    malloc_string(dev);
1251    assert_string_is_neither_NULL_nor_zerolength(device);
1252    assert_string_is_neither_NULL_nor_zerolength(mountpoint);
1253
1254    make_hole_for_dir(mountpoint);
1255    if (isdigit(device[0])) {
1256        return(1);
1257    } else {
1258        strcpy(dev, device);
1259    }
1260    log_msg(4, "(mount_USB_here --- device=%s, mountpoint=%s", device,
1261            mountpoint);
1262
1263#ifdef __FreeBSD__
1264    sprintf(command, "mount_vfat %s %s 2>> %s",
1265            device, mountpoint, MONDO_LOGFILE);
1266
1267#else
1268    sprintf(command, "mount %s -t vfat %s 2>> %s",
1269            device, mountpoint, MONDO_LOGFILE);
1270#endif
1271
1272    log_msg(4, command);
1273    retval = system(command);
1274    log_msg(1, "system(%s) returned %d", command, retval);
1275
1276    paranoid_free(command);
1277    paranoid_free(dev);
1278    return (retval);
1279}
1280
1281/**
1282 * Mount the CD-ROM at @p mountpoint.
1283 * @param device The device (or file if g_ISO_restore_mode) to mount.
1284 * @param mountpoint The place to mount it.
1285 * @return 0 for success, nonzero for failure.
1286 */
1287int mount_CDROM_here(char *device, char *mountpoint)
1288{
1289    /*@ buffer ****************************************************** */
1290    char *command;
1291    char *dev;
1292    char *options;
1293    int retval;
1294
1295    malloc_string(command);
1296    malloc_string(dev);
1297    malloc_string(options);
1298    assert_string_is_neither_NULL_nor_zerolength(device);
1299    assert_string_is_neither_NULL_nor_zerolength(mountpoint);
1300
1301    make_hole_for_dir(mountpoint);
1302    strcpy(options, "ro");
1303    if (isdigit(device[0])) {
1304        find_cdrom_device(device, FALSE);
1305    } else {
1306        strcpy(dev, device);
1307    }
1308    if (g_ISO_restore_mode) {
1309
1310#ifdef __FreeBSD__
1311        strcpy(dev, make_vn(device));
1312        if (!dev) {
1313            sprintf(command, "Unable to mount ISO (make_vn(%s) failed)",
1314                    device);
1315            fatal_error(command);
1316        }
1317        strcpy(device, dev);
1318#else
1319        strcat(options, ",loop");
1320#endif
1321
1322    }
1323    log_msg(4, "(mount_CDROM_here --- device=%s, mountpoint=%s", device,
1324            mountpoint);
1325    /*@ end vars *************************************************** */
1326
1327#ifdef __FreeBSD__
1328    sprintf(command, "mount_cd9660 -r %s %s 2>> %s",
1329            device, mountpoint, MONDO_LOGFILE);
1330
1331#else
1332    sprintf(command, "mount %s -o %s -t iso9660 %s 2>> %s",
1333            device, options, mountpoint, MONDO_LOGFILE);
1334#endif
1335
1336    log_msg(4, command);
1337    if (strncmp(device, "/dev/", 5) == 0) {
1338        retract_CD_tray_and_defeat_autorun();
1339    }
1340    retval = system(command);
1341    log_msg(1, "system(%s) returned %d", command, retval);
1342
1343    paranoid_free(command);
1344    paranoid_free(dev);
1345    paranoid_free(options);
1346    return (retval);
1347}
1348
1349
1350
1351
1352
1353
1354/**
1355 * Ask the user for CD number @p cd_number_i_want.
1356 * Sets g_current_media_number once the correct CD is inserted.
1357 * @param bkpinfo The backup information structure. Fields used:
1358 * - @c bkpinfo->backup_media_type
1359 * - @c bkpinfo->prefix
1360 * - @c bkpinfo->isodir
1361 * - @c bkpinfo->media_device
1362 * - @c bkpinfo->please_dont_eject_when_restoring
1363 * @param cd_number_i_want The CD number to ask for.
1364 */
1365void
1366insist_on_this_cd_number(int cd_number_i_want)
1367{
1368
1369    /*@ int ************************************************************* */
1370    int res = 0;
1371
1372
1373    /*@ buffers ********************************************************* */
1374    char *tmp;
1375    char *request;
1376
1377    assert(bkpinfo != NULL);
1378    assert(cd_number_i_want > 0);
1379
1380//  log_msg(3, "Insisting on CD number %d", cd_number_i_want);
1381
1382    if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
1383        log_msg(3,
1384                "No need to insist_on_this_cd_number when the backup type isn't CD-R(W) or NFS or ISO");
1385        return;
1386    }
1387    malloc_string(tmp);
1388    malloc_string(request);
1389    sprintf(tmp, "mkdir -p " MNT_CDROM);
1390    run_program_and_log_output(tmp, 5);
1391    if (g_ISO_restore_mode || bkpinfo->backup_media_type == iso
1392        || bkpinfo->backup_media_type == nfs) {
1393        log_msg(3, "Remounting CD");
1394        g_ISO_restore_mode = TRUE;
1395// FIXME --- I'm tempted to do something about this...
1396// Why unmount and remount again and again?
1397        if (is_this_device_mounted(MNT_CDROM)) {
1398            run_program_and_log_output("umount " MNT_CDROM, 5);
1399        }
1400        sprintf(tmp, "mkdir -p %s/isodir &> /dev/null", bkpinfo->tmpdir);
1401        system(tmp);
1402        sprintf(tmp, "%s/%s/%s-%d.iso", bkpinfo->isodir,
1403                bkpinfo->nfs_remote_dir, bkpinfo->prefix,
1404                cd_number_i_want);
1405        if (!does_file_exist(tmp)) {
1406            sprintf(tmp, "%s/isodir/%s/%s-%d.iso", bkpinfo->tmpdir,
1407                    bkpinfo->nfs_remote_dir, bkpinfo->prefix,
1408                    cd_number_i_want);
1409            if (does_file_exist(tmp)) {
1410                log_msg(1,
1411                        "FIXME - hacking bkpinfo->isodir from '%s' to %s/isodir",
1412                        bkpinfo->isodir, bkpinfo->tmpdir);
1413                sprintf(bkpinfo->isodir, "%s/isodir", bkpinfo->tmpdir);
1414            }
1415        }
1416        log_msg(3, "Mounting %s at %s", tmp, MNT_CDROM);
1417        if (mount_CDROM_here(tmp, MNT_CDROM)) {
1418            fatal_error("Mommy!");
1419        }
1420//    g_current_media_number = cd_number_i_want;
1421//    return;
1422    }
1423    if ((res = what_number_cd_is_this()) != cd_number_i_want) {
1424        log_msg(3, "Currently, we hold %d but we want %d", res,
1425                cd_number_i_want);
1426        sprintf(tmp, "Insisting on %s #%d",
1427                media_descriptor_string(bkpinfo->backup_media_type),
1428                cd_number_i_want);
1429        sprintf(request, "Please insert %s #%d and press Enter.",
1430                media_descriptor_string(bkpinfo->backup_media_type),
1431                cd_number_i_want);
1432        log_msg(3, tmp);
1433        while (what_number_cd_is_this() != cd_number_i_want) {
1434            paranoid_system("sync");
1435            if (is_this_device_mounted(MNT_CDROM)) {
1436                res =
1437                    run_program_and_log_output("umount " MNT_CDROM, FALSE);
1438            } else {
1439                res = 0;
1440            }
1441            if (res) {
1442                log_to_screen("WARNING - failed to unmount CD-ROM drive");
1443            }
1444            if (!bkpinfo->please_dont_eject) {
1445                res = eject_device(bkpinfo->media_device);
1446            } else {
1447                res = 0;
1448            }
1449            if (res) {
1450                log_to_screen("WARNING - failed to eject CD-ROM disk");
1451            }
1452            popup_and_OK(request);
1453            if (!bkpinfo->please_dont_eject) {
1454                inject_device(bkpinfo->media_device);
1455            }
1456            paranoid_system("sync");
1457        }
1458        log_msg(1, "Thankyou. Proceeding...");
1459        g_current_media_number = cd_number_i_want;
1460    }
1461    paranoid_free(tmp);
1462    paranoid_free(request);
1463}
1464
1465/* @} - end of deviceGroup */
1466
1467
1468
1469
1470
1471
1472/**
1473 * Ask user for details of backup/restore information.
1474 * Called when @c mondoarchive doesn't get any parameters.
1475 * @param bkpinfo The backup information structure to fill out with the user's data.
1476 * @param archiving_to_media TRUE if archiving, FALSE if restoring.
1477 * @return 0, always.
1478 * @bug No point of `int' return value.
1479 * @ingroup archiveGroup
1480 */
1481int interactively_obtain_media_parameters_from_user(bool archiving_to_media)
1482// archiving_to_media is TRUE if I'm being called by mondoarchive
1483// archiving_to_media is FALSE if I'm being called by mondorestore
1484{
1485    char *tmp;
1486    char *tmp1 = NULL;
1487    char *sz_size;
1488    char *command;
1489    char *comment;
1490    char *prompt;
1491    int i;
1492    FILE *fin;
1493
1494    malloc_string(tmp);
1495    malloc_string(sz_size);
1496    malloc_string(command);
1497    malloc_string(comment);
1498    malloc_string(prompt);
1499    assert(bkpinfo != NULL);
1500    sz_size[0] = '\0';
1501    bkpinfo->nonbootable_backup = FALSE;
1502
1503    // Tape, CD, NFS, ...?
1504    srandom(getpid());
1505    bkpinfo->backup_media_type =
1506        (g_restoring_live_from_cd) ? cdr :
1507        which_backup_media_type(bkpinfo->restore_data);
1508    if (bkpinfo->backup_media_type == none) {
1509        log_to_screen("User has chosen not to backup the PC");
1510        finish(1);
1511    }
1512    /* Why asking to remove the media with tape ?
1513    if (bkpinfo->backup_media_type == tape && bkpinfo->restore_data) {
1514        popup_and_OK("Please remove media from drive(s)");
1515    }
1516    */
1517    log_msg(3, "media type = %s",
1518            bkptype_to_string(bkpinfo->backup_media_type));
1519    if (archiving_to_media) {
1520        sensibly_set_tmpdir_and_scratchdir();
1521    }
1522    bkpinfo->cdrw_speed = (bkpinfo->backup_media_type == cdstream) ? 2 : 4;
1523    bkpinfo->compression_level =
1524        (bkpinfo->backup_media_type == cdstream) ? 1 : 5;
1525    bkpinfo->use_lzo =
1526        (bkpinfo->backup_media_type == cdstream) ? TRUE : FALSE;
1527    mvaddstr_and_log_it(2, 0, " ");
1528
1529    // Find device's /dev (or SCSI) entry
1530    switch (bkpinfo->backup_media_type) {
1531    case cdr:
1532    case cdrw:
1533    case dvd:
1534    case usb:
1535        /* Never try to eject a USB device */
1536        if (bkpinfo->backup_media_type == usb) {
1537            bkpinfo->please_dont_eject = TRUE;
1538        }
1539        if (archiving_to_media) {
1540            if ((bkpinfo->backup_media_type != dvd) && (bkpinfo->backup_media_type != usb)) {
1541                if (ask_me_yes_or_no
1542                    ("Is your computer a laptop, or does the CD writer incorporate BurnProof technology?"))
1543                {
1544                    bkpinfo->manual_cd_tray = TRUE;
1545                }
1546            }
1547            if ((bkpinfo->compression_level =
1548                 which_compression_level()) == -1) {
1549                log_to_screen("User has chosen not to backup the PC");
1550                finish(1);
1551            }
1552            sprintf(comment, "What speed is your %s (re)writer?",
1553                    media_descriptor_string(bkpinfo->backup_media_type));
1554            if (bkpinfo->backup_media_type == dvd) {
1555                find_dvd_device(bkpinfo->media_device, FALSE);
1556                strcpy(tmp, "1");
1557                sprintf(sz_size, "%d", DEFAULT_DVD_DISK_SIZE);  // 4.7 salesman's GB = 4.482 real GB = 4482 MB
1558                log_msg(1, "Setting to DVD defaults");
1559            } else {
1560                strcpy(bkpinfo->media_device, VANILLA_SCSI_CDROM);
1561                strcpy(tmp, "4");
1562                strcpy(sz_size, "650");
1563                log_msg(1, "Setting to CD defaults");
1564            }
1565            if ((bkpinfo->backup_media_type != dvd) && (bkpinfo->backup_media_type != usb)) {
1566                if (!popup_and_get_string("Speed", comment, tmp, 4)) {
1567                    log_to_screen("User has chosen not to backup the PC");
1568                    finish(1);
1569                }
1570            }
1571            bkpinfo->cdrw_speed = atoi(tmp);    // if DVD then this shouldn't ever be used anyway :)
1572            sprintf(comment,
1573                    "How much data (in Megabytes) will each %s store?",
1574                    media_descriptor_string(bkpinfo->backup_media_type));
1575            if (!popup_and_get_string("Size", comment, sz_size, 5)) {
1576                log_to_screen("User has chosen not to backup the PC");
1577                finish(1);
1578            }
1579            for (i = 0; i <= MAX_NOOF_MEDIA; i++) {
1580                bkpinfo->media_size[i] = atoi(sz_size);
1581            }
1582            if (bkpinfo->media_size[0] <= 0) {
1583                log_to_screen("User has chosen not to backup the PC");
1584                finish(1);
1585            }
1586        }
1587        /* No break because we continue even for usb */
1588    case cdstream:
1589        if ((bkpinfo->disaster_recovery) && (bkpinfo->backup_media_type != usb)) {
1590            strcpy(bkpinfo->media_device, "/dev/cdrom");
1591            log_msg(2, "CD-ROM device assumed to be at %s",
1592                    bkpinfo->media_device);
1593        } else if ((bkpinfo->restore_data && (bkpinfo->backup_media_type != usb))
1594                   || bkpinfo->backup_media_type == dvd) {
1595            if (!bkpinfo->media_device[0]) {
1596                strcpy(bkpinfo->media_device, "/dev/cdrom");
1597            }                   // just for the heck of it :)
1598            log_msg(1, "bkpinfo->media_device = %s",
1599                    bkpinfo->media_device);
1600            if (bkpinfo->backup_media_type == dvd
1601                || find_cdrom_device(bkpinfo->media_device, FALSE)) {
1602                log_msg(1, "bkpinfo->media_device = %s",
1603                        bkpinfo->media_device);
1604                sprintf(comment,
1605                        "Please specify your %s drive's /dev entry",
1606                        media_descriptor_string(bkpinfo->backup_media_type));
1607                if (!popup_and_get_string
1608                    ("Device?", comment, bkpinfo->media_device,
1609                     MAX_STR_LEN / 4)) {
1610                    log_to_screen("User has chosen not to backup the PC");
1611                    finish(1);
1612                }
1613            }
1614            log_msg(2, "%s device found at %s",
1615                    media_descriptor_string(bkpinfo->backup_media_type),
1616                    bkpinfo->media_device);
1617        } else {
1618            if ((find_cdrw_device(bkpinfo->media_device)) && (bkpinfo->backup_media_type != usb)) {
1619                bkpinfo->media_device[0] = '\0';
1620            }
1621            if (bkpinfo->media_device[0]) {
1622                if (bkpinfo->backup_media_type == usb) {
1623                    sprintf(tmp,
1624                        "I think your %s media corresponds to %s. Is this correct?",
1625                        media_descriptor_string(bkpinfo->backup_media_type),
1626                        bkpinfo->media_device);
1627                } else {
1628                    sprintf(tmp,
1629                        "I think I've found your %s burner at SCSI node %s. Is this correct? (Say no if you have an IDE burner and you are running a 2.6 kernel. You will then be prompted for further details.)",
1630                        media_descriptor_string(bkpinfo->backup_media_type),
1631                        bkpinfo->media_device);
1632                }
1633                if (!ask_me_yes_or_no(tmp)) {
1634                    bkpinfo->media_device[0] = '\0';
1635                }
1636            }
1637            if (!bkpinfo->media_device[0]) {
1638                if (bkpinfo->backup_media_type == usb) {
1639                    i = popup_and_get_string("/dev entry?",
1640                                         "What is the /dev entry of your USB Disk/Key, please?",
1641                                         bkpinfo->media_device,
1642                                         MAX_STR_LEN / 4);
1643                } else {
1644                    if (g_kernel_version < 2.6) {
1645                        i = popup_and_get_string("Device node?",
1646                                             "What is the SCSI node of your CD (re)writer, please?",
1647                                             bkpinfo->media_device,
1648                                             MAX_STR_LEN / 4);
1649                    } else {
1650                        i = popup_and_get_string("/dev entry?",
1651                                             "What is the /dev entry of your CD (re)writer, please?",
1652                                             bkpinfo->media_device,
1653                                             MAX_STR_LEN / 4);
1654                    }
1655                }
1656                if (!i) {
1657                    log_to_screen("User has chosen not to backup the PC");
1658                    finish(1);
1659                }
1660            }
1661        }
1662        if (bkpinfo->backup_media_type == cdstream) {
1663            for (i = 0; i <= MAX_NOOF_MEDIA; i++) {
1664                bkpinfo->media_size[i] = 650;
1665            }
1666        }
1667        break;
1668    case udev:
1669        if (!ask_me_yes_or_no
1670            ("This option is for advanced users only. Are you sure?")) {
1671            log_to_screen("User has chosen not to backup the PC");
1672            finish(1);
1673        }
1674    case tape:
1675
1676        if ((!bkpinfo->restore_mode) && (find_tape_device_and_size(bkpinfo->media_device, sz_size))) {
1677            log_msg(3, "Ok, using vanilla scsi tape.");
1678            strcpy(bkpinfo->media_device, VANILLA_SCSI_TAPE);
1679            if ((fin = fopen(bkpinfo->media_device, "r"))) {
1680                paranoid_fclose(fin);
1681            } else {
1682                strcpy(bkpinfo->media_device, "/dev/osst0");
1683            }
1684        }
1685        if (bkpinfo->media_device[0]) {
1686            if ((fin = fopen(bkpinfo->media_device, "r"))) {
1687                paranoid_fclose(fin);
1688            } else {
1689                if (does_file_exist("/tmp/mondo-restore.cfg")) {
1690                    read_cfg_var("/tmp/mondo-restore.cfg", "media-dev",
1691                                 bkpinfo->media_device);
1692                }
1693            }
1694            sprintf(tmp,
1695                    "I think I've found your tape streamer at %s; am I right on the money?",
1696                    bkpinfo->media_device);
1697        }
1698        if (bkpinfo->media_device[0]) {
1699            sprintf(tmp,
1700                    "I think I've found your tape streamer at %s; am I right on the money?",
1701                    bkpinfo->media_device);
1702            if (!ask_me_yes_or_no(tmp)) {
1703                bkpinfo->media_device[0] = '\0';
1704            }
1705        }
1706        if (!bkpinfo->media_device[0]) {
1707            if (!popup_and_get_string
1708                ("Device name?",
1709                 "What is the /dev entry of your tape streamer?",
1710                 bkpinfo->media_device, MAX_STR_LEN / 4)) {
1711                log_to_screen("User has chosen not to backup the PC");
1712                finish(1);
1713            }
1714        }
1715        sprintf(tmp, "ls -l %s", bkpinfo->media_device);
1716        if (run_program_and_log_output(tmp, FALSE)) {
1717            log_to_screen("User has not specified a valid /dev entry");
1718            finish(1);
1719        }
1720        log_msg(4, "sz_size = %s", sz_size);
1721        sz_size[0] = '\0';
1722/*
1723    if ((size_sz[0]=='\0' || atol(size_sz)==0) && archiving_to_media)
1724      {
1725        if (!popup_and_get_string("Tape size", "How much COMPRESSED data will one of your tape cartridges hold? (e.g. 4GB for 4 gigabytes)", size_sz, 16))
1726          { log_to_screen("User has chosen not to backup the PC"); finish(1); }
1727      }
1728*/
1729        bkpinfo->use_obdr = ask_me_yes_or_no
1730            ("Do you want to activate OBDR support for your tapes ?");
1731        if (sz_size[0] == '\0') {
1732            bkpinfo->media_size[0] = 0;
1733        } else {
1734            bkpinfo->media_size[0] =
1735                friendly_sizestr_to_sizelong(sz_size) / 2 - 50;
1736        }
1737        log_msg(4, "media_size[0] = %ld", bkpinfo->media_size[0]);
1738        if (bkpinfo->media_size[0] <= 0) {
1739            bkpinfo->media_size[0] = 0;
1740        }
1741        for (i = 1; i <= MAX_NOOF_MEDIA; i++) {
1742            bkpinfo->media_size[i] = bkpinfo->media_size[0];
1743        }
1744        if (archiving_to_media) {
1745            if ((bkpinfo->compression_level =
1746                 which_compression_level()) == -1) {
1747                log_to_screen("User has chosen not to backup the PC");
1748                finish(1);
1749            }
1750        }
1751        break;
1752
1753
1754
1755    case nfs:
1756        /* Never try to eject a NFS device */
1757        bkpinfo->please_dont_eject = TRUE;
1758
1759        /* Initiate bkpinfo nfs_mount path from running environment if not already done */
1760        if (!bkpinfo->nfs_mount[0]) {
1761            strcpy(bkpinfo->nfs_mount,
1762                   call_program_and_get_last_line_of_output
1763                   ("mount | grep \":\" | cut -d' ' -f1 | head -n1"));
1764        }
1765#ifdef __FreeBSD__
1766        if (TRUE)
1767#else
1768        if (!bkpinfo->disaster_recovery)
1769#endif
1770        {
1771            if (!popup_and_get_string
1772                ("NFS dir.",
1773                 "Please enter path and directory where archives are stored remotely. (Mondo has taken a guess at the correct value. If it is incorrect, delete it and type the correct one.)",
1774                 bkpinfo->nfs_mount, MAX_STR_LEN / 4)) {
1775                log_to_screen("User has chosen not to backup the PC");
1776                finish(1);
1777            }
1778            if (!bkpinfo->restore_data) {
1779                if ((bkpinfo->compression_level =
1780                     which_compression_level()) == -1) {
1781                    log_to_screen("User has chosen not to backup the PC");
1782                    finish(1);
1783                }
1784            }
1785            // check whether already mounted - we better remove
1786            // surrounding spaces and trailing '/' for this
1787            strip_spaces(bkpinfo->nfs_mount);
1788            if (bkpinfo->nfs_mount[strlen(bkpinfo->nfs_mount) - 1] == '/')
1789                bkpinfo->nfs_mount[strlen(bkpinfo->nfs_mount) - 1] = '\0';
1790            sprintf(command, "mount | grep \"%s \" | cut -d' ' -f3",
1791                    bkpinfo->nfs_mount);
1792            strcpy(bkpinfo->isodir,
1793                   call_program_and_get_last_line_of_output(command));
1794
1795            if (!bkpinfo->restore_data) {
1796                sprintf(comment,
1797                    "How much data (in Megabytes) will each media store?");
1798                if (!popup_and_get_string("Size", comment, sz_size, 5)) {
1799                    log_to_screen("User has chosen not to backup the PC");
1800                    finish(1);
1801                }
1802            } else {
1803                strcpy(sz_size, "0");
1804            }
1805            for (i = 0; i <= MAX_NOOF_MEDIA; i++) {
1806                bkpinfo->media_size[i] = atoi(sz_size);
1807            }
1808            if (bkpinfo->media_size[0] < 0) {
1809                log_to_screen("User has chosen not to backup the PC");
1810                finish(1);
1811            }
1812        }
1813        if (bkpinfo->disaster_recovery) {
1814            sprintf(command ,"umount %s/isodir 2> /dev/null", bkpinfo->tmpdir);
1815            system(command);
1816            if (!popup_and_get_string
1817                ("NFS share", "Which remote NFS share should I mount?",
1818                 bkpinfo->nfs_mount, MAX_STR_LEN)) {
1819                log_to_screen("User has chosen not to backup the PC");
1820                finish(1);
1821            }
1822        }
1823        /* Initiate bkpinfo isodir path from running environment if mount already done */
1824        if (is_this_device_mounted(bkpinfo->nfs_mount)) {
1825            strcpy(bkpinfo->isodir,
1826                   call_program_and_get_last_line_of_output
1827                   ("mount | grep \":\" | cut -d' ' -f3 | head -n1"));
1828        } else {
1829            sprintf(bkpinfo->isodir, "%s/nfsdir", bkpinfo->tmpdir);
1830            sprintf(command, "mkdir -p %s", bkpinfo->isodir);
1831            run_program_and_log_output(command, 5);
1832            sprintf(tmp, "mount -t nfs -o nolock %s %s", bkpinfo->nfs_mount,
1833                    bkpinfo->isodir);
1834            run_program_and_log_output(tmp, 3);
1835            malloc_string(g_selfmounted_isodir);
1836            strcpy(g_selfmounted_isodir, bkpinfo->isodir);
1837        }
1838        if (!is_this_device_mounted(bkpinfo->nfs_mount)) {
1839            popup_and_OK
1840                ("Please mount that partition before you try to backup to or restore from it.");
1841            finish(1);
1842        }
1843        strcpy(tmp, bkpinfo->nfs_remote_dir);
1844        if (!popup_and_get_string
1845            ("Directory", "Which directory within that mountpoint?", tmp,
1846             MAX_STR_LEN)) {
1847            log_to_screen("User has chosen not to backup the PC");
1848            finish(1);
1849        }
1850        strcpy(bkpinfo->nfs_remote_dir, tmp);
1851        // check whether writable - we better remove surrounding spaces for this
1852        strip_spaces(bkpinfo->nfs_remote_dir);
1853
1854        if (!popup_and_get_string
1855            ("Prefix.",
1856             "Please enter the prefix that will be prepended to your ISO filename.  Example: machine1 to obtain machine1-[1-9]*.iso files",
1857            bkpinfo->prefix, MAX_STR_LEN / 4)) {
1858            log_to_screen("User has chosen not to backup the PC");
1859            finish(1);
1860        }
1861        log_msg(3, "prefix set to %s", bkpinfo->prefix);
1862
1863        for (i = 0; i <= MAX_NOOF_MEDIA; i++) {
1864            bkpinfo->media_size[i] = 650;
1865        }
1866        log_msg(3, "Just set nfs_remote_dir to %s",
1867                bkpinfo->nfs_remote_dir);
1868        log_msg(3, "isodir is still %s", bkpinfo->isodir);
1869        break;
1870
1871    case iso:
1872        if (!bkpinfo->disaster_recovery) {
1873            if (!popup_and_get_string
1874                ("Storage dir.",
1875                 "Please enter the full path name to the directory for your ISO images.  Example: /mnt/raid0_0",
1876                 bkpinfo->isodir, MAX_STR_LEN / 4)) {
1877                log_to_screen("User has chosen not to backup the PC");
1878                finish(1);
1879            }
1880            if (archiving_to_media) {
1881                if ((bkpinfo->compression_level =
1882                     which_compression_level()) == -1) {
1883                    log_to_screen("User has chosen not to backup the PC");
1884                    finish(1);
1885                }
1886                if (!popup_and_get_string
1887                    ("ISO size.",
1888                     "Please enter how big you want each ISO image to be (in megabytes). This should be less than or equal to the size of the CD-R[W]'s or DVD's you plan to backup to.",
1889                     sz_size, 16)) {
1890                    log_to_screen("User has chosen not to backup the PC");
1891                    finish(1);
1892                }
1893                for (i = 0; i <= MAX_NOOF_MEDIA; i++) {
1894                    bkpinfo->media_size[i] = atoi(sz_size);
1895                }
1896            } else {
1897                for (i = 0; i <= MAX_NOOF_MEDIA; i++) {
1898                    bkpinfo->media_size[i] = 650;
1899                }
1900            }
1901        }
1902        if (!popup_and_get_string
1903            ("Prefix.",
1904             "Please enter the prefix that will be prepended to your ISO filename.  Example: machine1 to obtain machine1-[1-9]*.iso files",
1905             bkpinfo->prefix, MAX_STR_LEN / 4)) {
1906            log_to_screen("User has chosen not to backup the PC");
1907            finish(1);
1908        }
1909        log_msg(3, "prefix set to %s", bkpinfo->prefix);
1910        break;
1911    default:
1912        fatal_error
1913            ("I, Mojo Jojo, shall defeat those pesky Powerpuff Girls!");
1914    }
1915
1916    if (archiving_to_media) {
1917
1918#ifdef __FreeBSD__
1919        strcpy(bkpinfo->boot_device,
1920               call_program_and_get_last_line_of_output
1921               ("mount | grep ' / ' | head -1 | cut -d' ' -f1 | sed 's/\\([0-9]\\).*/\\1/'"));
1922#else
1923        strcpy(bkpinfo->boot_device,
1924               call_program_and_get_last_line_of_output
1925               ("mount | grep ' / ' | head -1 | cut -d' ' -f1 | sed 's/[0-9].*//'"));
1926#endif
1927        i = which_boot_loader(bkpinfo->boot_device);
1928        if (i == 'U')           // unknown
1929        {
1930
1931#ifdef __FreeBSD__
1932            if (!popup_and_get_string
1933                ("Boot device",
1934                 "What is your boot device? (e.g. /dev/ad0)",
1935                 bkpinfo->boot_device, MAX_STR_LEN / 4)) {
1936                log_to_screen("User has chosen not to backup the PC");
1937                finish(1);
1938            }
1939            i = which_boot_loader(bkpinfo->boot_device);
1940#else
1941            if (!popup_and_get_string
1942                ("Boot device",
1943                 "What is your boot device? (e.g. /dev/hda)",
1944                 bkpinfo->boot_device, MAX_STR_LEN / 4)) {
1945                log_to_screen("User has chosen not to backup the PC");
1946                finish(1);
1947            }
1948            if (does_string_exist_in_boot_block
1949                (bkpinfo->boot_device, "LILO")) {
1950                i = 'L';
1951            } else
1952                if (does_string_exist_in_boot_block
1953                    (bkpinfo->boot_device, "ELILO")) {
1954                i = 'E';
1955            } else
1956                if (does_string_exist_in_boot_block
1957                    (bkpinfo->boot_device, "GRUB")) {
1958                i = 'G';
1959            } else {
1960                i = 'U';
1961            }
1962#endif
1963            if (i == 'U') {
1964                if (ask_me_yes_or_no
1965                    ("Unidentified boot loader. Shall I restore it byte-for-byte at restore time and hope for the best?"))
1966                {
1967                    i = 'R';    // raw
1968                } else {
1969                    log_to_screen
1970                        ("I cannot find your boot loader. Please run mondoarchive with parameters.");
1971                    finish(1);
1972                }
1973            }
1974        }
1975        bkpinfo->boot_loader = i;
1976        strcpy(bkpinfo->include_paths, "/");
1977        if (!popup_and_get_string
1978            ("Backup paths",
1979             "Please enter paths which you want me to backup. The default is '/' (i.e. everything).",
1980             bkpinfo->include_paths, MAX_STR_LEN)) {
1981            log_to_screen("User has chosen not to backup the PC");
1982            finish(1);
1983        }
1984        strcpy(tmp, list_of_NFS_mounts_only());
1985        if (strlen(tmp) > 2) {
1986            if (bkpinfo->exclude_paths[0]) {
1987                strcat(bkpinfo->exclude_paths, " ");
1988            }
1989            strncpy(bkpinfo->exclude_paths, tmp, MAX_STR_LEN);
1990        }
1991// NTFS
1992        strcpy(tmp,
1993               call_program_and_get_last_line_of_output
1994               ("parted2fdisk -l | grep -i ntfs | awk '{ print $1};' | tr -s '\\n' ' ' | awk '{ print $0};'"));
1995        if (strlen(tmp) > 2) {
1996            if (!popup_and_get_string
1997                ("NTFS partitions",
1998                 "Please enter/confirm the NTFS partitions you wish to backup as well.",
1999                 tmp, MAX_STR_LEN / 4)) {
2000                log_to_screen("User has chosen not to backup the PC");
2001                finish(1);
2002            }
2003            strncpy(bkpinfo->image_devs, tmp, MAX_STR_LEN / 4);
2004        }
2005
2006
2007        if (!popup_and_get_string
2008            ("Exclude paths",
2009             "Please enter paths which you do NOT want to backup. Separate them with spaces. NB: /tmp and /proc are always excluded. :-) Just hit 'Enter' if you want to do a full system backup.",
2010             bkpinfo->exclude_paths, (4*MAX_STR_LEN)-1)) {
2011            log_to_screen("User has chosen not to backup the PC");
2012            finish(1);
2013        }
2014// Interactive mode:
2015#ifdef __IA64__
2016        bkpinfo->make_cd_use_lilo = TRUE;
2017#else
2018        bkpinfo->make_cd_use_lilo = FALSE;
2019#endif
2020        bkpinfo->backup_data = TRUE;
2021        bkpinfo->verify_data =
2022            ask_me_yes_or_no
2023            ("Will you want to verify your backups after Mondo has created them?");
2024
2025#ifndef __FreeBSD__
2026        if (!ask_me_yes_or_no
2027            ("Are you confident that your kernel is a sane, sensible, standard Linux kernel? Say 'no' if you are using a Gentoo <1.4 or Debian <3.0, please."))
2028#endif
2029        {
2030            strcpy(bkpinfo->kernel_path, "FAILSAFE");
2031        }
2032
2033        if (!ask_me_yes_or_no
2034            ("Are you sure you want to proceed? Hit 'no' to abort.")) {
2035            log_to_screen("User has chosen not to backup the PC");
2036            finish(1);
2037        }
2038    } else {
2039        bkpinfo->restore_data = TRUE;   // probably...
2040    }
2041
2042    if (bkpinfo->backup_media_type == iso
2043        || bkpinfo->backup_media_type == nfs) {
2044        g_ISO_restore_mode = TRUE;
2045    }
2046#ifdef __FreeSD__
2047// skip
2048#else
2049    if (bkpinfo->backup_media_type == nfs) {
2050        sprintf(tmp, "mount | grep \"%s\" | cut -d' ' -f3",
2051                bkpinfo->nfs_mount);
2052//      strcpy(bkpinfo->isodir, call_program_and_get_last_line_of_output(tmp));
2053        log_msg(3, "I think the NFS mount is mounted at %s",
2054                bkpinfo->isodir);
2055    }
2056    log_it("isodir = %s", bkpinfo->isodir);
2057    log_it("nfs_mount = '%s'", bkpinfo->nfs_mount);
2058#endif
2059
2060    log_it("media device = %s", bkpinfo->media_device);
2061    log_it("media size = %ld", bkpinfo->media_size[1]);
2062    log_it("media type = %s",
2063           bkptype_to_string(bkpinfo->backup_media_type));
2064    log_it("prefix = %s", bkpinfo->prefix);
2065    log_it("compression = %ld", bkpinfo->compression_level);
2066    log_it("include_paths = '%s'", bkpinfo->include_paths);
2067    log_it("exclude_paths = '%s'", bkpinfo->exclude_paths);
2068    log_it("scratchdir = '%s'", bkpinfo->scratchdir);
2069    log_it("tmpdir = '%s'", bkpinfo->tmpdir);
2070    log_it("image_devs = '%s'", bkpinfo->image_devs);
2071    log_it("boot_device = '%s' (loader=%c)", bkpinfo->boot_device,
2072           bkpinfo->boot_loader);
2073    if (bkpinfo->media_size[0] < 0) {
2074        if (archiving_to_media) {
2075            fatal_error("Media size is less than zero.");
2076        } else {
2077            log_msg(2, "Warning - media size is less than zero.");
2078            bkpinfo->media_size[0] = 0;
2079        }
2080    }
2081    paranoid_free(tmp);
2082    paranoid_free(sz_size);
2083    paranoid_free(command);
2084    paranoid_free(comment);
2085    paranoid_free(prompt);
2086    return (0);
2087}
2088
2089
2090
2091
2092/**
2093 * @addtogroup utilityGroup
2094 * @{
2095 */
2096/**
2097 * Get a space-separated list of NFS devices and mounts.
2098 * @return The list created.
2099 * @note The return value points to static data that will be overwritten with each call.
2100 */
2101char *list_of_NFS_devices_and_mounts(void)
2102{
2103    char *exclude_these_devices;
2104    char *exclude_these_directories;
2105    static char result_sz[1024];
2106
2107    malloc_string(exclude_these_devices);
2108    malloc_string(exclude_these_directories);
2109    strcpy(exclude_these_directories,list_of_NFS_mounts_only());
2110    strcpy(exclude_these_devices,
2111           call_program_and_get_last_line_of_output
2112           ("tr -s '\t' ' ' < /etc/fstab | grep -E '( (coda|ncpfs|nfs|nfs4|smbfs|cifs|afs|gfs|ocfs|ocfs2|mvfs|nsspool|nsvol) )' | cut -d' ' -f1 | tr -s '\n' ' ' | awk '{print $0;}'"));
2113    sprintf(result_sz, "%s %s", exclude_these_directories,
2114            exclude_these_devices);
2115    paranoid_free(exclude_these_devices);
2116    paranoid_free(exclude_these_directories);
2117    return (result_sz);
2118}
2119
2120
2121
2122
2123/**
2124 * Get a space-separated list of NFS mounts.
2125 * @return The list created.
2126 * @note The return value points to static data that will be overwritten with each call.
2127 * @bug Even though we only want the mounts, the devices are still checked.
2128 */
2129char *list_of_NFS_mounts_only(void)
2130{
2131    char *exclude_these_directories;
2132    static char result_sz[512];
2133
2134    malloc_string(exclude_these_directories);
2135    strcpy(exclude_these_directories,
2136           call_program_and_get_last_line_of_output
2137           ("mount -t coda,ncpfs,nfs,nfs4,smbfs,cifs,afs,gfs,ocfs,ocfs2,mvfs,nsspool,nssvol | tr -s '\t' ' ' | cut -d' ' -f3 | tr -s '\n' ' ' | awk '{print $0;}'"));
2138    sprintf(result_sz, "%s", exclude_these_directories);
2139    paranoid_free(exclude_these_directories);
2140    return (result_sz);
2141}
2142
2143/* @} - end of utilityGroup */
2144
2145
2146
2147
2148
2149/**
2150 * Create a randomly-named FIFO. The format is @p stub "." [random] [random] where
2151 * [random] is a random number between 1 and 32767.
2152 * @param store_name_here Where to store the new filename.
2153 * @param stub A random number will be appended to this to make the FIFO's name.
2154 * @ingroup deviceGroup
2155 */
2156void make_fifo(char *store_name_here, char *stub)
2157{
2158    char *tmp;
2159
2160    malloc_string(tmp);
2161    assert_string_is_neither_NULL_nor_zerolength(stub);
2162
2163    sprintf(store_name_here, "%s%d%d", stub, (int) (random() % 32768),
2164            (int) (random() % 32768));
2165    make_hole_for_file(store_name_here);
2166    mkfifo(store_name_here, S_IRWXU | S_IRWXG);
2167    sprintf(tmp, "chmod 770 %s", store_name_here);
2168    paranoid_system(tmp);
2169    paranoid_free(tmp);
2170}
2171
2172
2173
2174
2175
2176
2177/**
2178 * Set the tmpdir and scratchdir to reside on the partition with the most free space.
2179 * Automatically excludes DOS, NTFS, SMB, and NFS filesystems.
2180 * @param bkpinfo The backup information structure. @c bkpinfo->tmpdir and @c bkpinfo->scratchdir will be set.
2181 * @ingroup utilityGroup
2182 */
2183void sensibly_set_tmpdir_and_scratchdir()
2184{
2185    char *tmp, *command, *sz;
2186
2187    malloc_string(tmp);
2188    malloc_string(command);
2189    malloc_string(sz);
2190    assert(bkpinfo != NULL);
2191
2192#ifdef __FreeBSD__
2193    strcpy(tmp,
2194           call_program_and_get_last_line_of_output
2195           ("LANGUAGE=C df -m -P -t nonfs,msdosfs,ntfs,ntfs-3g,smbfs,smb,cifs,afs,gfs,ocfs,ocfs2,mvfs,nsspool,nssvol | tr -s '\t' ' ' | grep -vE \"none|Filesystem\" | awk '{printf \"%s %s\\n\", $4, $6;}' | sort -n | tail -n1 | awk '{print $NF;}'"));
2196#else
2197    strcpy(tmp,
2198           call_program_and_get_last_line_of_output
2199           ("LANGUAGE=C df -m -P -x nfs -x nfs4 -x vfat -x ntfs -x ntfs-3g -x smbfs -x smb -x cifs -x afs -x gfs -x ocfs -x ocfs2 -x mvfs -x nsspool -x nssvol -x iso9660| sed 's/                  /devdev/' | tr -s '\t' ' ' | grep -vE \"none|Filesystem|/dev/shm\" | awk '{printf \"%s %s\\n\", $4, $6;}' | sort -n | tail -n1 | awk '{print $NF;}'"));
2200#endif
2201
2202    if (tmp[0] != '/') {
2203        strcpy(sz, tmp);
2204        strcpy(tmp, "/");
2205        strcat(tmp, sz);
2206    }
2207    if (!tmp[0]) {
2208        fatal_error("I couldn't figure out the tempdir!");
2209    }
2210    setup_tmpdir(tmp);
2211    log_it("bkpinfo->tmpdir is being set to %s", bkpinfo->tmpdir);
2212
2213    sprintf(bkpinfo->scratchdir, "%s/mondo.scratch.%d", tmp,
2214            (int) (random() % 32768));
2215    log_it("bkpinfo->scratchdir is being set to %s", bkpinfo->scratchdir);
2216
2217    sprintf(command, "rm -Rf %s/tmp.mondo.* %s/mondo.scratch.*", tmp, tmp);
2218    paranoid_system(command);
2219    paranoid_free(tmp);
2220    paranoid_free(command);
2221    paranoid_free(sz);
2222}
2223
2224
2225
2226
2227
2228
2229/**
2230 * @addtogroup deviceGroup
2231 * @{
2232 */
2233/**
2234 * If we can read @p dev, set @p output to it.
2235 * If @p dev cannot be read, set @p output to "".
2236 * @param dev The device to check for.
2237 * @param output Set to @p dev if @p dev exists, "" otherwise.
2238 * @return TRUE if @p dev exists, FALSE if it doesn't.
2239 */
2240bool set_dev_to_this_if_rx_OK(char *output, char *dev)
2241{
2242    char *command;
2243
2244    malloc_string(command);
2245    if (!dev || dev[0] == '\0') {
2246        output[0] = '\0';
2247        return (FALSE);
2248    }
2249//  assert_string_is_neither_NULL_nor_zerolength(dev);
2250    log_msg(10, "Injecting %s", dev);
2251    inject_device(dev);
2252    if (!does_file_exist(dev)) {
2253        log_msg(10, "%s doesn't exist. Returning FALSE.", dev);
2254        return (FALSE);
2255    }
2256    sprintf(command, "dd bs=%ld count=1 if=%s of=/dev/null &> /dev/null",
2257            512L, dev);
2258    if (!run_program_and_log_output(command, FALSE)
2259        && !run_program_and_log_output(command, FALSE)) {
2260        strcpy(output, dev);
2261        log_msg(4, "Found it - %s", dev);
2262        return (TRUE);
2263    } else {
2264        output[0] = '\0';
2265        log_msg(4, "It's not %s", dev);
2266        return (FALSE);
2267    }
2268}
2269
2270
2271
2272
2273
2274/**
2275 * Find out what number CD is in the drive.
2276 * @param bkpinfo The backup information structure. The @c bkpinfo->media_device field is the only one used.
2277 * @return The current CD number, or -1 if it could not be found.
2278 * @note If the CD is not mounted, it will be mounted
2279 * (and remain mounted after this function returns).
2280 */
2281int what_number_cd_is_this()
2282{
2283    int cd_number = -1;
2284    char *mountdev;
2285    char *tmp;
2286
2287    malloc_string(mountdev);
2288    malloc_string(tmp);
2289    assert(bkpinfo != NULL);
2290//  log_it("Asking what_number_cd_is_this");
2291    if (g_ISO_restore_mode) {
2292        sprintf(tmp, "mount | grep iso9660 | awk '{print $3;}'");
2293//      log_it("tmp = %s", tmp);
2294
2295        strcpy(mountdev, call_program_and_get_last_line_of_output(tmp));
2296        strcat(mountdev, "/archives/THIS-CD-NUMBER");
2297//      log_it("mountdev = %s", mountdev);
2298        cd_number = atoi(last_line_of_file(mountdev));
2299//      log_it("cd_number = %d", cd_number);
2300        paranoid_free(mountdev);
2301        paranoid_free(tmp);
2302        return (cd_number);
2303    }
2304
2305    strcpy(mountdev, bkpinfo->media_device);
2306    if (!mountdev[0]) {
2307        log_it
2308            ("(what_number_cd_is_this) Warning - media_device unknown. Finding out...");
2309        find_cdrom_device(bkpinfo->media_device, FALSE);
2310    }
2311    if (!is_this_device_mounted(MNT_CDROM)) {
2312        if (bkpinfo->backup_media_type == usb) {
2313            mount_USB_here(mountdev, MNT_CDROM);
2314        } else {
2315            mount_CDROM_here(mountdev, MNT_CDROM);
2316        }
2317    }
2318    cd_number =
2319        atoi(last_line_of_file(MNT_CDROM "/archives/THIS-CD-NUMBER"));
2320//  log_it("cd_number..later.. = %d", cd_number);
2321    paranoid_free(mountdev);
2322    paranoid_free(tmp);
2323    return (cd_number);
2324}
2325
2326
2327
2328
2329
2330
2331
2332/**
2333 * Find out what device is mounted as root (/).
2334 * @return Root device.
2335 * @note The returned string points to static storage and will be overwritten with every call.
2336 * @bug A bit of a misnomer; it's actually finding out the root device.
2337 * The mountpoint (where it's mounted) will obviously be '/'.
2338 */
2339char *where_is_root_mounted()
2340{
2341    /*@ buffers **************** */
2342    static char tmp[MAX_STR_LEN];
2343
2344
2345#ifdef __FreeBSD__
2346    strcpy(tmp, call_program_and_get_last_line_of_output
2347           ("mount | grep \" on / \" | cut -d' ' -f1"));
2348#else
2349    strcpy(tmp, call_program_and_get_last_line_of_output
2350           ("mount | grep \" on / \" | cut -d' ' -f1 | sed s/[0-9]// | sed s/[0-9]//"));
2351    if (strstr(tmp, "/dev/cciss/")) {
2352        strcpy(tmp, call_program_and_get_last_line_of_output
2353               ("mount | grep \" on / \" | cut -d' ' -f1 | cut -dp -f1"));
2354    }
2355    if (strstr(tmp, "/dev/md")) {
2356        strcpy(tmp,
2357               call_program_and_get_last_line_of_output
2358               ("mount | grep \" on / \" | cut -d' ' -f1"));
2359    }
2360#endif
2361
2362    return (tmp);
2363}
2364
2365
2366/**
2367 * Find out which boot loader is in use.
2368 * @param which_device Device to look for the boot loader on.
2369 * @return 'L' for LILO, 'E'for ELILO, 'G' for GRUB, 'B' or 'D' for FreeBSD boot loaders, or 'U' for Unknown.
2370 * @note Under Linux, all drives are examined, not just @p which_device.
2371 */
2372#ifdef __FreeBSD__
2373char which_boot_loader(char *which_device)
2374{
2375    int count_lilos = 0;
2376    int count_grubs = 0;
2377    int count_boot0s = 0;
2378    int count_dangerouslydedicated = 0;
2379
2380    log_it("looking at drive %s's MBR", which_device);
2381    if (does_string_exist_in_boot_block(which_device, "GRUB")) {
2382        count_grubs++;
2383    }
2384    if (does_string_exist_in_boot_block(which_device, "LILO")) {
2385        count_lilos++;
2386    }
2387    if (does_string_exist_in_boot_block(which_device, "Drive")) {
2388        count_boot0s++;
2389    }
2390    if (does_string_exist_in_first_N_blocks
2391        (which_device, "FreeBSD/i386", 17)) {
2392        count_dangerouslydedicated++;
2393    }
2394    log_it("%d grubs and %d lilos and %d elilos and %d boot0s and %d DD\n",
2395           count_grubs, count_lilos, count_elilos, count_boot0s,
2396           count_dangerouslydedicated);
2397
2398    if (count_grubs && !count_lilos) {
2399        return ('G');
2400    } else if (count_lilos && !count_grubs) {
2401        return ('L');
2402    } else if (count_grubs == 1 && count_lilos == 1) {
2403        log_it("I'll bet you used to use LILO but switched to GRUB...");
2404        return ('G');
2405    } else if (count_boot0s == 1) {
2406        return ('B');
2407    } else if (count_dangerouslydedicated) {
2408        return ('D');
2409    } else {
2410        log_it("Unknown boot loader");
2411        return ('U');
2412    }
2413}
2414
2415#else
2416
2417char which_boot_loader(char *which_device)
2418{
2419    /*@ buffer ***************************************************** */
2420    char *list_drives_cmd;
2421    char *current_drive;
2422
2423    /*@ pointers *************************************************** */
2424    FILE *pdrives;
2425
2426    /*@ int ******************************************************** */
2427    int count_lilos = 0;
2428    int count_grubs = 0;
2429
2430    /*@ end vars *************************************************** */
2431
2432    malloc_string(list_drives_cmd);
2433    malloc_string(current_drive);
2434
2435#ifdef __IA64__
2436    /* No choice for it */
2437    return ('E');
2438#endif
2439    assert(which_device != NULL);
2440    //  sprintf (list_drives_cmd,
2441    //       "fdisk -l | grep /dev | grep cyl | tr ':' ' ' | cut -d' ' -f2");
2442
2443    sprintf(list_drives_cmd,
2444            "parted2fdisk -l 2>/dev/null | grep \"/dev/.*:\" | tr -s ':' ' ' | tr -s ' ' '\n' | grep /dev/; echo %s",
2445            where_is_root_mounted());
2446    log_it("list_drives_cmd = %s", list_drives_cmd);
2447
2448    if (!(pdrives = popen(list_drives_cmd, "r"))) {
2449        log_OS_error("Unable to open list of drives");
2450        paranoid_free(list_drives_cmd);
2451        paranoid_free(current_drive);
2452        return ('\0');
2453    }
2454    for (fgets(current_drive, MAX_STR_LEN, pdrives); !feof(pdrives);
2455         fgets(current_drive, MAX_STR_LEN, pdrives)) {
2456        strip_spaces(current_drive);
2457        log_it("looking at drive %s's MBR", current_drive);
2458        if (does_string_exist_in_boot_block(current_drive, "GRUB")) {
2459            count_grubs++;
2460            strcpy(which_device, current_drive);
2461            break;
2462        }
2463        if (does_string_exist_in_boot_block(current_drive, "LILO")) {
2464            count_lilos++;
2465            strcpy(which_device, current_drive);
2466            break;
2467        }
2468    }
2469    if (pclose(pdrives)) {
2470        log_OS_error("Cannot pclose pdrives");
2471    }
2472    log_it("%d grubs and %d lilos\n", count_grubs, count_lilos);
2473    if (count_grubs && !count_lilos) {
2474        return ('G');
2475    } else if (count_lilos && !count_grubs) {
2476        return ('L');
2477    } else if (count_grubs == 1 && count_lilos == 1) {
2478        log_it("I'll bet you used to use LILO but switched to GRUB...");
2479        return ('G');
2480    } else {
2481        // We need to look on each partition then
2482        sprintf(list_drives_cmd,
2483            "parted2fdisk -l 2>/dev/null | grep -E \"^/dev/\" | tr -s ':' ' ' | tr -s ' ' '\n' | grep /dev/");
2484        log_it("list_drives_cmd = %s", list_drives_cmd);
2485
2486        if (!(pdrives = popen(list_drives_cmd, "r"))) {
2487            log_OS_error("Unable to open list of drives");
2488            paranoid_free(list_drives_cmd);
2489            paranoid_free(current_drive);
2490            return ('\0');
2491        }
2492        for (fgets(current_drive, MAX_STR_LEN, pdrives); !feof(pdrives);
2493            fgets(current_drive, MAX_STR_LEN, pdrives)) {
2494            strip_spaces(current_drive);
2495            log_it("looking at partition %s's BR", current_drive);
2496            if (does_string_exist_in_boot_block(current_drive, "GRUB")) {
2497                count_grubs++;
2498                strcpy(which_device, current_drive);
2499                break;
2500            }
2501            if (does_string_exist_in_boot_block(current_drive, "LILO")) {
2502                count_lilos++;
2503                strcpy(which_device, current_drive);
2504                break;
2505            }
2506        }
2507        if (pclose(pdrives)) {
2508            log_OS_error("Cannot pclose pdrives");
2509        }
2510        log_it("%d grubs and %d lilos\n", count_grubs, count_lilos);
2511        paranoid_free(list_drives_cmd);
2512        paranoid_free(current_drive);
2513        if (count_grubs && !count_lilos) {
2514            return ('G');
2515        } else if (count_lilos && !count_grubs) {
2516            return ('L');
2517        } else if (count_grubs == 1 && count_lilos == 1) {
2518            log_it("I'll bet you used to use LILO but switched to GRUB...");
2519            return ('G');
2520        } else {
2521            log_it("Unknown boot loader");
2522            return ('U');
2523        }
2524    }
2525}
2526#endif
2527
2528
2529
2530
2531/**
2532 * Write zeroes over the first 16K of @p device.
2533 * @param device The device to zero.
2534 * @return 0 for success, 1 for failure.
2535 */
2536int zero_out_a_device(char *device)
2537{
2538    FILE *fout;
2539    int i;
2540
2541    assert_string_is_neither_NULL_nor_zerolength(device);
2542
2543    log_it("Zeroing drive %s", device);
2544    if (!(fout = fopen(device, "w"))) {
2545        log_OS_error("Unable to open/write to device");
2546        return (1);
2547    }
2548    for (i = 0; i < 16384; i++) {
2549        fputc('\0', fout);
2550    }
2551    paranoid_fclose(fout);
2552    log_it("Device successfully zeroed.");
2553    return (0);
2554}
2555
2556/**
2557 * Return the device pointed to by @p incoming.
2558 * @param incoming The device to resolve symlinks for.
2559 * @return The path to the real device file.
2560 * @note The returned string points to static storage that will be overwritten with each call.
2561 * @bug Won't work with file v4.0; needs to be written in C.
2562 */
2563char *resolve_softlinks_to_get_to_actual_device_file(char *incoming)
2564{
2565    static char output[MAX_STR_LEN];
2566    char *command;
2567    char *curr_fname;
2568    char *scratch;
2569    char *tmp;
2570    char *p;
2571
2572    struct stat statbuf;
2573    command = malloc(1000);
2574    malloc_string(tmp);
2575    malloc_string(scratch);
2576    malloc_string(curr_fname);
2577    if (!does_file_exist(incoming)) {
2578        log_it
2579            ("resolve_softlinks_to_get_to_actual_device_file --- device not found");
2580        strcpy(output, incoming);
2581    } else {
2582        strcpy(curr_fname, incoming);
2583        lstat(curr_fname, &statbuf);
2584        while (S_ISLNK(statbuf.st_mode)) {
2585            log_msg(1, "curr_fname = %s", curr_fname);
2586            sprintf(command, "file %s", curr_fname);
2587            strcpy(tmp, call_program_and_get_last_line_of_output(command));
2588            for (p = tmp + strlen(tmp); p != tmp && *p != '`' && *p != ' ';
2589                 p--);
2590            p++;
2591            strcpy(scratch, p);
2592            for (p = scratch; *p != '\0' && *p != '\''; p++);
2593            *p = '\0';
2594            log_msg(0, "curr_fname %s --> '%s' --> %s", curr_fname, tmp,
2595                    scratch);
2596            if (scratch[0] == '/') {
2597                strcpy(curr_fname, scratch);    // copy whole thing because it's an absolute softlink
2598            } else {            // copy over the basename cos it's a relative softlink
2599                p = curr_fname + strlen(curr_fname);
2600                while (p != curr_fname && *p != '/') {
2601                    p--;
2602                }
2603                if (*p == '/') {
2604                    p++;
2605                }
2606                strcpy(p, scratch);
2607            }
2608            lstat(curr_fname, &statbuf);
2609        }
2610        strcpy(output, curr_fname);
2611        log_it("resolved %s to %s", incoming, output);
2612    }
2613    paranoid_free(command);
2614    paranoid_free(curr_fname);
2615    paranoid_free(tmp);
2616    return (output);
2617}
2618
2619/* @} - end of deviceGroup */
2620
2621
2622/**
2623 * Return the type of partition format (GPT or MBR)
2624 */
2625char *which_partition_format(const char *drive)
2626{
2627    static char output[4];
2628    char *tmp;
2629    char *command;
2630    char *fdisk;
2631#ifdef __IA64__
2632    struct stat buf;
2633#endif
2634    malloc_string(tmp);
2635    malloc_string(command);
2636    malloc_string(fdisk);
2637    log_msg(0, "Looking for partition table format type");
2638    sprintf(fdisk, "/sbin/parted2fdisk");
2639    log_msg(1, "Using %s", fdisk);
2640    sprintf(command, "%s -l %s | grep 'EFI GPT'", fdisk, drive);
2641    strcpy(tmp, call_program_and_get_last_line_of_output(command));
2642    if (strstr(tmp, "GPT") == NULL) {
2643        strcpy(output, "MBR");
2644    } else {
2645        strcpy(output, "GPT");
2646    }
2647    log_msg(0, "Found %s partition table format type", output);
2648    paranoid_free(command);
2649    paranoid_free(tmp);
2650    paranoid_free(fdisk);
2651    return (output);
2652}
2653
2654/* @} - end of deviceGroup */
Note: See TracBrowser for help on using the repository browser.