/* libmondo-devices.c Subroutines for handling devices $Id: libmondo-devices.c 1581 2007-07-27 00:11:55Z bruno $ */ /** * @file * Functions to handle interactions with backup devices. */ #include "my-stuff.h" #include "mondostructures.h" #include "libmondo-files-EXT.h" #include "libmondo-devices.h" #include "libmondo-string-EXT.h" #include "libmondo-tools-EXT.h" #include "newt-specific-EXT.h" #include "libmondo-fork-EXT.h" #include "libmondo-stream-EXT.h" #include "mr_mem.h" #include "mr_msg.h" #include "mr_str.h" #include "mr_gettext.h" #include #include #include #ifdef __FreeBSD__ #define DKTYPENAMES #define FSTYPENAMES #include #include #elif linux #define u64 unsigned long long #include /* for BLKGETSIZE64 */ #include #endif /*@unused@*/ //static char cvsid[] = "$Id: libmondo-devices.c 1581 2007-07-27 00:11:55Z bruno $"; extern int g_current_media_number; extern double g_kernel_version; extern bool g_ISO_restore_mode; extern char *g_erase_tmpdir_and_scratchdir; extern char *g_selfmounted_isodir; extern char *MONDO_LOGFILE; static char g_cdrw_drive_is_here[MAX_STR_LEN / 4] = ""; static char g_cdrom_drive_is_here[MAX_STR_LEN / 4] = ""; static char g_dvd_drive_is_here[MAX_STR_LEN / 4] = ""; /** * ????? @bug ????? * @ingroup globalGroup */ bool g_restoring_live_from_cd = FALSE; extern t_bkptype g_backup_media_type; // set by main() void set_g_cdrom_and_g_dvd_to_bkpinfo_value(struct s_bkpinfo *bkpinfo) { strcpy(g_cdrom_drive_is_here, bkpinfo->media_device); // just in case strcpy(g_dvd_drive_is_here, bkpinfo->media_device); // just in case } /** * Retract all CD trays and wait for autorun to complete. * @ingroup deviceGroup */ void retract_CD_tray_and_defeat_autorun(void) { // log_it("rctada: Retracting all CD trays", __LINE__); if (strlen(g_cdrom_drive_is_here) > 0) { inject_device(g_cdrom_drive_is_here); } if (strlen(g_dvd_drive_is_here) > 0) { inject_device(g_dvd_drive_is_here); } if (strlen(g_cdrw_drive_is_here) > 0) { inject_device(g_cdrw_drive_is_here); } // log_it("rctada: killing autorun"); // run_program_and_log_output("killall autorun", TRUE); if (!run_program_and_log_output("ps | grep autorun | grep -v grep", 5)) { log_it("autorun detected; sleeping for 2 seconds"); sleep(2); } log_it("rctada: Unmounting all CD drives", __LINE__); run_program_and_log_output("umount /dev/cdr* /dev/dvd*", 5); } /** * Mount the CD-ROM at @p mountpoint. * @param device The device (or file if g_ISO_restore_mode) to mount. * @param mountpoint The place to mount it. * @return 0 for success, nonzero for failure. */ int mount_CDROM_here(char *device, char *mountpoint) { /*@ buffer ****************************************************** */ char *command = NULL; char *dev; int retval = 0; malloc_string(dev); assert_string_is_neither_NULL_nor_zerolength(device); assert_string_is_neither_NULL_nor_zerolength(mountpoint); make_hole_for_dir(mountpoint); if (isdigit(device[0])) { find_cdrom_device(device, FALSE); } else { strcpy(dev, device); } if (g_ISO_restore_mode) { #ifdef __FreeBSD__ strcpy(dev, make_vn(device)); if (!dev) { sprintf(command, "Unable to mount ISO (make_vn(%s) failed)", device); fatal_error(command); } strcpy(device, dev); #endif } mr_msg(4, "(mount_CDROM_here --- device=%s, mountpoint=%s", device, mountpoint); /*@ end vars *************************************************** */ #ifdef __FreeBSD__ mr_asprintf(&command, "mount_cd9660 -r %s %s 2>> %s", device, mountpoint, MONDO_LOGFILE); #else mr_asprintf(&command, "mount %s -o ro,loop -t iso9660 %s 2>> %s", device, mountpoint, MONDO_LOGFILE); #endif mr_msg(4, command); if (strncmp(device, "/dev/", 5) == 0) { retract_CD_tray_and_defeat_autorun(); } retval = system(command); mr_msg(1, "system(%s) returned %d", command, retval); mr_free(command); mr_free(dev); return (retval); } /** * Determine whether we're booted off a ramdisk. * @return @c TRUE (we are) or @c FALSE (we aren't). * @ingroup utilityGroup */ bool am_I_in_disaster_recovery_mode(void) { char *tmp = NULL; char *comment = NULL; bool is_this_a_ramdisk = FALSE; mr_asprintf(&tmp, where_is_root_mounted()); mr_asprintf(&comment, "root is mounted at %s\n", tmp); mr_msg(0, comment); mr_free(comment); mr_msg(0, "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().", tmp); #ifdef __FreeBSD__ if (strstr(tmp, "/dev/md")) { is_this_a_ramdisk = TRUE; } #else if (!strncmp(tmp, "/dev/ram", 8) || (!strncmp(tmp, "/dev/rd", 7) && !strcmp(tmp, "/dev/rd/") && strncmp(tmp, "/dev/rd/cd", 10)) || strstr(tmp, "rootfs") || !strcmp(tmp, "/dev/root")) { is_this_a_ramdisk = TRUE; } else { is_this_a_ramdisk = FALSE; } #endif mr_free(tmp); if (is_this_a_ramdisk) { if (!does_file_exist("/THIS-IS-A-RAMDISK") && !does_file_exist("/tmp/mountlist.txt.sample")) { log_to_screen (_("Using /dev/root is stupid of you but I'll forgive you.")); is_this_a_ramdisk = FALSE; } } if (does_file_exist("/THIS-IS-A-RAMDISK")) { is_this_a_ramdisk = TRUE; } mr_msg(1, "Is this a ramdisk? result = %d", is_this_a_ramdisk); return (is_this_a_ramdisk); } /** * Turn @c bkpinfo->backup_media_type into a human-readable string. * @return The human readable string (e.g. @c cdr becomes "cdr"). * @note The returned string points to static storage that will be overwritten with each call. * @ingroup stringGroup */ char *bkptype_to_string(t_bkptype bt) { static char output[MAX_STR_LEN / 4]; switch (bt) { case none: strcpy(output, "none"); break; case iso: strcpy(output, "iso"); break; case cdr: strcpy(output, "cdr"); break; case cdrw: strcpy(output, "cdrw"); break; case cdstream: strcpy(output, "cdstream"); break; case nfs: strcpy(output, "nfs"); break; case tape: strcpy(output, "tape"); break; case udev: strcpy(output, "udev"); break; case usb: strcpy(output, "usb"); break; default: strcpy(output, "default"); } return (output); } /** * @addtogroup deviceGroup * @{ */ /** * Eject the tray of the specified CD device. * @param dev The device to eject. * @return the return value of the @c eject command. (0=success, nonzero=failure) */ int eject_device(char *dev) { char *command = NULL; int res1 = 0, res2 = 0; if (IS_THIS_A_STREAMING_BACKUP(g_backup_media_type) && g_backup_media_type != udev) { mr_asprintf(&command, "mt -f %s offline", dev); res1 = run_program_and_log_output(command, 1); mr_free(command); } else { res1 = 0; } #ifdef __FreeBSD__ if (strstr(dev, "acd")) { mr_asprintf(&command, "cdcontrol -f %s eject", dev); } else { mr_asprintf(&command, "camcontrol eject `echo %s | sed 's|/dev/||'`", dev); } #else mr_asprintf(&command, "eject %s", dev); #endif mr_msg(3, "Ejecting %s", dev); res2 = run_program_and_log_output(command, 1); mr_free(command); if (res1 && res2) { return (1); } else { return (0); } } /** * Load (inject) the tray of the specified CD device. * @param dev The device to load/inject. * @return 0 for success, nonzero for failure. */ int inject_device(char *dev) { char *command = NULL; int i; #ifdef __FreeBSD__ if (strstr(dev, "acd")) { mr_asprintf(&command, "cdcontrol -f %s close", dev); } else { mr_asprintf(&command, "camcontrol load `echo %s | sed 's|/dev/||'`", dev); } #else mr_asprintf(&command, "eject -t %s", dev); #endif i = run_program_and_log_output(command, FALSE); mr_free(command); return (i); } /** * Determine whether the specified @p device (really, you can use any file) * exists. * @return TRUE if it exists, FALSE if it doesn't. */ bool does_device_exist(char *device) { /*@ buffers *********************************************************** */ char *tmp = NULL; bool ret = FALSE; assert_string_is_neither_NULL_nor_zerolength(device); mr_asprintf(&tmp, "ls %s > /dev/null 2> /dev/null", device); if (system(tmp)) { ret = FALSE; } else { ret = TRUE; } mr_free(tmp); return(ret); } /** * Determine whether a non-Microsoft partition exists on any connected hard drive. * @return TRUE (there's a Linux/FreeBSD partition) or FALSE (Microsoft has taken over yet another innocent PC). */ bool does_nonMS_partition_exist(void) { #if __FreeBSD__ return !system ("for drive in /dev/ad? /dev/da?; do fdisk $drive | grep -q FreeBSD && exit 0; done; false"); #else return !system ("parted2fdisk -l 2>/dev/null | grep '^/dev/' | grep -Eqv '(MS|DOS|FAT|NTFS)'"); #endif } /** * Determine whether the specified @p partno exists on the specified @p drive. * @param drive The drive to search for the partition in. * @param partno The partition number to look for. * @return 0 if it exists, nonzero otherwise. */ int does_partition_exist(const char *drive, int partno) { /*@ buffers **************************************************** */ char *program = NULL; char *incoming = NULL; char *searchstr = NULL; /*@ ints ******************************************************* */ int res = 0; /*@ pointers *************************************************** */ FILE *fin; /*@ end vars *************************************************** */ assert_string_is_neither_NULL_nor_zerolength(drive); assert(partno >= 0 && partno < 999); malloc_string(incoming); malloc_string(searchstr); #ifdef __FreeBSD__ // We assume here that this is running from mondorestore. (It is.) mr_asprintf(&program, "ls %s %s >/dev/null 2>&1", drive, build_partition_name(tmp, drive, partno)); res = system(program); mr_free(program); return(res); #endif mr_asprintf(&program, "parted2fdisk -l %s 2> /dev/null", drive); fin = popen(program, "r"); if (!fin) { log_it("program=%s", program); log_OS_error("Cannot popen-in program"); mr_free(program); return (0); } mr_free(program); (void) build_partition_name(searchstr, drive, partno); strcat(searchstr, " "); for (res = 0; !res && fgets(incoming, MAX_STR_LEN - 1, fin);) { if (strstr(incoming, searchstr)) { res = 1; } } mr_free(incoming); if (pclose(fin)) { log_OS_error("Cannot pclose fin"); } mr_free(searchstr); return (res); } /** * Determine whether given NULL-terminated @p str exists in the MBR of @p dev. * @param dev The device to look in. * @param str The string to look for. * @return TRUE if it exists, FALSE if it doesn't. */ bool does_string_exist_in_boot_block(char *dev, char *str) { /*@ buffers **************************************************** */ char *command = NULL; /*@ end vars *************************************************** */ int ret; assert_string_is_neither_NULL_nor_zerolength(dev); assert_string_is_neither_NULL_nor_zerolength(str); mr_asprintf(&command, "dd if=%s bs=446 count=1 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null", dev, str); ret = system(command); mr_free(command); if (ret) { return (FALSE); } else { return (TRUE); } } /** * Determine whether specified @p str exists in the first @p n sectors of * @p dev. * @param dev The device to look in. * @param str The string to look for. * @param n The number of 512-byte sectors to search. */ bool does_string_exist_in_first_N_blocks(char *dev, char *str, int n) { /*@ buffers **************************************************** */ char *command = NULL; /*@ end vars *************************************************** */ int ret; mr_asprintf(&command, "dd if=%s bs=512 count=%i 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null", dev, n, str); ret = system(command); mr_free(command); if (ret) { return (FALSE); } else { return (TRUE); } } /** * Try to mount CD-ROM at @p mountpoint. If the CD-ROM is not found or has * not been specified, call find_cdrom_device() to find it. * @param bkpinfo The backup information structure. The only field used is @c bkpinfo->media_device. * @param mountpoint Where to mount the CD-ROM. * @return 0 for success, nonzero for failure. * @see mount_CDROM_here */ int find_and_mount_actual_cd(struct s_bkpinfo *bkpinfo, char *mountpoint) { /*@ buffers ***************************************************** */ /*@ int's ****************************************************** */ int res; char *dev = NULL; /*@ end vars **************************************************** */ malloc_string(dev); assert(bkpinfo != NULL); assert_string_is_neither_NULL_nor_zerolength(mountpoint); if (g_backup_media_type == dvd) { strcpy(dev, g_dvd_drive_is_here); if (!dev[0]) { find_dvd_device(dev, FALSE); } } else { strcpy(dev, g_cdrom_drive_is_here); if (!dev[0]) { find_cdrom_device(dev, FALSE); } } if (bkpinfo->backup_media_type != iso) { retract_CD_tray_and_defeat_autorun(); } if (!dev[0] || (res = mount_CDROM_here(dev, mountpoint))) { if (!popup_and_get_string ("CD-ROM device", "Please enter your CD-ROM's /dev device", dev, MAX_STR_LEN / 4)) { res = 1; } else { res = mount_CDROM_here(dev, mountpoint); } } if (res) { mr_msg(1, _("mount failed")); } else { mr_msg(1, _("mount succeeded with %s"), dev); } mr_free(dev); return(res); } /** * Locate a CD-R/W writer's SCSI node. * @param cdrw_device SCSI node will be placed here. * @return 0 for success, nonzero for failure. */ /** * Locate a CD-R/W writer's SCSI node. * @param cdrw_device SCSI node will be placed here. * @return 0 for success, nonzero for failure. */ int find_cdrw_device(char *cdrw_device) { /*@ buffers ************************ */ char *comment; char *tmp; char *cdr_exe; char *command; malloc_string(comment); malloc_string(tmp); malloc_string(cdr_exe); malloc_string(command); if (g_cdrw_drive_is_here[0]) { strcpy(cdrw_device, g_cdrw_drive_is_here); mr_msg(3, "Been there, done that. Returning %s", cdrw_device); mr_free(comment); mr_free(tmp); mr_free(cdr_exe); mr_free(command); return (0); } if (g_backup_media_type == dvd) { mr_msg(1, "This is dumb. You're calling find_cdrw_device() but you're backing up to DVD. WTF?"); mr_free(comment); mr_free(tmp); mr_free(cdr_exe); mr_free(command); return (1); } run_program_and_log_output("insmod ide-scsi", -1); if (find_home_of_exe("cdrecord")) { strcpy(cdr_exe, "cdrecord"); } else { strcpy(cdr_exe, "dvdrecord"); } tmp[0] = '\0'; if (find_home_of_exe(cdr_exe)) { sprintf(command, "%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", cdr_exe); strcpy(tmp, call_program_and_get_last_line_of_output(command)); } if (strlen(tmp) < 2) { mr_free(comment); mr_free(tmp); mr_free(cdr_exe); mr_free(command); return 1; } else { strcpy(cdrw_device, tmp); sprintf(comment, "Found CDRW device - %s", cdrw_device); log_it(comment); strcpy(g_cdrw_drive_is_here, cdrw_device); mr_free(comment); mr_free(tmp); mr_free(cdr_exe); mr_free(command); return (0); } } /** * Attempt to locate a CD-ROM device's /dev entry. * Several different methods may be used to find the device, including * calling @c cdrecord, searching @c dmesg, and trial-and-error. * @param output Where to put the located /dev entry. * @param try_to_mount Whether to mount the CD as part of the test; if mount * fails then return failure. * @return 0 for success, nonzero for failure. */ int find_cdrom_device(char *output, bool try_to_mount) { /*@ pointers **************************************************** */ FILE *fin; char *p; char *q; char *r; int retval = 0; /*@ bool's ****************************************************** */ bool found_it = FALSE; /*@ buffers ***************************************************** */ char *tmp; char *cdr_exe; char *phrase_one; char *phrase_two; char *command; char *dvd_last_resort; char *mountpoint; static char the_last_place_i_found_it[MAX_STR_LEN] = ""; /*@ intialize *************************************************** */ malloc_string(tmp); malloc_string(cdr_exe); malloc_string(phrase_one); malloc_string(phrase_two); malloc_string(command); malloc_string(dvd_last_resort); malloc_string(mountpoint); output[0] = '\0'; phrase_one[0] = '\0'; phrase_two[0] = '\0'; dvd_last_resort[0] = '\0'; /*@ end vars **************************************************** */ if (g_cdrom_drive_is_here[0] && !isdigit(g_cdrom_drive_is_here[0])) { strcpy(output, g_cdrom_drive_is_here); mr_msg(3, "Been there, done that. Returning %s", output); retval = 0; goto end_of_find_cdrom_device; } if (the_last_place_i_found_it[0] != '\0' && !try_to_mount) { strcpy(output, the_last_place_i_found_it); mr_msg(3, "find_cdrom_device() --- returning last found location - '%s'", output); retval = 0; goto end_of_find_cdrom_device; } sprintf(mountpoint, "/tmp/cd.%d", (int) (random() % 32767)); make_hole_for_dir(mountpoint); if (find_home_of_exe("cdrecord")) { strcpy(cdr_exe, "cdrecord"); } else { strcpy(cdr_exe, "dvdrecord"); } tmp[0] = '\0'; if (!find_home_of_exe(cdr_exe)) { strcpy(output, "/dev/cdrom"); mr_msg(4, "Can't find cdrecord; assuming %s", output); if (!does_device_exist(output)) { mr_msg(4, "That didn't work. Sorry."); retval = 1; goto end_of_find_cdrom_device; } else { retval = 0; goto end_of_find_cdrom_device; } } sprintf(command, "%s -scanbus 2> /dev/null", cdr_exe); fin = popen(command, "r"); if (!fin) { mr_msg(4, "command=%s", command); log_OS_error("Cannot popen command"); return (1); } for (fgets(tmp, MAX_STR_LEN, fin); !feof(fin); fgets(tmp, MAX_STR_LEN, fin)) { p = strchr(tmp, '\''); if (p) { q = strchr(++p, '\''); if (q) { for (r = q; *(r - 1) == ' '; r--); *r = '\0'; strcpy(phrase_one, p); p = strchr(++q, '\''); if (p) { q = strchr(++p, '\''); if (q) { while (*(q - 1) == ' ') { q--; } *q = '\0'; strcpy(phrase_two, p); } } } } } paranoid_pclose(fin); #ifndef __FreeBSD__ if (strlen(phrase_two) == 0) { mr_msg(4, "Not running phase two. String is empty."); } else { sprintf(command, "dmesg | grep \"%s\" 2> /dev/null", phrase_two); fin = popen(command, "r"); if (!fin) { mr_msg(4, "Cannot run 2nd command - non-fatal, fortunately"); } else { for (fgets(tmp, MAX_STR_LEN, fin); !feof(fin); fgets(tmp, MAX_STR_LEN, fin)) { mr_msg(5, "--> '%s'", tmp); if (tmp[0] != ' ' && tmp[1] != ' ') { p = strchr(tmp, ':'); if (p) { *p = '\0'; if (strstr(tmp, "DVD")) { sprintf(dvd_last_resort, "/dev/%s", tmp); mr_msg(4, "Ignoring '%s' because it's a DVD drive", tmp); } else { sprintf(output, "/dev/%s", tmp); found_it = TRUE; } } } } paranoid_pclose(fin); } } #endif #ifdef __FreeBSD__ if (!found_it) { mr_msg(4, "OK, approach 2"); if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/cdrom"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/cdrom1"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/dvd"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/acd0"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/cd01"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/acd1"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/cd1"))) { retval = 1; goto end_of_find_cdrom_device; } } } } } } } } #else if (!found_it && strlen(dvd_last_resort) > 0) { mr_msg(4, "Well, I'll use the DVD - %s - as a last resort", dvd_last_resort); strcpy(output, dvd_last_resort); found_it = TRUE; } if (found_it) { sprintf(tmp, "grep \"%s=ide-scsi\" /proc/cmdline &> /dev/null", strrchr(output, '/') + 1); if (system(tmp) == 0) { mr_msg(4, "%s is not right. It's being SCSI-emulated. Continuing.", output); found_it = FALSE; output[0] = '\0'; } } if (found_it) { mr_msg(4, "(find_cdrom_device) --> '%s'", output); if (!does_device_exist(output)) { found_it = FALSE; mr_msg(4, "OK, I was wrong, I haven't found it... yet."); } } if (!found_it) { mr_msg(4, "OK, approach 2"); if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/scd0"))) { if (!(found_it = set_dev_to_this_if_rx_OK(output, "/dev/sr0"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/cdrom"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/cdrom0"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/cdrom1"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/sr1"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, "/dev/dvd"))) { if (! (found_it = set_dev_to_this_if_rx_OK(output, g_cdrw_drive_is_here))) { retval = 1; goto end_of_find_cdrom_device; } } } } } } } } } #endif if (found_it && try_to_mount) { if (mount_CDROM_here(output, mountpoint)) { mr_msg(4, "[Cardigans] I've changed my mind"); found_it = FALSE; } else { sprintf(tmp, "%s/archives", mountpoint); if (!does_file_exist(tmp)) { mr_msg(4, "[Cardigans] I'll take it back"); found_it = FALSE; } else { sprintf(command, "umount %s", output); paranoid_system(command); mr_msg(4, "I'm confident the Mondo CD is in %s", output); } } } unlink(mountpoint); if (found_it) { if (!does_file_exist(output)) { mr_msg(3, "I still haven't found it."); return (1); } mr_msg(3, "(find_cdrom_device) --> '%s'", output); strcpy(the_last_place_i_found_it, output); strcpy(g_cdrom_drive_is_here, output); retval = 0; goto end_of_find_cdrom_device; } sprintf(command, "%s -scanbus | grep \"[0-9],[0-9],[0-9]\" | grep \"[D|C][V|D]\" | grep -n \"\" | grep \"%s\" | cut -d':' -f2", cdr_exe, g_cdrw_drive_is_here); mr_msg(1, "command=%s", command); strcpy(tmp, call_program_and_get_last_line_of_output(command)); if (tmp[0]) { strcpy(output, tmp); mr_msg(4, "Finally found it at %s", output); retval = 0; goto end_of_find_cdrom_device; } else { mr_msg(4, "Still couldn't find it."); retval = 1; goto end_of_find_cdrom_device; } end_of_find_cdrom_device: mr_free(tmp); mr_free(cdr_exe); mr_free(phrase_one); mr_free(phrase_two); mr_free(command); mr_free(dvd_last_resort); mr_free(mountpoint); return (retval); } int find_dvd_device(char *output, bool try_to_mount) { char *command; char *tmp; int retval = 0, devno = -1; malloc_string(command); malloc_string(tmp); if (g_dvd_drive_is_here[0]) { strcpy(output, g_dvd_drive_is_here); mr_msg(3, "Been there, done that. Returning %s", output); return (0); } sprintf(tmp, call_program_and_get_last_line_of_output ("dvdrecord -scanbus 2> /dev/null | grep \") '\" | grep -n \"\" | grep DVD | cut -d':' -f1") ); mr_msg(5, "tmp = '%s'", tmp); if (!tmp[0]) sprintf(tmp, call_program_and_get_last_line_of_output ("cdrecord -scanbus 2> /dev/null | grep \") '\" | grep -n \"\" | grep DVD | cut -d':' -f1") ); if (tmp[0]) { devno = atoi(tmp) - 1; } if (devno >= 0) { retval = 0; sprintf(output, "/dev/scd%d", devno); strcpy(g_dvd_drive_is_here, output); mr_msg(2, "I think DVD is at %s", output); } else { mr_msg(2, "I cannot find DVD"); retval = 1; } if (try_to_mount) { mr_msg(1, "Ignoring the fact that try_to_mount==TRUE"); } return (retval); } #include /** * Find the size of the specified @p drive, in megabytes. Uses @c ioctl calls * and @c dmesg. * @param drive The device to find the size of. * @return size in megabytes. */ long get_phys_size_of_drive(char *drive) { int fd; #if linux unsigned long long s = 0; int fileid, cylinders = 0, cylindersleft = 0; int cylindersize = 0; int gotgeo = 0; struct hd_geometry hdgeo; #elif __FreeBSD__ off_t s; #endif long outvalA = -1; long outvalB = -1; long outvalC = -1; if ((fd = open(drive, O_RDONLY)) != -1) { if (ioctl(fd, #if linux #ifdef BLKGETSIZE64 BLKGETSIZE64, #else BLKGETSIZE, #endif #elif __FreeBSD__ DIOCGMEDIASIZE, #endif &s) != -1) { close(fd); // s>>11 works for older disks but not for newer ones outvalB = #if linux #ifdef BLKGETSIZE64 s >> 20 #else s >> 11 #endif #else s >> 20 #endif ; } } if (outvalB <= 0) { mr_msg(1, "Error getting size of %s: %s", drive, strerror(errno)); #if linux fileid = open(drive, O_RDONLY); if (fileid) { if (ioctl(fileid, HDIO_GETGEO, &hdgeo) != -1) { if (hdgeo.cylinders && hdgeo.heads && hdgeo.sectors) { cylindersleft = cylinders = hdgeo.cylinders; cylindersize = hdgeo.heads * hdgeo.sectors / 2; outvalA = cylindersize * cylinders / 1024; mr_msg(2, "Got Harddisk geometry, C:%d, H:%d, S:%d", hdgeo.cylinders, hdgeo.heads, hdgeo.sectors); gotgeo = 1; } else { mr_msg(1, "Harddisk geometry wrong"); } } else { mr_msg(1, "Error in ioctl() getting new hard disk geometry (%s), resizing in unsafe mode", strerror(errno)); } close(fileid); } else { mr_msg(1, "Failed to open %s for reading: %s", drive, strerror(errno)); } if (!gotgeo) { mr_msg(1, "Failed to get harddisk geometry, using old mode"); } /* if ((fd = open (drive, O_RDONLY)) != -1) { if (ioctl (fd, HDIO_GETGEO, &hdgeo) != -1) { close (fd); mr_msg (2, "Geometry of drive %s is C:%d, H:%d, S%d, its size is %d MB", drive, hdgeo.cylinders, hdgeo.heads, hdgeo.sectors, (hdgeo.cylinders * hdgeo.heads * hdgeo.sectors / 2 / 1024)); if ( hdgeo.cylinders && hdgeo.heads && hdgeo.sectors ) { outvalB = ((long) (hdgeo.cylinders * hdgeo.heads * hdgeo.sectors / 2 / 1024)); } } close (fd); */ #endif } // OLDER DISKS will give ridiculously low value for outvalB (so outvalA is returned) :) // NEWER DISKS will give sane value for outvalB (close to outvalA, in other words) :) outvalC = (outvalA > outvalB) ? outvalA : outvalB; // mr_msg (5, "drive = %s, error = %s", drive, strerror (errno)); // fatal_error ("GPSOD: Unable to get size of drive"); mr_msg(1, "%s --> %ld or %ld --> %ld", drive, outvalA, outvalB, outvalC); return (outvalC); } /** * Determine whether @p format is supported by the kernel. Uses /proc/filesystems * under Linux and @c lsvfs under FreeBSD. * @param format The format to test. * @return TRUE if the format is supported, FALSE if not. */ bool is_this_a_valid_disk_format(char *format) { char *good_formats = NULL; char *command = NULL; char *tmp = NULL; char *format_sz = NULL; FILE *pin = NULL; bool retval = FALSE; size_t n = 0; assert_string_is_neither_NULL_nor_zerolength(format); mr_asprintf(&format_sz, "%s ", format); #ifdef __FreeBSD__ mr_asprintf(&command, "lsvfs | tr -s '\t' ' ' | grep -v Filesys | grep -v -- -- | cut -d' ' -f1 | tr -s '\n' ' '"); #else mr_asprintf(&command, "grep -v nodev /proc/filesystems | tr -s '\t' ' ' | cut -d' ' -f2 | tr -s '\n' ' '"); #endif pin = popen(command, "r"); mr_free(command); if (!pin) { log_OS_error("Unable to read good formats"); } else { mr_getline(&good_formats, &n , pin); if (pclose(pin)) { log_OS_error("Cannot pclose good formats"); } mr_strip_spaces(good_formats); // " ntfs 7 " -- um, cheating much? :) mr_asprintf(&tmp, " %s swap lvm raid ntfs 7 ",good_formats); mr_free(good_formats); good_formats = tmp; if (strstr(good_formats, format_sz)) { retval = TRUE; } mr_free(good_formats); } mr_free(format_sz); return (retval); } /** @def SWAPLIST_COMMAND The command to list the swap files/partitions in use. */ /** * Determine whether @p device_raw is currently mounted. * @param device_raw The device to check. * @return TRUE if it's mounted, FALSE if not. */ bool is_this_device_mounted(char *device_raw) { /*@ pointers **************************************************** */ FILE *fin; /*@ buffers ***************************************************** */ char *incoming = NULL; char *device_with_tab = NULL; char *device_with_space = NULL; char *tmp = NULL; size_t n = 0; #ifdef __FreeBSD__ #define SWAPLIST_COMMAND "swapinfo" #else #define SWAPLIST_COMMAND "cat /proc/swaps" #endif /*@ end vars **************************************************** */ assert(device_raw != NULL); // assert_string_is_neither_NULL_nor_zerolength(device_raw); if (device_raw[0] != '/' && !strstr(device_raw, ":/")) { mr_msg(1, "%s needs to have a '/' prefixed - I'll do it", device_raw); mr_asprintf(&tmp, "/%s", device_raw); } else { mr_asprintf(&tmp, device_raw); } mr_msg(1, "Is %s mounted?", tmp); if (!strcmp(tmp, "/proc") || !strcmp(tmp, "proc")) { mr_msg(1, "I don't know how the heck /proc made it into the mountlist. I'll ignore it."); return (FALSE); } mr_asprintf(&device_with_tab, "%s\t", tmp); mr_asprintf(&device_with_space, "%s ", tmp); mr_free(tmp); if (!(fin = popen("mount", "r"))) { log_OS_error("Cannot popen 'mount'"); return (FALSE); } for (mr_getline(&incoming, &n, fin); !feof(fin); mr_getline(&incoming, &n, fin)) { if (strstr(incoming, device_with_space) //> incoming || strstr(incoming, device_with_tab)) // > incoming) { paranoid_pclose(fin); mr_free(incoming); return(TRUE); } } mr_free(incoming); mr_free(device_with_tab); paranoid_pclose(fin); mr_asprintf(&tmp, "%s | grep -E \"^%s\" > /dev/null 2> /dev/null", SWAPLIST_COMMAND, device_with_space); mr_free(device_with_space); mr_msg(4, "tmp (command) = '%s'", tmp); if (!system(tmp)) { mr_free(tmp); return(TRUE); } mr_free(tmp); return (FALSE); } #ifdef __FreeBSD__ // CODE IS FREEBSD-SPECIFIC /** * Create a loopback device for specified @p fname. * @param fname The file to associate with a device. * @return /dev entry for the device, or NULL if it couldn't be allocated. */ char *make_vn(char *fname) { char *device = NULL; char *mddevice = NULL; char *command = NULL; int vndev = 2; if (atoi (call_program_and_get_last_line_of_output ("/sbin/sysctl -n kern.osreldate")) < 500000) { do { mr_free(mddevice); mr_asprintf(&mddevice, "vn%ic", vndev++); mr_free(command); mr_asprintf(&command, "vnconfig %s %s", mddevice, fname); if (vndev > 10) { mr_free(command); mr_free(mddevice); return NULL; } } while (system(command)); mr_free(command); } else { mr_asprintf(&command, "mdconfig -a -t vnode -f %s", fname); mr_asprintf(&mddevice, call_program_and_get_last_line_of_output(command)); mr_free(command); if (!strstr(mddevice, "md")) { mr_free(mddevice); return NULL; } } mr_asprintf(&device, "/dev/%s", mddevice); mr_free(mddevice); return(device); } // CODE IS FREEBSD-SPECIFIC /** * Deallocate specified @p dname. * This should be called when you are done with the device created by make_vn(), * so the system does not run out of @c vn devices. * @param dname The device to deallocate. * @return 0 for success, nonzero for failure. */ int kick_vn(char *dname) { char *command; int ret = 0; if (strncmp(dname, "/dev/", 5) == 0) { dname += 5; } if (atoi (call_program_and_get_last_line_of_output ("/sbin/sysctl -n kern.osreldate")) < 500000) { mr_asprintf(&command, "vnconfig -d %s", dname); } else { mr_asprintf(&command, "mdconfig -d -u %s", dname); } ret = system(command); mr_free(command); return(ret); } #endif /** * Ask the user for CD number @p cd_number_i_want. * Sets g_current_media_number once the correct CD is inserted. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->backup_media_type * - @c bkpinfo->prefix * - @c bkpinfo->isodir * - @c bkpinfo->media_device * - @c bkpinfo->please_dont_eject_when_restoring * @param cd_number_i_want The CD number to ask for. */ void insist_on_this_cd_number(struct s_bkpinfo *bkpinfo, int cd_number_i_want) { /*@ int ************************************************************* */ int res = 0; /*@ buffers ********************************************************* */ char *tmp = NULL; char *request = NULL; assert(bkpinfo != NULL); assert(cd_number_i_want > 0); // mr_msg(3, "Insisting on CD number %d", cd_number_i_want); if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) { mr_msg(3, "No need to insist_on_this_cd_number when the backup type isn't CD-R(W) or NFS or ISO"); return; } mr_asprintf(&tmp, "mkdir -p " MNT_CDROM); run_program_and_log_output(tmp, 5); mr_free(tmp); if (g_ISO_restore_mode || bkpinfo->backup_media_type == iso || bkpinfo->backup_media_type == nfs) { mr_msg(3, "Remounting CD"); g_ISO_restore_mode = TRUE; // FIXME --- I'm tempted to do something about this... // Why unmount and remount again and again? if (is_this_device_mounted(MNT_CDROM)) { run_program_and_log_output("umount " MNT_CDROM, 5); } system("mkdir -p /tmp/isodir &> /dev/null"); mr_asprintf(&tmp, "%s/%s/%s-%d.iso", bkpinfo->isodir, bkpinfo->nfs_remote_dir, bkpinfo->prefix, cd_number_i_want); if (!does_file_exist(tmp)) { mr_free(tmp); mr_asprintf(&tmp, "/tmp/isodir/%s/%s-%d.iso", bkpinfo->nfs_remote_dir, bkpinfo->prefix, cd_number_i_want); if (does_file_exist(tmp)) { mr_msg(1, "FIXME - hacking bkpinfo->isodir from '%s' to /tmp/isodir", bkpinfo->isodir); strcpy(bkpinfo->isodir, "/tmp/isodir"); } } mr_msg(3, "Mounting %s at %s", tmp, MNT_CDROM); if (mount_CDROM_here(tmp, MNT_CDROM)) { fatal_error("Mommy!"); } mr_free(tmp); } if ((res = what_number_cd_is_this(bkpinfo)) != cd_number_i_want) { mr_msg(3, "Currently, we hold %d but we want %d", res, cd_number_i_want); mr_asprintf(&tmp, "Insisting on %s #%d", bkpinfo->backup_media_string, cd_number_i_want); mr_asprintf(&request, "Please insert %s #%d and press Enter.", bkpinfo->backup_media_string, cd_number_i_want); mr_msg(3, tmp); mr_free(tmp); while (what_number_cd_is_this(bkpinfo) != cd_number_i_want) { sync(); if (is_this_device_mounted(MNT_CDROM)) { res = run_program_and_log_output("umount " MNT_CDROM, FALSE); } else { res = 0; } if (res) { log_to_screen(_("WARNING - failed to unmount CD-ROM drive")); } if (!bkpinfo->please_dont_eject) { res = eject_device(bkpinfo->media_device); } else { res = 0; } if (res) { log_to_screen(_("WARNING - failed to eject CD-ROM disk")); } popup_and_OK(request); if (!bkpinfo->please_dont_eject) { inject_device(bkpinfo->media_device); } sync(); } mr_free(request); mr_msg(1, "Thankyou. Proceeding..."); g_current_media_number = cd_number_i_want; } } /* @} - end of deviceGroup */ /** * Ask user for details of backup/restore information. * Called when @c mondoarchive doesn't get any parameters. * @param bkpinfo The backup information structure to fill out with the user's data. * @param archiving_to_media TRUE if archiving, FALSE if restoring. * @return 0, always. * @bug No point of `int' return value. * @ingroup archiveGroup */ int interactively_obtain_media_parameters_from_user(struct s_bkpinfo *bkpinfo, bool archiving_to_media) // archiving_to_media is TRUE if I'm being called by mondoarchive // archiving_to_media is FALSE if I'm being called by mondorestore { char *tmp = NULL; char *sz_size = NULL; char *command = NULL; char *comment = NULL; char *prompt = NULL; int i = 0; int res = 0; FILE *fin = NULL; assert(bkpinfo != NULL); bkpinfo->nonbootable_backup = FALSE; // Tape, CD, NFS, ...? srandom(getpid()); bkpinfo->backup_media_type = (g_restoring_live_from_cd) ? cdr : which_backup_media_type(bkpinfo->restore_data); if (bkpinfo->backup_media_type == none) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } if (bkpinfo->backup_media_type == tape && bkpinfo->restore_data) { popup_and_OK(_("Please remove CD from drive")); } mr_msg(3, "media type = %s", bkptype_to_string(bkpinfo->backup_media_type)); if (archiving_to_media) { sensibly_set_tmpdir_and_scratchdir(bkpinfo); } bkpinfo->cdrw_speed = (bkpinfo->backup_media_type == cdstream) ? 2 : 4; bkpinfo->compression_level = (bkpinfo->backup_media_type == cdstream) ? 1 : 5; bkpinfo->use_lzo = (bkpinfo->backup_media_type == cdstream) ? TRUE : FALSE; mvaddstr_and_log_it(2, 0, " "); // Find device's /dev (or SCSI) entry switch (bkpinfo->backup_media_type) { case cdr: case cdrw: case dvd: case usb: if (archiving_to_media) { if ((bkpinfo->backup_media_type != dvd) && (bkpinfo->backup_media_type != usb)) { if (ask_me_yes_or_no (_("Is your computer a laptop, or does the CD writer incorporate BurnProof technology?"))) { bkpinfo->manual_cd_tray = TRUE; } } if ((bkpinfo->compression_level = which_compression_level()) == -1) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } mr_asprintf(&comment, _("What speed is your %s (re)writer?"), bkpinfo->backup_media_string); if (bkpinfo->backup_media_type == dvd) { find_dvd_device(bkpinfo->media_device, FALSE); mr_asprintf(&tmp, "1"); mr_asprintf(&sz_size, "%d", DEFAULT_DVD_DISK_SIZE); // 4.7 salesman's GB = 4.482 real GB = 4482 MB mr_msg(1, "Setting to DVD defaults"); } else if (bkpinfo->backup_media_type == usb) { strcpy(bkpinfo->media_device, VANILLA_USB_DEVICE); asprintf(&sz_size, "512"); } else { strcpy(bkpinfo->media_device, VANILLA_SCSI_CDROM); mr_asprintf(&tmp, "4"); mr_asprintf(&sz_size, "650"); mr_msg(1, "Setting to CD defaults"); } if ((bkpinfo->backup_media_type != dvd) && (bkpinfo->backup_media_type != usb)) { if (!popup_and_get_string(_("Speed"), comment, tmp, 4)) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } } mr_free(comment); bkpinfo->cdrw_speed = atoi(tmp); // if DVD then this shouldn't ever be used anyway :) mr_free(tmp); mr_asprintf(&comment, _("How much data (in Megabytes) will each %s store?"), bkpinfo->backup_media_string); if (!popup_and_get_string("Size", comment, sz_size, 5)) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } mr_free(comment); bkpinfo->media_size = atol(sz_size); mr_free(sz_size); if (bkpinfo->media_size <= 0L) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } } case cdstream: if (bkpinfo->disaster_recovery) { strcpy(bkpinfo->media_device, "/dev/cdrom"); mr_msg(2, "CD-ROM device assumed to be at %s", bkpinfo->media_device); } else if (bkpinfo->restore_data || bkpinfo->backup_media_type == dvd) { if (!bkpinfo->media_device[0]) { strcpy(bkpinfo->media_device, "/dev/cdrom"); } // just for the heck of it :) mr_msg(1, "bkpinfo->media_device = %s", bkpinfo->media_device); if (bkpinfo->backup_media_type == dvd || find_cdrom_device(bkpinfo->media_device, FALSE)) { mr_msg(1, "bkpinfo->media_device = %s", bkpinfo->media_device); mr_asprintf(&comment, _("Please specify your %s drive's /dev entry"), bkpinfo->backup_media_string); if (!popup_and_get_string (_("Device?"), comment, bkpinfo->media_device, MAX_STR_LEN / 4)) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } mr_free(comment); } mr_msg(2, "%s device found at %s", bkpinfo->backup_media_string, bkpinfo->media_device); } else { if (find_cdrw_device(bkpinfo->media_device)) { bkpinfo->media_device[0] = '\0'; } if (bkpinfo->media_device[0]) { mr_asprintf(&tmp, _("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."), bkpinfo->backup_media_string, bkpinfo->media_device); if (!ask_me_yes_or_no(tmp)) { bkpinfo->media_device[0] = '\0'; } mr_free(tmp); } else { if (g_kernel_version < 2.6) { i = popup_and_get_string("Device node?", "What is the SCSI node of your CD (re)writer, please?", bkpinfo->media_device, MAX_STR_LEN / 4); } else { i = popup_and_get_string("/dev entry?", "What is the /dev entry of your CD (re)writer, please?", bkpinfo->media_device, MAX_STR_LEN / 4); } if (!i) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } } } if (bkpinfo->backup_media_type == cdstream) { bkpinfo->media_size = (long)650; } break; case udev: if (!ask_me_yes_or_no (_("This option is for advanced users only. Are you sure?"))) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } case tape: res = find_tape_device_and_size(bkpinfo->media_device, sz_size); if ((res != 0) && (! bkpinfo->media_device[0])) { mr_msg(3, "Ok, using vanilla scsi tape."); strcpy(bkpinfo->media_device, VANILLA_SCSI_TAPE); if ((fin = fopen(bkpinfo->media_device, "r"))) { paranoid_fclose(fin); } else { mr_msg(2, "Don't know what your tape device is :-("); } } if (bkpinfo->media_device[0]) { if ((fin = fopen(bkpinfo->media_device, "r"))) { paranoid_fclose(fin); } else { if (does_file_exist("/tmp/mondo-restore.cfg")) { read_cfg_var("/tmp/mondo-restore.cfg", "media-dev", bkpinfo->media_device); } } mr_asprintf(&tmp, _("I think I've found your tape streamer at %s; am I right on the money?"), bkpinfo->media_device); if (!ask_me_yes_or_no(tmp)) { if (!popup_and_get_string ("Device name?", "What is the /dev entry of your tape streamer?", bkpinfo->media_device, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } } mr_free(tmp); } else { if (!popup_and_get_string ("Device name?", "What is the /dev entry of your tape streamer?", bkpinfo->media_device, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } } mr_asprintf(&tmp, "ls -l %s", bkpinfo->media_device); if (run_program_and_log_output(tmp, FALSE)) { log_to_screen(_("User has not specified a valid /dev entry")); finish(1); } mr_free(tmp); mr_msg(4, "sz_size = %s", sz_size); mr_free(sz_size); bkpinfo->media_size = 0L; mr_msg(4, "media_size = %ld", bkpinfo->media_size); if (bkpinfo->media_size <= 0L) { bkpinfo->media_size = 0L; } if (archiving_to_media) { if ((bkpinfo->compression_level = which_compression_level()) == -1) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } } break; case nfs: if (!bkpinfo->nfs_mount[0]) { strcpy(bkpinfo->nfs_mount, call_program_and_get_last_line_of_output ("mount | grep \":\" | cut -d' ' -f1 | head -n1")); } #ifdef __FreeBSD__ if (TRUE) #else if (!bkpinfo->disaster_recovery) #endif { if (!popup_and_get_string ("NFS dir.", "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.)", bkpinfo->nfs_mount, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } if (!bkpinfo->restore_data) { if ((bkpinfo->compression_level = which_compression_level()) == -1) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } } // check whether already mounted - we better remove // surrounding spaces and trailing '/' for this mr_strip_spaces(bkpinfo->nfs_mount); if (bkpinfo->nfs_mount[strlen(bkpinfo->nfs_mount) - 1] == '/') bkpinfo->nfs_mount[strlen(bkpinfo->nfs_mount) - 1] = '\0'; mr_asprintf(&command, "mount | grep \"%s \" | cut -d' ' -f3", bkpinfo->nfs_mount); strcpy(bkpinfo->isodir, call_program_and_get_last_line_of_output(command)); mr_free(command); mr_asprintf(&comment, _("How much data (in Megabytes) will each media store?")); if (!popup_and_get_string(_("Size"), comment, sz_size, 5)) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } mr_free(comment); bkpinfo->media_size = atol(sz_size); if (bkpinfo->media_size <= 0L) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } } if (bkpinfo->disaster_recovery) { system("umount /tmp/isodir 2> /dev/null"); if (!popup_and_get_string ("NFS share", "Which remote NFS share should I mount?", bkpinfo->nfs_mount, MAX_STR_LEN)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } } if (!is_this_device_mounted(bkpinfo->nfs_mount)) { sprintf(bkpinfo->isodir, "/tmp/isodir.mondo.%d", (int) (random() % 32768)); mr_asprintf(&command, "mkdir -p %s", bkpinfo->isodir); run_program_and_log_output(command, 5); mr_free(command); mr_asprintf(&tmp, "mount -t nfs -o nolock %s %s", bkpinfo->nfs_mount, bkpinfo->isodir); run_program_and_log_output(tmp, 5); mr_free(tmp); malloc_string(g_selfmounted_isodir); strcpy(g_selfmounted_isodir, bkpinfo->isodir); } if (!is_this_device_mounted(bkpinfo->nfs_mount)) { popup_and_OK (_("Please mount that partition before you try to backup to or restore from it.")); finish(1); } mr_asprintf(&tmp, bkpinfo->nfs_remote_dir); if (!popup_and_get_string ("Directory", "Which directory within that mountpoint?", tmp, MAX_STR_LEN)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } strcpy(bkpinfo->nfs_remote_dir, tmp); mr_free(tmp); // check whether writable - we better remove surrounding spaces for this mr_strip_spaces(bkpinfo->nfs_remote_dir); mr_asprintf(&command, "echo hi > '%s/%s/.dummy.txt'", bkpinfo->isodir, bkpinfo->nfs_remote_dir); while (run_program_and_log_output(command, FALSE)) { mr_asprintf(&tmp, bkpinfo->nfs_remote_dir); mr_asprintf(&prompt, _("Directory '%s' under mountpoint '%s' does not exist or is not writable. You can fix this or change the directory and retry or cancel the backup."), bkpinfo->nfs_remote_dir, bkpinfo->isodir); if (!popup_and_get_string ("Directory", prompt, tmp, MAX_STR_LEN)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } mr_free(prompt); strcpy(bkpinfo->nfs_remote_dir, tmp); mr_free(tmp); // check whether writable - we better remove surrounding space s for this mr_strip_spaces(bkpinfo->nfs_remote_dir); mr_free(command); mr_asprintf(&command, "echo hi > '%s/%s/.dummy.txt'", bkpinfo->isodir, bkpinfo->nfs_remote_dir); } mr_free(command); if (!popup_and_get_string ("Prefix.", "Please enter the prefix that will be prepended to your ISO filename. Example: machine1 to obtain machine1-[1-9]*.iso files", bkpinfo->prefix, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } mr_msg(3, "prefix set to %s", bkpinfo->prefix); bkpinfo->media_size = (long)650; mr_msg(3, "Just set nfs_remote_dir to %s", bkpinfo->nfs_remote_dir); mr_msg(3, "isodir is still %s", bkpinfo->isodir); break; case iso: if (!bkpinfo->disaster_recovery) { if (!popup_and_get_string ("Storage dir.", "Please enter the full path name to the directory for your ISO images. Example: /mnt/raid0_0", bkpinfo->isodir, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } if (archiving_to_media) { if ((bkpinfo->compression_level = which_compression_level()) == -1) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } if (!popup_and_get_string ("ISO size.", "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.", sz_size, 16)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } bkpinfo->media_size = atol(sz_size); } else { bkpinfo->media_size = (long)650; } } if (!popup_and_get_string ("Prefix.", "Please enter the prefix that will be prepended to your ISO filename. Example: machine1 to obtain machine1-[1-9]*.iso files", bkpinfo->prefix, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } mr_msg(3, "prefix set to %s", bkpinfo->prefix); break; default: fatal_error ("I, Mojo Jojo, shall defeat those pesky Powerpuff Girls!"); } if (archiving_to_media) { #ifdef __FreeBSD__ strcpy(bkpinfo->boot_device, call_program_and_get_last_line_of_output ("mount | grep ' / ' | head -1 | cut -d' ' -f1 | sed 's/\\([0-9]\\).*/\\1/'")); #else strcpy(bkpinfo->boot_device, call_program_and_get_last_line_of_output ("mount | grep ' / ' | head -1 | cut -d' ' -f1 | sed 's/[0-9].*//'")); #endif i = which_boot_loader(bkpinfo->boot_device); if (i == 'U') // unknown { #ifdef __FreeBSD__ if (!popup_and_get_string ("Boot device", "What is your boot device? (e.g. /dev/ad0)", bkpinfo->boot_device, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } i = which_boot_loader(bkpinfo->boot_device); #else if (!popup_and_get_string ("Boot device", "What is your boot device? (e.g. /dev/hda)", bkpinfo->boot_device, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } if (does_string_exist_in_boot_block (bkpinfo->boot_device, "LILO")) { i = 'L'; } else if (does_string_exist_in_boot_block (bkpinfo->boot_device, "ELILO")) { i = 'E'; } else if (does_string_exist_in_boot_block (bkpinfo->boot_device, "GRUB")) { i = 'G'; } else { i = 'U'; } #endif if (i == 'U') { if (ask_me_yes_or_no (_("Unidentified boot loader. Shall I restore it byte-for-byte at restore time and hope for the best?"))) { i = 'R'; // raw } else { log_to_screen (_("I cannot find your boot loader. Please run mondoarchive with parameters.")); finish(1); } } } bkpinfo->boot_loader = i; strcpy(bkpinfo->include_paths, "/"); if (!popup_and_get_string ("Backup paths", "Please enter paths which you want me to backup. The default is '/' (i.e. everything).", bkpinfo->include_paths, MAX_STR_LEN)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } mr_asprintf(&tmp, list_of_NFS_mounts_only()); if (strlen(tmp) > 2) { if (bkpinfo->exclude_paths[0]) { strcat(bkpinfo->exclude_paths, " "); } strncpy(bkpinfo->exclude_paths, tmp, MAX_STR_LEN); } mr_free(tmp); // NTFS mr_asprintf(&tmp, call_program_and_get_last_line_of_output ("parted2fdisk -l | grep -i ntfs | awk '{ print $1};' | tr -s '\\n' ' ' | awk '{ print $0};'")); if (strlen(tmp) > 2) { if (!popup_and_get_string ("NTFS partitions", "Please enter/confirm the NTFS partitions you wish to backup as well.", tmp, MAX_STR_LEN / 4)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } strncpy(bkpinfo->image_devs, tmp, MAX_STR_LEN / 4); } mr_free(tmp); if (!popup_and_get_string ("Exclude paths", "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.", bkpinfo->exclude_paths, (4*MAX_STR_LEN)-1)) { log_to_screen("User has chosen not to backup the PC"); finish(1); } // Interactive mode: #ifdef __IA64__ bkpinfo->make_cd_use_lilo = TRUE; #else bkpinfo->make_cd_use_lilo = FALSE; #endif bkpinfo->backup_data = TRUE; bkpinfo->verify_data = ask_me_yes_or_no (_("Will you want to verify your backups after Mondo has created them?")); #ifndef __FreeBSD__ if (!ask_me_yes_or_no ("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.")) #endif { strcpy(bkpinfo->kernel_path, "FAILSAFE"); } if (!ask_me_yes_or_no (_("Are you sure you want to proceed? Hit 'no' to abort."))) { log_to_screen(_("User has chosen not to backup the PC")); finish(1); } } else { bkpinfo->restore_data = TRUE; // probably... } if (bkpinfo->backup_media_type == iso || bkpinfo->backup_media_type == nfs) { g_ISO_restore_mode = TRUE; } #ifdef __FreeSD__ // skip #else if (bkpinfo->backup_media_type == nfs) { mr_msg(3, "I think the NFS mount is mounted at %s", bkpinfo->isodir); } log_it("isodir = %s", bkpinfo->isodir); log_it("nfs_mount = '%s'", bkpinfo->nfs_mount); #endif log_it("media device = %s", bkpinfo->media_device); log_it("media size = %ld", bkpinfo->media_size); log_it("media type = %s", bkpinfo->backup_media_string); log_it("prefix = %s", bkpinfo->prefix); log_it("compression = %ld", bkpinfo->compression_level); log_it("include_paths = '%s'", bkpinfo->include_paths); log_it("exclude_paths = '%s'", bkpinfo->exclude_paths); log_it("scratchdir = '%s'", bkpinfo->scratchdir); log_it("tmpdir = '%s'", bkpinfo->tmpdir); log_it("image_devs = '%s'", bkpinfo->image_devs); log_it("boot_device = '%s' (loader=%c)", bkpinfo->boot_device, bkpinfo->boot_loader); if (bkpinfo->media_size < 0L) { if (archiving_to_media) { fatal_error("Media size is less than zero."); } else { mr_msg(2, "Warning - media size is less than zero."); bkpinfo->media_size = 0L; } } return (0); } /** * Get a space-separated list of NFS mounts. * @return The list created. * @note The return value points to static data that will be overwritten with each call. * @bug Even though we only want the mounts, the devices are still checked. */ char *list_of_NFS_mounts_only(void) { char *exclude_these_devices; char *exclude_these_directories; static char result_sz[512]; malloc_string(exclude_these_devices); malloc_string(exclude_these_directories); strcpy(exclude_these_directories, call_program_and_get_last_line_of_output ("mount -t coda,ncpfs,nfs,smbfs,cifs,afs,ocfs,ocfs2,mvfs | tr -s '\t' ' ' | cut -d' ' -f3 | tr -s '\n' ' ' | awk '{print $0;}'")); strcpy(exclude_these_devices, call_program_and_get_last_line_of_output ("tr -s '\t' ' ' < /etc/fstab | grep -E '( (coda|ncpfs|nfs|smbfs|cifs|afs|ocfs|ocfs2|mvfs) )' | cut -d' ' -f1 | tr -s '\n' ' ' | awk '{print $0;}'")); sprintf(result_sz, "%s", exclude_these_directories); mr_free(exclude_these_devices); mr_free(exclude_these_directories); return (result_sz); } /* @} - end of utilityGroup */ /** * Set the tmpdir and scratchdir to reside on the partition with the most free space. * Automatically excludes DOS, NTFS, SMB, and NFS filesystems. * @param bkpinfo The backup information structure. @c bkpinfo->tmpdir and @c bkpinfo->scratchdir will be set. * @ingroup utilityGroup */ void sensibly_set_tmpdir_and_scratchdir(struct s_bkpinfo *bkpinfo) { char *tmp = NULL; char *command = NULL; char *sz = NULL; int i = 0; malloc_string(command); assert(bkpinfo != NULL); #ifdef __FreeBSD__ mr_asprintf(&tmp, call_program_and_get_last_line_of_output ("df -m -P -t nonfs,msdosfs,ntfs,smbfs,smb,cifs,afs,ocfs,ocfs2,mvfs | tr -s '\t' ' ' | grep -vE \"none|Filesystem\" | awk '{printf \"%s %s\\n\", $4, $6;}' | sort -n | tail -n1 | awk '{print $NF;}'")); #else mr_asprintf(&tmp, call_program_and_get_last_line_of_output ("df -m -P -x nfs -x vfat -x ntfs -x smbfs -x smb -x cifs -x afs -x ocfs -x ocfs2 -x mvfs | 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;}'")); #endif if (tmp[0] != '/') { mr_asprintf(&sz, "/%s", tmp); mr_free(tmp); tmp = sz; } if (!tmp[0]) { fatal_error("I couldn't figure out the tempdir!"); } i = (int) (random() % 32768); sprintf(bkpinfo->tmpdir, "%s/tmp.mondo.%d", tmp, i); log_it("bkpinfo->tmpdir is being set to %s", bkpinfo->tmpdir); sprintf(bkpinfo->scratchdir, "%s/mondo.scratch.%d", tmp, i); log_it("bkpinfo->scratchdir is being set to %s", bkpinfo->scratchdir); sprintf(g_erase_tmpdir_and_scratchdir, "rm -Rf %s %s", bkpinfo->tmpdir, bkpinfo->scratchdir); mr_asprintf(&command, "rm -Rf %s/tmp.mondo.* %s/mondo.scratch.*", tmp, tmp); mr_free(tmp); paranoid_system(command); mr_free(command); } /** * @addtogroup deviceGroup * @{ */ /** * If we can read @p dev, set @p output to it. * If @p dev cannot be read, set @p output to "". * @param dev The device to check for. * @param output Set to @p dev if @p dev exists, "" otherwise. * @return TRUE if @p dev exists, FALSE if it doesn't. */ bool set_dev_to_this_if_rx_OK(char *output, char *dev) { char *command = NULL; if (!dev || dev[0] == '\0') { output[0] = '\0'; return (FALSE); } mr_msg(10, "Injecting %s", dev); inject_device(dev); if (!does_file_exist(dev)) { mr_msg(10, "%s doesn't exist. Returning FALSE.", dev); return (FALSE); } mr_asprintf(&command, "dd bs=%ld count=1 if=%s of=/dev/null &> /dev/null", 512L, dev); if (!run_program_and_log_output(command, FALSE) && !run_program_and_log_output(command, FALSE)) { strcpy(output, dev); mr_msg(4, "Found it - %s", dev); mr_free(command); return (TRUE); } else { output[0] = '\0'; mr_msg(4, "It's not %s", dev); mr_free(command); return (FALSE); } } /** * Find out what number CD is in the drive. * @param bkpinfo The backup information structure. The @c bkpinfo->media_device field is the only one used. * @return The current CD number, or -1 if it could not be found. * @note If the CD is not mounted, it will be mounted * (and remain mounted after this function returns). */ int what_number_cd_is_this(struct s_bkpinfo *bkpinfo) { int cd_number = -1; char *mountdev = NULL; char *tmp = NULL; assert(bkpinfo != NULL); if (g_ISO_restore_mode) { mr_asprintf(&tmp, "mount | grep iso9660 | awk '{print $3;}'"); mr_asprintf(&mountdev, "%s/archives/THIS-CD-NUMBER", call_program_and_get_last_line_of_output(tmp)); cd_number = atoi(last_line_of_file(mountdev)); mr_free(mountdev); mr_free(tmp); return (cd_number); } mr_asprintf(&mountdev, bkpinfo->media_device); if (!mountdev[0]) { log_it ("(what_number_cd_is_this) Warning - media_device unknown. Finding out..."); find_cdrom_device(bkpinfo->media_device, FALSE); } if (!is_this_device_mounted(MNT_CDROM)) { mount_CDROM_here(mountdev, MNT_CDROM); } cd_number = atoi(last_line_of_file(MNT_CDROM "/archives/THIS-CD-NUMBER")); mr_free(mountdev); return (cd_number); } /** * Find out what device is mounted as root (/). * @return Root device. * @note The returned string points to static storage and will be overwritten with every call. * @bug A bit of a misnomer; it's actually finding out the root device. * The mountpoint (where it's mounted) will obviously be '/'. */ char *where_is_root_mounted() { /*@ buffers **************** */ static char tmp[MAX_STR_LEN]; #ifdef __FreeBSD__ strcpy(tmp, call_program_and_get_last_line_of_output ("mount | grep \" on / \" | cut -d' ' -f1")); #else strcpy(tmp, call_program_and_get_last_line_of_output ("mount | grep \" on / \" | cut -d' ' -f1 | sed s/[0-9]// | sed s/[0-9]//")); if (strstr(tmp, "/dev/cciss/")) { strcpy(tmp, call_program_and_get_last_line_of_output ("mount | grep \" on / \" | cut -d' ' -f1 | cut -dp -f1")); } if (strstr(tmp, "/dev/md")) { strcpy(tmp, call_program_and_get_last_line_of_output ("mount | grep \" on / \" | cut -d' ' -f1")); } #endif return (tmp); } /** * Find out which boot loader is in use. * @param which_device Device to look for the boot loader on. * @return 'L' for LILO, 'E'for ELILO, 'G' for GRUB, 'B' or 'D' for FreeBSD boot loaders, or 'U' for Unknown. * @note Under Linux, all drives are examined, not just @p which_device. */ char which_boot_loader(char *which_device) { #ifdef __FreeBSD__ int count_lilos = 0; int count_grubs = 0; int count_boot0s = 0; int count_dangerouslydedicated = 0; log_it("looking at drive %s's MBR", which_device); if (does_string_exist_in_boot_block(which_device, "GRUB")) { count_grubs++; } if (does_string_exist_in_boot_block(which_device, "LILO")) { count_lilos++; } if (does_string_exist_in_boot_block(which_device, "Drive")) { count_boot0s++; } if (does_string_exist_in_first_N_blocks (which_device, "FreeBSD/i386", 17)) { count_dangerouslydedicated++; } log_it("%d grubs and %d lilos and %d elilos and %d boot0s and %d DD\n", count_grubs, count_lilos, count_elilos, count_boot0s, count_dangerouslydedicated); if (count_grubs && !count_lilos) { return ('G'); } else if (count_lilos && !count_grubs) { return ('L'); } else if (count_grubs == 1 && count_lilos == 1) { log_it("I'll bet you used to use LILO but switched to GRUB..."); return ('G'); } else if (count_boot0s == 1) { return ('B'); } else if (count_dangerouslydedicated) { return ('D'); } else { log_it("Unknown boot loader"); return ('U'); } #else /*@ buffer ***************************************************** */ char *list_drives_cmd; char *current_drive = NULL; /*@ pointers *************************************************** */ FILE *pdrives; /*@ int ******************************************************** */ int count_lilos = 0; int count_grubs = 0; size_t n = 0; /*@ end vars *************************************************** */ #ifdef __IA64__ /* No choice for it */ return ('E'); #endif assert(which_device != NULL); mr_asprintf(&list_drives_cmd, "parted2fdisk -l 2>/dev/null | grep \"/dev/.*:\" | tr -s ':' ' ' | tr -s ' ' '\n' | grep /dev/; echo %s", where_is_root_mounted()); log_it("list_drives_cmd = %s", list_drives_cmd); if (!(pdrives = popen(list_drives_cmd, "r"))) { log_OS_error("Unable to open list of drives"); mr_free(list_drives_cmd); return ('\0'); } mr_free(list_drives_cmd); for (mr_getline(¤t_drive, &n, pdrives); !feof(pdrives); mr_getline(¤t_drive, &n, pdrives)) { mr_strip_spaces(current_drive); log_it("looking at drive %s's MBR", current_drive); if (does_string_exist_in_boot_block(current_drive, "GRUB")) { count_grubs++; strcpy(which_device, current_drive); break; } if (does_string_exist_in_boot_block(current_drive, "LILO")) { count_lilos++; strcpy(which_device, current_drive); break; } } mr_free(current_drive); if (pclose(pdrives)) { log_OS_error("Cannot pclose pdrives"); } log_it("%d grubs and %d lilos\n", count_grubs, count_lilos); if (count_grubs && !count_lilos) { return ('G'); } else if (count_lilos && !count_grubs) { return ('L'); } else if (count_grubs == 1 && count_lilos == 1) { log_it("I'll bet you used to use LILO but switched to GRUB..."); return ('G'); } else { // We need to look on each partition then mr_asprintf(&list_drives_cmd, "parted2fdisk -l 2>/dev/null | grep -E \"^/dev/\" | tr -s ':' ' ' | tr -s ' ' '\n' | grep /dev/"); log_it("list_drives_cmd = %s", list_drives_cmd); if (!(pdrives = popen(list_drives_cmd, "r"))) { log_OS_error("Unable to open list of drives"); mr_free(list_drives_cmd); return ('\0'); } mr_free(list_drives_cmd); for (mr_getline(¤t_drive, &n, pdrives); !feof(pdrives); mr_getline(¤t_drive, &n, pdrives)) { mr_strip_spaces(current_drive); log_it("looking at partition %s's BR", current_drive); if (does_string_exist_in_boot_block(current_drive, "GRUB")) { count_grubs++; strcpy(which_device, current_drive); break; } if (does_string_exist_in_boot_block(current_drive, "LILO")) { count_lilos++; strcpy(which_device, current_drive); break; } } mr_free(current_drive); if (pclose(pdrives)) { log_OS_error("Cannot pclose pdrives"); } log_it("%d grubs and %d lilos\n", count_grubs, count_lilos); if (count_grubs && !count_lilos) { return ('G'); } else if (count_lilos && !count_grubs) { return ('L'); } else if (count_grubs == 1 && count_lilos == 1) { log_it("I'll bet you used to use LILO but switched to GRUB..."); return ('G'); } else { log_it("Unknown boot loader"); return ('U'); } } #endif } /** * Write zeroes over the first 16K of @p device. * @param device The device to zero. * @return 0 for success, 1 for failure. */ int zero_out_a_device(char *device) { FILE *fout; int i; assert_string_is_neither_NULL_nor_zerolength(device); log_it("Zeroing drive %s", device); if (!(fout = fopen(device, "w"))) { log_OS_error("Unable to open/write to device"); return (1); } for (i = 0; i < 16384; i++) { fputc('\0', fout); } paranoid_fclose(fout); log_it("Device successfully zeroed."); return (0); } /** * Return the device pointed to by @p incoming. * @param incoming The device to resolve symlinks for. * @return The path to the real device file. * @note The returned string points to static storage that will be overwritten with each call. * @bug Won't work with file v4.0; needs to be written in C. */ char *resolve_softlinks_to_get_to_actual_device_file(char *incoming) { static char output[MAX_STR_LEN]; char *command = NULL; char *curr_fname = NULL; char *scratch = NULL; char *tmp = NULL; char *p = NULL; struct stat statbuf; malloc_string(tmp); malloc_string(scratch); malloc_string(curr_fname); if (!does_file_exist(incoming)) { log_it ("resolve_softlinks_to_get_to_actual_device_file --- device not found"); strcpy(output, incoming); } else { strcpy(curr_fname, incoming); lstat(curr_fname, &statbuf); while (S_ISLNK(statbuf.st_mode)) { mr_msg(1, "curr_fname = %s", curr_fname); mr_asprintf(&command, "file %s", curr_fname); strcpy(tmp, call_program_and_get_last_line_of_output(command)); mr_free(command); for (p = tmp + strlen(tmp); p != tmp && *p != '`' && *p != ' '; p--); p++; strcpy(scratch, p); for (p = scratch; *p != '\0' && *p != '\''; p++); *p = '\0'; mr_msg(0, "curr_fname %s --> '%s' --> %s", curr_fname, tmp, scratch); if (scratch[0] == '/') { strcpy(curr_fname, scratch); // copy whole thing because it's an absolute softlink } else { // copy over the basename cos it's a relative softlink p = curr_fname + strlen(curr_fname); while (p != curr_fname && *p != '/') { p--; } if (*p == '/') { p++; } strcpy(p, scratch); } lstat(curr_fname, &statbuf); } strcpy(output, curr_fname); log_it("resolved %s to %s", incoming, output); } mr_free(curr_fname); mr_free(tmp); return (output); } /* @} - end of deviceGroup */ /** * Return the type of partition format (GPT or MBR) */ char *which_partition_format(const char *drive) { static char output[4]; char *tmp = NULL; char *command = NULL; char *fdisk = NULL; mr_msg(0, "Looking for partition table format type"); mr_asprintf(&fdisk, "/sbin/parted2fdisk"); mr_msg(1, "Using %s", fdisk); mr_asprintf(&command, "%s -l %s | grep 'EFI GPT'", fdisk, drive); mr_free(fdisk); mr_asprintf(&tmp, call_program_and_get_last_line_of_output(command)); mr_free(command); if (strstr(tmp, "GPT") == NULL) { strcpy(output, "MBR"); } else { strcpy(output, "GPT"); } mr_free(tmp); mr_msg(0, "Found %s partition table format type", output); return(output); } /* @} - end of deviceGroup */