/* libmondo-devices.c Subroutines for handling devices $Id: libmondo-devices.c 3893 2024-03-13 15:49:30Z bruno $ */ /** * @file * Functions to handle interactions with backup devices. */ #include #include #include "my-stuff.h" #include "mr_mem.h" #include "mr_str.h" #include "mondostructures.h" #include "libmondo-files-EXT.h" #include "libmondo-string-EXT.h" #include "libmondo-tools-EXT.h" #include "libmondo-gui-EXT.h" #include "libmondo-fork-EXT.h" #include "libmondo-stream-EXT.h" #include "newt-specific-EXT.h" #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 3893 2024-03-13 15:49:30Z bruno $"; // extern char *which_compression_type(); /* Do we use extended attributes and acl ? */ extern char *g_getfacl; extern char *g_getfattr; extern int g_current_media_number; extern double g_kernel_version; extern bool g_ISO_restore_mode; extern char *g_selfmounted_isodir; extern char *MONDO_LOGFILE; extern void setup_tmpdir(char *path); extern void setup_scratchdir(char *path); extern char *call_program_and_get_last_line_of_output(const char *); /** * ????? @bug ????? * @ingroup globalGroup */ bool g_restoring_live_from_cd = FALSE; bool g_restoring_live_from_netfs = FALSE; extern t_bkptype g_backup_media_type; // set by main() /* Reference to global bkpinfo */ extern struct s_bkpinfo *bkpinfo; /* Stuff that handles the -I and -E option when a whole disk DSF is used */ typedef struct mounted_fs_struct { char device[MAX_STR_LEN]; /* The name of the device */ char mount_point[MAX_STR_LEN]; /* The devices mount point */ unsigned char check; /* 1 == included on DSF */ struct mounted_fs_struct *next; } MOUNTED_FS_STRUCT; static MOUNTED_FS_STRUCT *DSF_Head = NULL; /* Points to the first entry of mounted_fs_struct list */ static MOUNTED_FS_STRUCT *DSF_Tail = NULL; /* Points to the last entry of mounted_fs_struct list */ /** * Frees the memory for all of the structures on the linked list of * all of the non-NETFS mounted file systems. */ static void free_mounted_fs_list (void) { MOUNTED_FS_STRUCT *DSFptr = NULL; MOUNTED_FS_STRUCT *DSFnext = NULL; DSFptr = DSF_Head; while (DSFptr != NULL) { DSFnext = DSFptr->next; paranoid_free(DSFptr); DSFptr = DSFnext; } DSF_Head = NULL; DSF_Tail = NULL; } /** * Creates a singly linked list of all of the non-NETFS mounted file systems. * @param DSFptr A pointer to the structure MOUNTED_FS_STRUCT used to hold * the list of mounted file systems. * @return None. */ static void add_mounted_fs_struct (MOUNTED_FS_STRUCT *DSFptr) { assert (DSFptr); if (DSF_Head == NULL) { DSF_Head = DSFptr; } else { DSF_Tail->next = DSFptr; } DSFptr->next = NULL; DSF_Tail = DSFptr; } /** * Creates a linked list of all of the non-NETFS mounted file systems. * We use a linked list because we don't know how many mounted file * there are (and there can be a lot). * @return 0 on success and greated than 0 on failure. */ static int create_list_of_non_NETFS_mounted_file_systems (void) { int i = 0; int mount_cnt = 0; size_t lastpos = 0; char *mounted_file_system = NULL; char *command = NULL; char *token = NULL; char token_chars[] =" :\t\r\f\a\0"; MOUNTED_FS_STRUCT *DSFptr = NULL; free_mounted_fs_list(); /******** * Find the number of mounted file system entries and their respective mount points. * I can't return all of the entries as one string because it's length can be longer * than MAX_STR_LEN which is used in call_program_and_get_last_line_of_output(). * So start looping and get the number of mounted file systems and query them one by one. ********/ /* Get the number of mounted file systems ((those that start with "/dev/" */ mr_asprintf(command, "mount 2>/dev/null | awk '{if($1 ~ \"^/dev/\"){print $0}}'|wc -l"); log_msg(5, "Running: %s", command); mounted_file_system = call_program_and_get_last_line_of_output(command); mr_free(command); mount_cnt = atoi(mounted_file_system); log_msg (5, "mount_cnt: %d", mount_cnt); mr_free(mounted_file_system); for (i=mount_cnt; i > 0; i--) { mr_asprintf(command, "mount 2>/dev/null | awk '{if($1 ~ \"^/dev/\"){print $1,$3}}'|head -n %d", i); log_msg(5, "Running: %s", command); mounted_file_system = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg (5, "mounted_file_system: %s", mounted_file_system); if ((token = mr_strtok(mounted_file_system, token_chars, &lastpos)) == NULL) { log_msg (4, "Could not get the list of mounted file systems"); mr_free(mounted_file_system); mr_free(token); return (1); } if (token) { log_msg (5, "token: %s", token); } while (token != NULL) { log_msg (5, "token: %s", token); if ((DSFptr = (MOUNTED_FS_STRUCT *) calloc(1, sizeof(MOUNTED_FS_STRUCT))) == NULL) { fatal_error ("Cannot allocate memory"); } add_mounted_fs_struct(DSFptr); strcpy(DSFptr->device, token); mr_free(token); if ((token = mr_strtok(mounted_file_system, token_chars, &lastpos)) == NULL) { log_msg (5, "Ran out of entries on the mounted file systems list"); mr_free(mounted_file_system); mr_free(token); return (1); } log_msg (5, "token: %s", token); strcpy(DSFptr->mount_point, token); mr_free(token); token = mr_strtok(mounted_file_system, token_chars, &lastpos); } mr_free(mounted_file_system); } return (0); } /** * Find the structure, in the singly linked list of all of the non-NETFS * mounted file systems, that contains the specified mount point. * @param mount_point The mount point to find * @return NULL is it didn't find the mount point, a pointer to the * structure if it did. */ static MOUNTED_FS_STRUCT *find_mount_point_in_list (char *mount_point) { MOUNTED_FS_STRUCT *DSFptr = NULL; DSFptr = DSF_Head; while (DSFptr != NULL) { if (!strcmp(DSFptr->mount_point, mount_point)) { break; } DSFptr = DSFptr->next; } return (DSFptr); } /** * Find the structure, in the singly linked list of all of the non-NETFS * mounted file systems, that contains the specified device. * @param device The device to find * @return NULL if it didn't find the device, a pointer to the * structure if it did. */ static MOUNTED_FS_STRUCT *find_device_in_list(char *device) { MOUNTED_FS_STRUCT *DSFptr = NULL; DSFptr = DSF_Head; while (DSFptr != NULL) { if (!strcmp(DSFptr->device, device)) { break; } DSFptr = DSFptr->next; } return (DSFptr); } /** * Given a whole disk device special file, determine which mounted file systems * are on the dsf's partitions and which mounted file systems are not. * @param dsf The whole disk device special file. * @param included_dsf_list A char pointer used to hold the list of mount points * that are on the dsf. Memory for the array will be allocated within the function. * @param excluded_dsf_list A char pointer used to hold the list of mount points * that are not on the dsf. Memory for the array will be allocated within the function. * @return 0 on success, -1 if no device special file was passed in, -2 if a device * special file was passed in but it has no partitions on it, or 1 on failure */ static int get_dsf_mount_list (const char *dsf, char **included_dsf_list, char **excluded_dsf_list) { int i = 0; int c = 0; size_t lastpos = 0; char *VG = NULL; char *tmp = NULL; char *command = NULL; char *partition_list = NULL; char partitions[64][MAX_STR_LEN]; char *mount_list = NULL; char *token = NULL; char *ndsf = NULL; char token_chars[] =" \t\r\f\a\0"; MOUNTED_FS_STRUCT *DSFptr = NULL; memset((char *)partitions, 0, sizeof(partitions)); log_msg(5, "dsf: %s", dsf); /******** * See if a device special file was passed in (i.e. it must start with /dev/ ********/ if (strncmp(dsf, "/dev/", 5)) { log_msg (4, "%s does not start with /dev/ and (probably) is not a device special file", dsf); return (-1); } log_msg(4, " %s looks like a device special file", dsf); /* Verify that the dsf exists */ mr_asprintf(command, "ls -al %s 2>/dev/null | wc -l", dsf); log_msg(5, " Executing: %s", command); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(5, " Return value: %s", tmp); c = atoi(tmp); mr_free(tmp); if (!c) { log_to_screen("Cannot find device special file %s", dsf); return (1); } log_msg(4, " %s device special file exists", dsf); /* Get a list of the mounted file systems */ if (create_list_of_non_NETFS_mounted_file_systems()) { log_to_screen ("Could not get the list of mounted file systems"); return (1); } log_msg (5, "Processing dsf: %s", dsf); /******** * Get a list of the dsf's partitions. There could be no partitions on the disk * or a dsf of a partition was passed in (e.g. /dev/sda1 instead of /dev/sda). * Either way, it's an error. ********/ mr_asprintf(command, "mr-parted2fdisk -l %s 2>/dev/null|grep -E \"^/dev/\"|awk '{printf(\"%%s \", $1)}END{print \"\"}'", dsf); log_msg(5, "Executing: %s", command); partition_list = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(4, "Partition list for %s: %s", dsf, partition_list); if (!strlen(partition_list)) { /* There were no partitions on the disk */ log_msg(4, "No partitions on device special file %s", dsf); log_msg(4, "I guess it's a partition itself"); strcpy(partitions[0], dsf); ndsf = truncate_to_drive_name(dsf); } else { /* Fill the partition list */ i = 0; lastpos = 0; while ((token = mr_strtok(partition_list, token_chars, &lastpos)) != NULL) { log_msg (4, "Found partition: %s", token); strcpy(partitions[i++], token); mr_free(token); } mr_asprintf(ndsf, "%s", dsf); } mr_free(partition_list); /* In any case we want to exclude the dsf itself from all MondRescue activities * at restore time (LVM, fdisk, ...) so we want it in our exclude_dev list */ if ((DSFptr = (MOUNTED_FS_STRUCT *) calloc(1, sizeof(MOUNTED_FS_STRUCT))) == NULL) { fatal_error ("Cannot allocate memory"); } add_mounted_fs_struct(DSFptr); strcpy(DSFptr->device, dsf); DSFptr->check = 1; /* For the rest ndsf is the new dsf to deal with */ /******** * At this point, we have a list of all of the partitions on the dsf. Now try to * see which partitions have a file system on them. * * Loop through each partition on the disk and: * * - If the partition is swap, it ignores it. * * - If the partition is mounted (e.g. /dev/sda1 is mounted on /boot), it adds an entry * to the linked list, copies to it the device name and mount point, and sets check == 1. * * - If the partition is part of a Volume Group that has Logical Volumes mounted, it adds * an entry to the linked list for each mounted Logical Volume in that Volume Group, copying * to it the device name and mount point, and sets check == 1. Note that if the Volume Group * contains more than one disk, it will still add the entry even if the Logical Volume's * extents are not on the dsf that was passed in to the function. For example, Volume Group * VolGroup00 contains the disks /dev/sda1 and /dev/sdb1, and the Logical Volumes LogVol01, * which is mounted on /var and has all of its extents on /dev/sda1, and LogVol02, which is * mounted as /usr and has all of its extents on /dev/sdb1. If you pass /dev/sda into the * function, both /var and /usr will be archived even though /usr is actually on/dev/sdb. * * - If the partition is part of a Volume Group that has Logical Volumes used in a mounted * software raid device, it adds an entry to the linked list, copies to it the software raid * device name and mount point, and sets check == 1. * * - If the partition is part of a mounted software raid device, it adds an entry to the linked * list, copies to it the software raid device name and mount point, and sets check == 1. * ********/ for (i=0; strlen(partitions[i]); i++) { log_msg(4, "Processing partition: %s", partitions[i]); /* See if it's swap. If it is, ignore it. */ mr_asprintf(command, "mr-parted2fdisk -l %s 2>/dev/null | awk '{if(($1==\"%s\")&&(toupper($0) ~ \"SWAP\")){print $1;exit}}'", ndsf, partitions[i]); log_msg(5, " Running: %s", command); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(5, " Return value: %s", tmp); c = strlen(tmp); mr_free(tmp); if (c) { log_msg(4, "It's swap. Ignoring partition %s", partitions[i]); continue; } /* It's not swap. See if we can find the mount point from the mount command. */ mr_asprintf(command, "mount 2>/dev/null | awk '{if((NF>0)&&($1==\"%s\")){print $3}}'", partitions[i]); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); if (strlen(tmp)) { log_msg(4, " %s is mounted: %s", partitions[i], tmp); if ((DSFptr = find_mount_point_in_list(tmp)) == NULL) { log_msg (4, "Can't find mount point %s in mounted file systems list", tmp); mr_free(tmp); return (1); } DSFptr->check = 1; mr_free(tmp); continue; } mr_free(tmp); /* It's not swap and it's not mounted. See if it's LVM */ log_msg(4, " It's not mounted. Checking to see if it's LVM..."); /* Check for LVM */ mr_asprintf(command, "pvdisplay -c %s 2> /dev/null", partitions[i]); log_msg(5, " Running: %s", command); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); if (strlen(tmp)) { log_msg(4, "Found an LVM partition at %s. Find the VG it's in...", partitions[i]); /* It's LVM: Find the VG it's in */ mr_asprintf(command, "pvdisplay -v %s 2>/dev/null|grep \"VG Name\"|awk '{print $NF}'", partitions[i]); log_msg(5, " Running: %s", command); VG = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(4, " Volume Group: %s", VG); if (strlen(VG)) { /* Found the Volume Group. Now find all of the VG's mount points */ log_msg(4, " Found the Volume Group. Now find all of the VG's mount points"); mr_asprintf(command, "mount 2>/dev/null|grep -E \"/dev/mapper/%s|/dev/%s/\"|awk '{printf(\"%%s \",$3)}END{print \"\"}'", VG, VG); log_msg(5, " Running: %s", command); mount_list = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(4, " VG %s mount_list: %s", VG, mount_list); lastpos = 0; while ((token = mr_strtok(mount_list, token_chars, &lastpos)) != NULL) { log_msg (5, "mount point token: %s", token); if ((DSFptr = find_mount_point_in_list(token)) == NULL) { log_msg (4, "Can't find mount point %s in mounted file systems list", token); mr_free(tmp); mr_free(token); return (1); } DSFptr->check = 1; mr_free(token); } /******** * Now we want to see if there are any software raid devices using * any of the Logical Volumes on the Volume Group. *******/ mr_free(mount_list); mr_asprintf(command, "%s", "cat " MDSTAT_FILE " |grep -iv Personal|awk '{if($0~\"^.*[ ]+:\"){printf(\"/dev/%s \", $1)}}END{print \"\"}'"); log_msg (5, "Running: %s", command); mount_list = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(4, " Software raid device list: %s", mount_list); lastpos = 0; while ((token = mr_strtok(mount_list, token_chars, &lastpos)) != NULL) { mr_asprintf(command, "mdadm --detail %s 2>/dev/null | grep -c %s", token, VG); log_msg (5, "Running: %s", command); mr_free(tmp); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(4, "Number of Software raid device: %s", tmp); if (atoi(tmp)) { /* This device is on our disk */ if ((DSFptr = find_device_in_list(token)) == NULL) { log_msg (4, "Can't find device %s in mounted file systems list", token); mr_free(tmp); mr_free(token); return (1); } DSFptr->check = 1; } } mr_free(token); paranoid_free(mount_list); } else { log_msg (4, "Error finding Volume Group for partition %s", partitions[i]); mr_free(tmp); return (1); } mr_free(tmp); mr_free(VG); continue; } else { log_msg (4, "Error finding partition type for the partition %s", partitions[i]); } mr_free(tmp); /******** * It's not swap, mounted, or LVM. See if it's used in a software raid device. ********/ log_msg (5, "It's not swap, mounted, or LVM. See if it's used in a software raid device."); mr_asprintf(command, "mdadm --examine %s 2>/dev/null | awk '{if($1 == \"UUID\"){print $3}}'", partitions[i]); log_msg(4, " Running: %s", command); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); if (!strlen(tmp)) { log_msg(4, " Partition %s is not used in a non-LVM software raid device", partitions[i]); mr_free(tmp); continue; } log_msg (5, " UUID: %s", tmp); /* Get the Software raid device list */ mr_asprintf(command, "%s", "cat " MDSTAT_FILE " | grep -iv Personal|awk '{if($0~\"^.*[ ]+:\"){printf(\"/dev/%s \", $1)}}END{print \"\"}'"); log_msg (5, " Running: %s", command); mount_list = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(4, " Software raid device list: %s", mount_list); /* Loop through the software raid device list to see if we can find the partition */ lastpos = 0; while ((token = mr_strtok(mount_list, token_chars, &lastpos)) != NULL) { mr_asprintf(command, "mdadm --detail %s 2>/dev/null | grep -c %s", token, tmp); log_msg(4, " Running: %s", command); mr_free(tmp); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); if (!atoi(tmp)) { log_msg (4," Didn't find partition %s in software raid device %s", partitions[i], token); } else { if ((DSFptr = find_device_in_list(token)) == NULL) { log_msg (4, "Can't find device %s in mounted file systems list", token); mr_free(tmp); mr_free(token); return (1); } DSFptr->check = 1; break; } mr_free(token); } mr_free(tmp); mr_free(mount_list); } /* Determine how much memory to allocate for included_dsf_list and excluded_dsf_list */ i = 0; DSFptr= DSF_Head; while (DSFptr != NULL) { i += strlen(DSFptr->mount_point) + 1; DSFptr = DSFptr->next; } log_msg (5, "i: %d", i); if ((*included_dsf_list = (char *) calloc(i+100, sizeof(char))) == NULL) { fatal_error ("Cannot allocate memory"); } if ((*excluded_dsf_list = (char *) calloc(i+100, sizeof(char))) == NULL) { fatal_error ("Cannot allocate memory"); } DSFptr= DSF_Head; while (DSFptr != NULL) { if (DSFptr->check) { log_msg (4, "%s is mounted on %s and is on disk %s", DSFptr->device, DSFptr->mount_point, ndsf); strcat(*included_dsf_list, DSFptr->mount_point); strcat(*included_dsf_list, "|"); } else { log_msg (4, "%s is mounted on %s and is NOT on disk %s", DSFptr->device, DSFptr->mount_point, ndsf); strcat(*excluded_dsf_list, DSFptr->mount_point); strcat(*excluded_dsf_list, "|"); } DSFptr = DSFptr->next; } mr_free(ndsf); log_msg (5, "included_dsf_list: %s", *included_dsf_list); log_msg (5, "excluded_dsf_list: %s", *excluded_dsf_list); 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; if (dev == NULL) { return (1); } #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); } /** * Retract all CD trays and wait for autorun to complete. * @ingroup deviceGroup */ void retract_CD_tray_and_defeat_autorun(void) { // log_it("rctada: Retracting optical tray", __LINE__); if (!bkpinfo->please_dont_eject) { if (bkpinfo->media_device != NULL) { inject_device(bkpinfo->media_device); } } if (!run_program_and_log_output("ps | grep autorun | grep -v grep", 5)) { log_it("autorun detected; sleeping for 2 seconds"); sleep(2); } } /** * Find out what device is mounted as root (/). * @return Root device. * @note The returned string points to storage that needs to be freed by * caller * @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(void) { /*@ buffers **************** */ char *tmp = NULL; #ifdef __FreeBSD__ tmp = call_program_and_get_last_line_of_output("mount | grep \" on / \" | cut -d' ' -f1"); #else 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/")) { mr_free(tmp); tmp = call_program_and_get_last_line_of_output("mount | grep \" on / \" | cut -d' ' -f1 | cut -dp -f1"); } if (strstr(tmp, "/dev/md")) { mr_free(tmp); tmp = call_program_and_get_last_line_of_output("mount | grep \" on / \" | cut -d' ' -f1"); } #endif return (tmp); } /** * 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; bool is_this_a_ramdisk = FALSE; tmp = where_is_root_mounted(); log_msg(0, "root is mounted at %s", tmp); log_msg(0, "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")) { 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; } log_msg(1, "Is this a ramdisk? result = %s", (is_this_a_ramdisk) ? "TRUE" : "FALSE"); return (is_this_a_ramdisk); } /** * Turn @c bkpinfo->backup_media_type into a human-readable string. * @return The human readable string (e.g. @c optical becomes "optical"). * @note The returned string points to static storage that will be overwritten with each call. * @ingroup stringGroup */ static char *bkptype_to_string(t_bkptype bt) { char *output = NULL; switch (bt) { case none: mr_asprintf(output, "%s", "none"); break; case iso: mr_asprintf(output, "%s", "iso"); break; case optical: mr_asprintf(output, "%s", "optical"); break; case netfs: mr_asprintf(output, "%s", "netfs"); break; case tape: mr_asprintf(output, "%s", "tape"); break; case udev: mr_asprintf(output, "%s", "udev"); break; case usb: mr_asprintf(output, "%s", "usb"); break; default: mr_asprintf(output, "%s", "default"); break; } 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 (dev == NULL) { return (1); } 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 log_msg(3, "Ejecting %s", dev); res2 = run_program_and_log_output(command, 1); mr_free(command); if (res1 && res2) { return (1); } else { return (0); } } /** * 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 machine). */ bool does_nonMS_partition_exist(void) { #ifdef __FreeBSD__ return !system("for drive in /dev/ad? /dev/da?; do fdisk $drive | grep -q FreeBSD && exit 0; done; false"); #else return !system("mr-parted2fdisk -l 2>/dev/null | grep '^/dev/' | grep -Eqv '(MS|DOS|EFI|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); #ifdef __FreeBSD__ // We assume here that this is running from mondorestore. (It is.) tmp = build_partition_name(drive, partno); mr_asprintf(program, "ls %s %s >/dev/null 2>&1", drive, tmp); mr_free(tmp); res = system(program); mr_free(program); return(res); #endif mr_asprintf(program, "mr-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); searchstr = build_partition_name(drive, partno); mr_strcat(searchstr, " "); for (res = 0, mr_getline(incoming, fin); !res && !feof(fin) ; mr_getline(incoming, fin)) { if (strstr(incoming, searchstr)) { res = 1; } mr_free(incoming); } mr_free(searchstr); mr_free(incoming); if (pclose(fin)) { log_OS_error("Cannot pclose fin"); } return (res); } /** * Determine whether given NULL-terminated @p str exists in the begining 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 i; assert_string_is_neither_NULL_nor_zerolength(dev); assert_string_is_neither_NULL_nor_zerolength(str); /* For UEFI detection, this should be extended to count=1000 ! */ if (bkpinfo->boot_type == UEFI) { mr_asprintf(command, "dd if=%s bs=446 count=1000 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null", dev, str); } else { mr_asprintf(command, "dd if=%s bs=446 count=1 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null", dev, str); } i = system(command); mr_free(command); if (i) { 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 i; mr_asprintf(command, "dd if=%s bs=512 count=%i 2> /dev/null | strings | grep \"%s\" > /dev/null 2> /dev/null", dev, n, str); i = system(command); mr_free(command); if (i) { return (FALSE); } else { return (TRUE); } } /* * This function tries to find an optical media device * and return it's device file to the caller that needs to free it */ char *find_optical_device(void) { char *dev = NULL; char *tmp1 = NULL; char *command = NULL; log_to_screen("I am looking for your optical burner. Please wait."); if (bkpinfo->media_device != NULL) { log_msg(3, "Been there, done that. Returning %s", bkpinfo->media_device); return (bkpinfo->media_device); } mr_asprintf(tmp1, "cdrecord -inq 2> /dev/null | grep -E '^Detected ' | cut -d':' -f2"); dev = call_program_and_get_last_line_of_output(tmp1); mr_free(tmp1); if ((dev != NULL) && does_file_exist(dev)) { log_msg(2, "find_optical_device found %s automatically", dev); } else { mr_free(dev); tmp1 = find_home_of_exe("lsscsi"); if (tmp1 != NULL) { mr_asprintf(command, "%s | grep ' cd' | awk '{print $NF}' | head -1", tmp1); dev = call_program_and_get_last_line_of_output(command); mr_free(command); } mr_free(tmp1); if ((dev == NULL) || !does_file_exist(dev)) { /* trying something else then */ mr_asprintf(dev, "%s", VANILLA_SCSI_CDROM); if (!does_file_exist(dev)) { mr_asprintf(dev, "%s", ALT_CDROM); if (!does_file_exist(dev)) { mr_asprintf(dev, "%s", "/dev/cdrom"); if (!does_file_exist(dev)) { mr_asprintf(dev, "%s", "/dev/dvd"); if (!does_file_exist(dev)) { log_it("Unable to find a tape device on this system"); } } } } } if (dev != NULL) { log_it("find_optical_device found %s manually", dev); } } if ((dev != NULL) && does_file_exist(dev)) { log_it("find_optical_device returns %s", dev); } else { mr_free(dev); log_it("find_optical_device found no optical burner on your system returning NULL"); } return(dev); } /** * Mount the CD-ROM or USB device at /mnt/cdrom. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->backup_media_type * - @c bkpinfo->disaster_recovery * - @c bkpinfo->isodir * - @c bkpinfo->media_device * @return TRUE for success, FALSE for failure. */ bool mount_media(const char *mountpoint) { char *mount_cmd = NULL; char *mountdir = NULL; char *tmp = NULL; int i = 0, res = 0; #ifdef __FreeBSD__ char mdd[32]; char *mddev = mdd; char *dev; #endif if (bkpinfo->backup_media_type == tape || bkpinfo->backup_media_type == udev) { log_msg(8, "Tape/udev. Therefore, no need to mount a media."); return(TRUE); } assert_string_is_neither_NULL_nor_zerolength(mountpoint); mr_asprintf(tmp, "mount | grep -F %s", mountpoint); if (!run_program_and_log_output(tmp, FALSE)) { log_msg(2, "mount_media() - media already mounted. Fair enough."); mr_free(tmp); return (TRUE); } mr_free(tmp); make_hole_for_dir(mountpoint); if (bkpinfo->backup_media_type == netfs) { log_msg(2, "Mounting for Network thingy"); log_msg(2, "isodir = %s", bkpinfo->isodir); if (((bkpinfo->isodir == NULL) || !strcmp(bkpinfo->isodir, "/")) && am_I_in_disaster_recovery_mode()) { mr_asprintf(bkpinfo->isodir, "%s", "/tmp/isodir"); log_msg(1, "isodir is being set to %s", bkpinfo->isodir); } #ifdef __FreeBSD__ if (bkpinfo->netfs_remote_dir != NULL) { // NETFS mr_asprintf(mount_cmd, "/mnt/isodir/%s/%s/%s-%d.iso", bkpinfo->isodir, bkpinfo->netfs_remote_dir, bkpinfo->prefix, g_current_media_number); } else { // ISO mr_asprintf(mount_cmd, "/mnt/isodir/%s/%s-%d.iso", bkpinfo->isodir, bkpinfo->prefix, g_current_media_number); } mddev = make_vn(mount_cmd); mr_free(mount_cmd); mr_asprintf(mount_cmd, "mount_cd9660 -r %s %s", mddev, mountpoint); #else if (bkpinfo->netfs_remote_dir != NULL) { // NETFS mr_asprintf(mount_cmd, "mount %s/%s/%s-%d.iso -t iso9660 -o loop,ro %s", bkpinfo->isodir, bkpinfo->netfs_remote_dir, bkpinfo->prefix, g_current_media_number, mountpoint); } else { // ISO mr_asprintf(mount_cmd, "mount %s/%s-%d.iso -t iso9660 -o loop,ro %s", bkpinfo->isodir, bkpinfo->prefix, g_current_media_number, mountpoint); } #endif } else if (bkpinfo->backup_media_type == iso) { if (bkpinfo->subdir) { mr_asprintf(mountdir, "%s/%s", bkpinfo->isodir, bkpinfo->subdir); } else { mr_asprintf(mountdir, "%s", bkpinfo->isodir); } #ifdef __FreeBSD__ mr_asprintf(mount_cmd, "%s/%s-%d.iso", mountdir, bkpinfo->prefix, g_current_media_number); mddev = make_vn(mount_cmd); mr_free(mount_cmd); mr_asprintf(mount_cmd, "mount_cd9660 -r %s %s", mddev, mountpoint); #else mr_asprintf(mount_cmd, "mount %s/%s-%d.iso -t iso9660 -o loop,ro %s", mountdir, bkpinfo->prefix, g_current_media_number, mountpoint); #endif mr_free(mountdir); } else if (bkpinfo->backup_media_type == usb) { mr_asprintf(mount_cmd, "mount -t vfat %s %s", bkpinfo->media_device, mountpoint); } else { // optical if (bkpinfo->disaster_recovery && does_file_exist("/tmp/CDROM-LIVES-HERE")) { mr_asprintf(bkpinfo->media_device, "%s", last_line_of_file("/tmp/CDROM-LIVES-HERE")); } else { if (bkpinfo->media_device == NULL) { bkpinfo->media_device = find_optical_device(); } } #ifdef __FreeBSD__ if (g_ISO_restore_mode) { mr_asprintf(dev, "%s", make_vn(bkpinfo->media_device)); if (!dev) { mr_asprintf(command, "Unable to mount ISO (make_vn(%s) failed)", bkpinfo->media_device); fatal_error(command); } mr_free(bkpinfo->media_device); bkpinfo->media_device = dev } mr_asprintf(mount_cmd, "mount_cd9660 -r %s %s 2>> %s", bkpinfo->media_device, mountpoint, MONDO_LOGFILE); #else mr_asprintf(mount_cmd, "mount %s -o loop,ro -t iso9660 %s 2>> %s", bkpinfo->media_device, mountpoint, MONDO_LOGFILE); #endif log_msg(2, "(mount_media) --- command = %s", mount_cmd); // usefull ?? if (strncmp(bkpinfo->media_device, "/dev/", 5) == 0) { retract_CD_tray_and_defeat_autorun(); } } for (i = 0; i < 2; i++) { res = run_program_and_log_output(mount_cmd, FALSE); if (!res) { break; } else { log_msg(2, "Failed to mount device."); sleep(5); sync(); } } mr_free(mount_cmd); if (res) { log_msg(2, "Failed, despite %d attempts", i); return(FALSE); } else { log_msg(2, "Mounted media drive OK"); return(TRUE); } } /************************************************************************** *END_MOUNT_MEDIA * **************************************************************************/ /** * Try to mount CD/DVD at @p mountpoint. If the CD/DVD is not found or has * not been specified, call find_optical_device() to find it. * @param mountpoint Where to mount the CD-ROM. * @return TRUE for success, FALSE for failure. * @see mount_media */ bool find_and_mount_actual_cd(char *mountpoint) { /*@ buffers ***************************************************** */ /*@ int's ****************************************************** */ bool res; /*@ end vars **************************************************** */ assert(bkpinfo != NULL); assert_string_is_neither_NULL_nor_zerolength(mountpoint); if (bkpinfo->media_device == NULL) { bkpinfo->media_device = find_optical_device(); } if (bkpinfo->backup_media_type != iso) { retract_CD_tray_and_defeat_autorun(); } if ((bkpinfo->media_device == NULL) || (res = mount_media(mountpoint))) { mr_free(bkpinfo->media_device); if ((bkpinfo->media_device = mr_popup_and_get_string("CD-ROM device", "Please enter your CD-ROM's /dev device", "/dev/cdrom")) == NULL) { res = TRUE; } else { res = mount_media(mountpoint); } } if (res) { log_msg(1, "mount failed"); } else { log_msg(1, "mount succeeded with %s", bkpinfo->media_device); } return (res); } /* * This function tries to find a USB media device * and return it's device file to the caller that needs to free it */ char *find_usb_device(void) { char *dev = NULL; char *tmp1 = NULL; char *command = NULL; log_to_screen("I am looking for your USB key. Please wait."); if (bkpinfo->media_device != NULL) { log_msg(3, "Been there, done that. Returning %s", bkpinfo->media_device); return (bkpinfo->media_device); } tmp1 = find_home_of_exe("lsscsi"); if (tmp1 != NULL) { mr_asprintf(command, "%s | grep ' disk' | grep USB | awk '{print $NF}' | head -1", tmp1); dev = call_program_and_get_last_line_of_output(command); mr_free(command); } mr_free(tmp1); if ((dev == NULL) || !does_file_exist(dev)) { tmp1 = find_home_of_exe("lsblk"); if (tmp1 != NULL) { mr_asprintf(command, "%s --noheadings --raw --output rm,tran,type,path --sort path | awk '/^1 usb disk/ {d=$4} END {print d}'", tmp1); dev = call_program_and_get_last_line_of_output(command); mr_free(command); } mr_free(tmp1); log_it("Unable to find a tape device on this system"); } if (dev != NULL) { log_it("find_usb_device found %s manually", dev); } if ((dev != NULL) && does_file_exist(dev)) { log_it("find_usb_device returns %s", dev); } else { mr_free(dev); log_it("find_usb_device found no USB key on your system returning NULL"); } return(dev); } /* Generic fund to find a media * Return a dynamically allocted string that caller needs to free * @media_type is the type of media to look for */ char *find_device(t_bkptype media_type) { if (media_type == usb) { return(find_usb_device()); } else if (media_type == tape) { return(find_tape_device()); } else if (media_type == optical) { return(find_optical_device()); } else { return(NULL); } } #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; 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) { log_msg(1, "Error getting size of %s: %s", drive, strerror(errno)); #if linux fileid = open(drive, O_RDONLY); if (fileid != -1) { if (ioctl(fileid, HDIO_GETGEO, &hdgeo) != -1) { if (hdgeo.cylinders && hdgeo.heads && hdgeo.sectors) { cylindersize = hdgeo.heads * hdgeo.sectors / 2; outvalA = cylindersize * cylinders / 1024; log_msg(2, "Got Harddisk geometry, C:%d, H:%d, S:%d", hdgeo.cylinders, hdgeo.heads, hdgeo.sectors); gotgeo = 1; } else { log_msg(1, "Harddisk geometry wrong"); } } else { log_msg(1, "Error in ioctl() getting new hard disk geometry (%s), resizing in unsafe mode", strerror(errno)); } close(fileid); } else { log_msg(1, "Failed to open %s for reading: %s", drive, strerror(errno)); } if (!gotgeo) { log_msg(1, "Failed to get harddisk geometry, using old mode"); } #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; // log_msg (5, "drive = %s, error = %s", drive, strerror (errno)); // fatal_error ("GPSOD: Unable to get size of drive"); log_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 *format_sz = NULL; char *p = NULL; FILE *pin; int retval; 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"); retval = 0; } else { mr_getline(p, pin); good_formats = mr_strip_spaces(p); mr_free(p); (void)pclose(pin); mr_strcat(good_formats, " swap lvm raid ntfs-3g ntfs 7 "); // " ntfs 7 " -- um, cheating much? :) if (strstr(good_formats, format_sz)) { retval = 1; } else { retval = 0; } } 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) { char *tmp = NULL; bool retval = FALSE; mr_asprintf(tmp, "mr-device-mounted %s > /dev/null", device_raw); if (system(tmp) == 0) { retval = TRUE; } mr_free(tmp); return(retval); } #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 = (char *) malloc(MAX_STR_LEN); char *mddevice = NULL; char *command = NULL; char *tmp = NULL; int vndev = 2; tmp = call_program_and_get_last_line_of_output("/sbin/sysctl -n kern.osreldate"); if (atoi(tmp) < 500000) { do { mr_asprintf(mddevice, "vn%ic", vndev++); mr_free(command); mr_asprintf(command, "vnconfig %s %s", mddevice, fname); if (vndev > 10) { mr_free(tmp); mr_free(mddevice); mr_free(command); return NULL; } } while (system(command)); } else { mr_asprintf(command, "mdconfig -a -t vnode -f %s", fname); mddevice = call_program_and_get_last_line_of_output(command); if (!strstr(mddevice, "md")) { mr_free(tmp); mr_free(command); mr_free(mddevice); return NULL; } } mr_free(tmp); mr_free(command); sprintf(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 = NULL; char *tmp = NULL; int res = 0; if (strncmp(dname, "/dev/", 5) == 0) { dname += 5; } tmp = call_program_and_get_last_line_of_output("/sbin/sysctl -n kern.osreldate"); if (atoi(tmp) < 500000) { mr_asprintf(command, "vnconfig -d %s", dname); } else { mr_asprintf(command, "mdconfig -d -u %s", dname); } mr_free(tmp); res = system(command); mr_free(command); return(res); } #endif /** * 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_USB_here(char *device, char *mountpoint) { /*@ buffer ****************************************************** */ char *command = NULL; int retval; 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])) { return(1); } log_msg(4, "(mount_USB_here --- device=%s, mountpoint=%s", device, mountpoint); #ifdef __FreeBSD__ mr_asprintf(command, "mount_vfat %s %s 2>> %s", device, mountpoint, MONDO_LOGFILE); #else mr_asprintf(command, "mount %s -t vfat %s 2>> %s", device, mountpoint, MONDO_LOGFILE); #endif log_msg(4, command); retval = system(command); log_msg(1, "system(%s) returned %d", command, retval); mr_free(command); return (retval); } /** * 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(void) { int cd_number = -1; char *mountdev = NULL; char *tmp = NULL; assert(bkpinfo != NULL); // log_it("Asking what_number_cd_is_this"); if ((g_ISO_restore_mode) || (g_restoring_live_from_cd)) { tmp = call_program_and_get_last_line_of_output("mount | grep iso9660 | awk '{print $3;}'"); mr_asprintf(mountdev, "%s%s", tmp, "/archives/THIS-CD-NUMBER"); mr_free(tmp); cd_number = atoi(last_line_of_file(mountdev)); mr_free(mountdev); return (cd_number); } if ((bkpinfo->media_device == NULL) || !does_file_exist(bkpinfo->media_device)) { log_it("ERROR: bkpinfo->media_device shoulnd't be unaccessible here\n"); /* trying again ! */ bkpinfo->media_device = find_optical_device(); } if ((bkpinfo->media_device == NULL) || !does_file_exist(bkpinfo->media_device)) { fatal_error("ERROR: bkpinfo->media_device shoulnd't really be unaccessible here\n"); } if (!is_this_device_mounted(MNT_CDROM)) { mount_media(MNT_CDROM); } cd_number = atoi(last_line_of_file(MNT_CDROM "/archives/THIS-CD-NUMBER")); return(cd_number); } /** * 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(int cd_number_i_want) { /*@ int ************************************************************* */ int res = 0; /*@ buffers ********************************************************* */ char *tmp = NULL; char *mds = NULL; char *request = NULL; assert(bkpinfo != NULL); assert(cd_number_i_want > 0); // log_msg(3, "Insisting on CD number %d", cd_number_i_want); if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) { log_msg(3, "No need to insist_on_this_cd_number when the backup type isn't CD-R(W) or NFS or ISO"); return; } if (!does_file_exist(MNT_CDROM)) { 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 == netfs) { g_ISO_restore_mode = TRUE; } if ((res = what_number_cd_is_this()) != cd_number_i_want) { log_msg(3, "Currently, we hold %d but we want %d", res, cd_number_i_want); /* Now we need to umount the current media to have the next mounted after */ run_program_and_log_output("umount -d " MNT_CDROM, FALSE); log_msg(3, "Mounting next media %d",cd_number_i_want); g_current_media_number = cd_number_i_want; mount_media(MNT_CDROM); mds = media_descriptor_string(bkpinfo->backup_media_type); log_msg(3, "Insisting on %s #%d", mds, cd_number_i_want); mr_asprintf(request, "Please insert %s #%d and press Enter.", mds, cd_number_i_want); mr_free(mds); while (what_number_cd_is_this() != cd_number_i_want) { sync(); if (is_this_device_mounted(MNT_CDROM)) { res = run_program_and_log_output("umount -d " 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); log_msg(1, "Thankyou. Proceeding..."); g_current_media_number = cd_number_i_want; } } /* Update the bkpinfo structure for exclude & include paths * in order to handle correctly paths corresponding to devices */ void mr_make_devlist_from_pathlist(char *pathlist, char mode) { char *token = NULL; size_t lastpos = 0; char *mounted_on_dsf = NULL; char *not_mounted_on_dsf = NULL; char token_chars[] ="|\t\r\f\a\0\n"; char *tmp = NULL; char *tmp1 = NULL; char *tmp2 = NULL; if (pathlist == NULL) { return; } while ((token = mr_strtok(pathlist, token_chars, &lastpos)) != NULL) { switch (get_dsf_mount_list(token, &mounted_on_dsf, ¬_mounted_on_dsf)) { case 1: if (mode == 'E') { log_msg(1, "WARNING ! %s doesn't exist in -E option", token); } else { log_msg(1, "ERROR ! %s doesn't exist in -I option", token); fatal_error("Error processing -I option"); } break; /* Everything is OK; proceed to archive data */ case 0: if (mode == 'E') { if (strlen(mounted_on_dsf)) { log_to_screen("Excluding the following file systems on %s:", token); log_to_screen("==> %s", mounted_on_dsf); log_msg (5, "Adding to bkpinfo->exclude_paths due to -E option: %s", mounted_on_dsf); if (bkpinfo->exclude_paths) { mr_strcat(bkpinfo->exclude_paths,"|%s",mounted_on_dsf); } else { mr_asprintf(bkpinfo->exclude_paths,"%s",mounted_on_dsf); } if (bkpinfo->exclude_devs) { mr_strcat(bkpinfo->exclude_devs,"|%s",token); } else { mr_asprintf(bkpinfo->exclude_devs,"%s",token); } } } else { log_to_screen("Archiving only the following file systems on %s:", token); log_to_screen("==> %s", mounted_on_dsf); mr_free(bkpinfo->include_paths); mr_asprintf(bkpinfo->include_paths, "%s", "/"); if (strlen(not_mounted_on_dsf)) { log_msg (5, "Adding to bkpinfo->exclude_paths due to -I option: %s", not_mounted_on_dsf); log_to_screen("Not archiving the following file systems:"); log_to_screen("==> %s", not_mounted_on_dsf); if (bkpinfo->exclude_paths) { mr_strcat(bkpinfo->exclude_paths, "|%s",not_mounted_on_dsf); } else { mr_asprintf(bkpinfo->exclude_paths,"%s",not_mounted_on_dsf); } } } break; /* It's a dsf but not a whole disk dsf */ case -2: log_to_screen("Could %s be a partition instead of a whole disk device special file?\nIgnored.", token); break; /* A device special file was not passed in. Process it as a path. */ case -1: /* Adds a | to ensure correct detection even at both ends */ mr_asprintf(tmp1,"|%s",token); mr_asprintf(tmp2,"|%s|",token); if (mode == 'E') { /* Add the token if not already in the list */ mr_asprintf(tmp,"|%s|",bkpinfo->exclude_paths); if (strstr(tmp,tmp2) == NULL) { if (bkpinfo->exclude_paths) { mr_strcat(bkpinfo->exclude_paths,tmp1); mr_free(tmp1); } else { bkpinfo->exclude_paths = tmp1; } } } else { /* Add the token if not already in the list */ mr_asprintf(tmp,"|%s|",bkpinfo->include_paths); if (strstr(tmp,tmp2) == NULL) { mr_strcat(bkpinfo->include_paths, "%s", tmp1); } mr_free(tmp1); } mr_free(tmp); mr_free(tmp2); break; default: fatal_error("Error processing -I or -E option"); break; } mr_free(token); if (bkpinfo->include_paths != NULL) { log_msg(1, "include_paths is now '%s'", bkpinfo->include_paths); } if (bkpinfo->exclude_paths != NULL) { log_msg(1, "exclude_paths is now '%s'", bkpinfo->exclude_paths); } if (bkpinfo->exclude_devs != NULL) { log_msg(1, "exclude_devs is now '%s'", bkpinfo->exclude_devs); } } } /** * Return the type of boot of the system (UEFI or BIOS) */ t_boot mr_boot_type(void) { t_boot ret = BIOS; DIR *fd = NULL; /* Try to detect whether we are in fact in UEFI mode */ fd = opendir("/sys/firmware/efi"); if (fd != NULL) { ret = UEFI; log_msg(2, "UEFI boot mode detected"); closedir(fd); } return(ret); } /** * Find out which boot loader is in use. * @param which_device Device to look for the boot loader on. * @return 'L' for LILO, '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. */ #ifdef __FreeBSD__ char which_boot_loader(const char *which_device) { 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 andand %d boot0s and %d DD\n", count_grubs, count_lilos, 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 char which_boot_loader(const char *which_device) { /*@ buffer ***************************************************** */ char *list_drives_cmd = NULL; char *current_drive; char *tmp; /*@ pointers *************************************************** */ FILE *pdrives; /*@ int ******************************************************** */ int count_lilos = 0; int count_grubs = 0; /*@ end vars *************************************************** */ if (bkpinfo->boot_type == UEFI) { /* hardcoded for now. We can for sure do a better job here ! */ /* RHEL, SLES, Mageia, Debian, Ubuntu use grub as boot loader as it seems for UEFI */ return ('G'); } assert(which_device != NULL); tmp = where_is_root_mounted(); mr_asprintf(list_drives_cmd, "mr-parted2fdisk -l 2>/dev/null | grep \"/dev/.*:\" | tr -s ':' ' ' | tr -s ' ' '\n' | grep /dev/; echo %s", tmp); log_it("list_drives_cmd = %s", list_drives_cmd); mr_free(tmp); 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); malloc_string(current_drive); if (bkpinfo->boot_type == BIOS) { for (tmp = fgets(current_drive, MAX_STR_LEN, pdrives); !feof(pdrives) && (tmp != NULL); tmp = fgets(current_drive, MAX_STR_LEN, pdrives)) { 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; } } 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) { paranoid_free(current_drive); return ('G'); } else if (count_lilos && !count_grubs) { paranoid_free(current_drive); return ('L'); } else if (count_grubs == 1 && count_lilos == 1) { log_it("I'll bet you used to use LILO but switched to GRUB..."); paranoid_free(current_drive); return ('G'); } else { // We need to look on each partition then mr_asprintf(list_drives_cmd, "mr-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); paranoid_free(current_drive); return ('\0'); } mr_free(list_drives_cmd); for (tmp = fgets(current_drive, MAX_STR_LEN, pdrives); !feof(pdrives) && (tmp != NULL); tmp = fgets(current_drive, MAX_STR_LEN, pdrives)) { 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; } } if (pclose(pdrives)) { log_OS_error("Cannot pclose pdrives"); } log_it("%d grubs and %d lilos\n", count_grubs, count_lilos); paranoid_free(current_drive); 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 /** * Get a |-separated list of NETFS mounts. * @return The list created. * @note The return value points to allocated string that needs to be freed by * caller * @bug Even though we only want the mounts, the devices are still checked. */ char *list_of_NETFS_mounts_only(void) { char *exclude_these_directories = NULL; exclude_these_directories = call_program_and_get_last_line_of_output("mount -t coda,ncpfs,fuse.sshfs,nfs,nfs4,vmhgfs,smbfs,cifs,afs,gfs,ocfs,ocfs2,mvfs,nsspool,nssvol,fuse.boostfs | tr -s '\t' ' ' | cut -d' ' -f3 | tr -s '\n' '|' | awk '{print $0;}'"); log_msg(9,"list_of_NETFS_mounts_only returns %s",exclude_these_directories); return(exclude_these_directories); } /* @} - end of utilityGroup */ /** * 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(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 = NULL; char *tmpro = NULL; char *tmp1 = NULL; char *tmp2 = NULL; char *mds = NULL; char *oldtmp = NULL; char *q = NULL; char p[16*MAX_STR_LEN]; char *sz_size = NULL; char *command = NULL; char *compression_type = NULL; char *comment = NULL; char i = '\0'; FILE *fin; malloc_string(tmp1); assert(bkpinfo != NULL); // Tape, CD, NETFS, ...? srandom(getpid()); bkpinfo->backup_media_type = (g_restoring_live_from_cd) ? optical : which_backup_media_type(bkpinfo->restore_data); if (bkpinfo->backup_media_type == none) { log_to_screen("User has chosen not to backup the machine"); finish(1); } tmp = bkptype_to_string(bkpinfo->backup_media_type); log_msg(3, "media type = %s", tmp); mr_free(tmp); /* Why asking to remove the media with tape ? if (bkpinfo->backup_media_type == tape && bkpinfo->restore_data) { popup_and_OK("Please remove media from drive(s)"); } */ if (archiving_to_media) { // TODO: Should be common ? setup_tmpdir(NULL); /* * Set the scratchdir to reside on the partition with the most free space. * Automatically excludes DOS, NTFS, SMB, and NFS filesystems. */ #ifdef __FreeBSD__ tmp = call_program_and_get_last_line_of_output("df -m -P -t nonfs,msdosfs,ntfs,ntfs-3g,vmhgfs,smbfs,smb,cifs,afs,gfs,ocfs,ocfs2,mvfs,nsspool,nssvol | grep -vE \"none|Filesystem\" | awk '{printf \"%s %s\\n\", $4, $6;}' | sort -nr | awk '{print $NF;}' | while read x ; do test -w $x && echo $x && break ; done"); #else tmp = call_program_and_get_last_line_of_output("df -m -P -x nfs -x nfs4 -x fuse.sshfs -x fuse -x vfat -x ntfs -x ntfs-3g -x vmhgfs -x smbfs -x smb -x cifs -x afs -x gfs -x ocfs -x ocfs2 -x mvfs -x nsspool -x nssvol -x iso9660 | grep -vE \"none|Filesystem|/dev/shm\" | awk '{printf \"%s %s\\n\", $4, $6;}' | sort -nr | awk '{print $NF;}' | while read x ; do test -w $x && echo $x && break ; done"); #endif if (tmp[0] != '/') { mr_asprintf(sz, "%s", tmp); mr_free(tmp); mr_asprintf(tmp, "/%s", sz); mr_free(sz); } setup_scratchdir(tmp); mr_free(tmp); if ((compression_type = which_compression_type()) == NULL) { log_to_screen("User has chosen not to backup the machine"); finish(1); } bkpinfo->compression_level = 5; if ((bkpinfo->compression_level = which_compression_level()) == -1) { log_to_screen("User has chosen not to backup the machine"); finish(1); } bkpinfo->use_lzo = FALSE; } mvaddstr_and_log_it(2, 0, " "); // Find device's /dev (or SCSI) entry switch (bkpinfo->backup_media_type) { case optical: case usb: /* Never try to eject a USB device */ if (bkpinfo->backup_media_type == usb) { bkpinfo->please_dont_eject = TRUE; } mds = media_descriptor_string(bkpinfo->backup_media_type); if (bkpinfo->backup_media_type != usb) { if (ask_me_yes_or_no("Is your computer a laptop type (manual insert of MondoRescue media)?")) { bkpinfo->manual_cd_tray = TRUE; } if (ask_me_yes_or_no("Do you want to blank your media before burning them (RW type of media)?")) { bkpinfo->wipe_media_first = TRUE; } } if (bkpinfo->media_device == NULL) { bkpinfo->media_device = find_device(bkpinfo->backup_media_type); if (bkpinfo->media_device != NULL) { log_msg(1, "MondoRescue device found automatically for your %s is %s", mds, bkpinfo->media_device); } else { log_msg(1, "No MondoRescue device found yet for your %s", mds); } } if (archiving_to_media) { if (bkpinfo->backup_media_type == optical) { mr_asprintf(sz_size, "%d", DEFAULT_DVD_DISK_SIZE); // 4.7 salesman's GB = 4.482 real GB = 4482 MB log_msg(1, "Setting to DVD defaults ~4.4GB"); } else { mr_asprintf(sz_size, "%d", 0); // No value for USB } mr_asprintf(comment, "How much data (in Megabytes) will each %s store?", mds); tmp1 = mr_popup_and_get_string("Size", comment, sz_size); if (!tmp1) { log_to_screen("User has chosen not to backup the machine"); finish(1); } else { bkpinfo->media_size = atoi(tmp1); } mr_free(tmp1); if (bkpinfo->media_size <= 0) { log_to_screen("User has chosen not to backup the machine"); finish(1); } } // If media_device not found ask if (bkpinfo->media_device == NULL) { mr_asprintf(comment, "Please specify your Mondorescue %s media /dev entry", mds); tmp2 = mr_popup_and_get_string("/dev entry?", comment, bkpinfo->media_device); if (!tmp2) { log_to_screen("User has chosen not to backup the machine"); finish(1); } else { mr_free(bkpinfo->media_device); bkpinfo->media_device = tmp2; } } log_msg(2, "%s device found at %s", mds, bkpinfo->media_device); mr_asprintf(tmp, "MondoRescue thinks your %s media corresponds to %s. Is this correct?", mds, bkpinfo->media_device); if (!ask_me_yes_or_no(tmp)) { mr_free(bkpinfo->media_device); } mr_free(tmp); if (bkpinfo->media_device == NULL) { mr_asprintf(tmp, "Please then specify your Mondorescue %s media /dev entry", mds); tmp2 = mr_popup_and_get_string("/dev entry?", tmp, bkpinfo->media_device); mr_free(tmp); if (tmp2 == NULL) { log_to_screen("User has chosen not to backup the machine"); finish(1); } else { mr_free(bkpinfo->media_device); bkpinfo->media_device = tmp2; } } mr_free(mds); 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 machine"); finish(1); } break; case tape: bkpinfo->media_device = find_tape_device(); if (bkpinfo->media_device != NULL) { if ((fin = fopen(bkpinfo->media_device, "r"))) { paranoid_fclose(fin); } else { if (does_file_exist("/tmp/mondorestore.cfg")) { read_cfg_var("/tmp/mondorestore.cfg", "media-dev", bkpinfo->media_device); } } } if (bkpinfo->media_device != NULL) { mr_asprintf(tmp, "Mondorescue thinks your tape streamer at %s; is that correct?", bkpinfo->media_device); if (!ask_me_yes_or_no(tmp)) { mr_free(bkpinfo->media_device); } mr_free(tmp); } if (bkpinfo->media_device == NULL) { tmp2 = mr_popup_and_get_string("Device name?", "What is the /dev entry of your tape streamer?", ""); if (tmp2 == NULL) { log_to_screen("User has chosen not to backup the machine"); finish(1); } else { bkpinfo->media_device = tmp2; } } 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_asprintf(sz_size,"%ld",bkpinfo->internal_tape_block_size); tmp = mr_popup_and_get_string("Tape block size?", "What is the block size of your tape streamer?", sz_size); if (tmp == NULL) { log_to_screen("User has chosen not to backup the machine"); finish(1); } bkpinfo->internal_tape_block_size = atol(tmp); mr_free(sz_size); mr_free(tmp); // log the block-size log_msg(0,"Tape block size= %ld", bkpinfo->internal_tape_block_size); if (bkpinfo->internal_tape_block_size <= 0) { log_to_screen("User has chosen not to backup the machine"); finish(1); } bkpinfo->media_size = 0; log_msg(4, "media_size = %ld", bkpinfo->media_size); if (archiving_to_media) { bkpinfo->use_obdr = ask_me_yes_or_no("Do you want to activate OBDR support for your tapes ?"); if (bkpinfo->use_obdr) { log_msg(4, "obdr mode = TRUE"); } else { log_msg(4, "obdr mode = FALSE"); } } break; case netfs: /* Never try to eject a NETFS device */ bkpinfo->please_dont_eject = TRUE; /* Force NFS to be the protocol by default */ if (bkpinfo->netfs_proto == NULL) { mr_asprintf(bkpinfo->netfs_proto, "nfs"); } /* Initiate bkpinfo netfs_mount path from running environment if not already done */ if (bkpinfo->netfs_mount == NULL) { bkpinfo->netfs_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("Network shared 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.)", p,(MAX_STR_LEN / 4)-1)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->netfs_mount); // check whether already mounted - we better remove // surrounding spaces and trailing '/' for this bkpinfo->netfs_mount = mr_strip_spaces(p); if (bkpinfo->netfs_mount[strlen(bkpinfo->netfs_mount) - 1] == '/') bkpinfo->netfs_mount[strlen(bkpinfo->netfs_mount) - 1] = '\0'; q = strchr(bkpinfo->netfs_mount, '@'); if (q != NULL) { /* User found. Store the 2 values */ q++; /* new netfs mount */ strcpy(tmp1,q); } else { strcpy(tmp1,bkpinfo->netfs_mount); } mr_asprintf(command, "mount | grep \"%s \" | cut -d' ' -f3", tmp1); mr_free(bkpinfo->isodir); bkpinfo->isodir = call_program_and_get_last_line_of_output(command); mr_free(command); if (!bkpinfo->restore_data) { mr_asprintf(sz_size, "%d", DEFAULT_DVD_DISK_SIZE); // 4.7 salesman's GB = 4.482 real GB = 4482 MB mr_asprintf(comment, "How much data (in Megabytes) will each media store?"); strcpy(tmp1, sz_size); mr_free(sz_size); if (!popup_and_get_string("Size", comment, tmp1, 5)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(comment); mr_asprintf(sz_size, "%s", tmp1); } else { mr_asprintf(sz_size, "0"); } bkpinfo->media_size = atoi(sz_size); mr_free(sz_size); if (bkpinfo->media_size < 0) { log_to_screen("User has chosen not to backup the machine"); finish(1); } } /* TODO: Useless I think */ if (bkpinfo->disaster_recovery) { mr_asprintf(command ,"umount %s/isodir 2> /dev/null", bkpinfo->tmpdir); paranoid_system(command); mr_free(command); } strcpy(tmp1, bkpinfo->netfs_proto); if (!popup_and_get_string("Network protocol", "Which protocol should I use (nfs/sshfs/smbfs) ?",tmp1, MAX_STR_LEN-1)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->netfs_proto); mr_asprintf(bkpinfo->netfs_proto, "%s", tmp1); strcpy(tmp1, bkpinfo->netfs_mount); if (!popup_and_get_string("Network share", "Which remote share should I mount?", tmp1, MAX_STR_LEN-1)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->netfs_mount); mr_asprintf(bkpinfo->netfs_mount, "%s", tmp1); if (bkpinfo->netfs_user) { strcpy(tmp1, bkpinfo->netfs_user); } else { strcpy(tmp1, ""); } if (!popup_and_get_string("Network user", "Which user should I use if any ?",tmp1, MAX_STR_LEN-1)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->netfs_user); if (strcmp(tmp1, "") != 0) { mr_asprintf(bkpinfo->netfs_user, "%s", tmp1); } /* Initiate bkpinfo isodir path from running environment if mount already done */ log_msg(3, "Testing mount for %s", bkpinfo->netfs_mount); if (is_this_device_mounted(bkpinfo->netfs_mount)) { mr_free(bkpinfo->isodir); bkpinfo->isodir = call_program_and_get_last_line_of_output("mount | grep \":\" | cut -d' ' -f3 | head -n1"); } else { // Why netfsdir ? mr_asprintf(bkpinfo->isodir, "%s/netfsdir", bkpinfo->tmpdir); mr_asprintf(command, "mkdir -p %s", bkpinfo->isodir); run_program_and_log_output(command, 5); mr_free(command); if (bkpinfo->restore_data) { /* mount the FS read-only in restore mode to avoid any erase of whatever */ mr_asprintf(tmpro, "-o ro"); } else { mr_asprintf(tmpro, ""); } /* Build the mount string */ if (strstr(bkpinfo->netfs_proto, "smbfs")) { mr_asprintf(tmp, "mount -t cifs %s %s %s",bkpinfo->netfs_mount, bkpinfo->isodir,tmpro); if (bkpinfo->netfs_user) { mr_strcat(tmp, " -o user=%s", bkpinfo->netfs_user); } else { if (strstr(bkpinfo->netfs_proto, "sshfs")) { mr_asprintf(tmp, "sshfs %s ",tmpro); } else { mr_asprintf(tmp, "mount -t %s -o nolock %s ", bkpinfo->netfs_proto,tmpro); } if (bkpinfo->netfs_user) { mr_strcat(tmp, "%s@", bkpinfo->netfs_user); } mr_strcat(tmp, "%s %s", bkpinfo->netfs_mount, bkpinfo->isodir); } run_program_and_log_output(tmp, 3); mr_free(tmp); malloc_string(g_selfmounted_isodir); strcpy(g_selfmounted_isodir, bkpinfo->isodir); } } log_msg(3, "Testing mount for %s", bkpinfo->netfs_mount); if (!is_this_device_mounted(bkpinfo->netfs_mount)) { popup_and_OK("Please mount that partition before you try to backup to or restore from it."); finish(1); } if (bkpinfo->netfs_remote_dir == NULL) { fatal_error("bkpinfo->netfs_remote_dir should not be NULL"); } strcpy(tmp1, bkpinfo->netfs_remote_dir); if (!popup_and_get_string ("Directory", "Which directory within that mountpoint?", tmp1, MAX_STR_LEN-1)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->netfs_remote_dir); // check whether writable - we better remove surrounding spaces for this bkpinfo->netfs_remote_dir = mr_strip_spaces(tmp1); tmp = mr_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); if (tmp == NULL) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->prefix); bkpinfo->prefix = tmp; log_msg(3, "prefix set to %s", bkpinfo->prefix); log_msg(3, "Just set netfs_remote_dir to %s", bkpinfo->netfs_remote_dir); log_msg(3, "isodir is still %s", bkpinfo->isodir); break; case iso: if (!bkpinfo->disaster_recovery) { tmp = mr_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); if (tmp == NULL) { log_to_screen("User has chosen not to backup the machine"); finish(1); } else { mr_free(bkpinfo->isodir); bkpinfo->isodir = tmp; } if (archiving_to_media) { sprintf(tmp1, "%d", DEFAULT_DVD_DISK_SIZE); // 4.7 salesman's GB = 4.482 real GB = 4482 MB 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 (700) or DVD's (4480) you plan to backup to.", tmp1, 16)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } bkpinfo->media_size = atoi(tmp1); } else { bkpinfo->media_size = 650; } } tmp = mr_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); if (tmp == NULL) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->prefix); bkpinfo->prefix = tmp; log_msg(3, "prefix set to %s", bkpinfo->prefix); break; default: case none: fatal_error("Unknown backup_media_type"); break; } if (archiving_to_media) { /* Needs to be done before calling which_boot_loader */ bkpinfo->boot_type = mr_boot_type(); mr_free(bkpinfo->boot_device); #ifdef __FreeBSD__ bkpinfo->boot_device = call_program_and_get_last_line_of_output("mount | grep ' / ' | head -1 | cut -d' ' -f1 | sed 's/\\([0-9]\\).*/\\1/'"); #else 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)-1)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } i = which_boot_loader(bkpinfo->boot_device); #else strcpy(tmp1, bkpinfo->boot_device); if (!popup_and_get_string("Boot device", "What is your boot device? (e.g. /dev/hda)", tmp1,(MAX_STR_LEN / 4)-1)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->boot_device); mr_asprintf(bkpinfo->boot_device, "%s", tmp1); if (does_string_exist_in_boot_block(bkpinfo->boot_device, "LILO")) { i = 'L'; } 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; /* TODO: Check consistency of boot type and boot loader */ if (bkpinfo->include_paths) { strcpy(tmp1, bkpinfo->include_paths); mr_free(bkpinfo->include_paths); } else { strcpy(tmp1, "/"); } if (!popup_and_get_string("Backup paths", "Please enter paths (separated by '|') which you want me to backup. The default is '/' (i.e. everything).", tmp1, MAX_STR_LEN-1)) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_asprintf(bkpinfo->include_paths, "%s", tmp1); tmp = list_of_NETFS_mounts_only(); if (strlen(tmp) > 2) { mr_strcat(bkpinfo->exclude_paths, "|%s",tmp); } mr_free(tmp); // NTFS tmp = call_program_and_get_last_line_of_output("mr-parted2fdisk -l 2>/dev/null | grep -i ntfs | awk '{ print $1};' | tr -s '\\n' ' ' | awk '{ print $0};'"); if (strlen(tmp) > 2) { tmp1 = mr_popup_and_get_string("NTFS partitions", "Please enter/confirm the NTFS partitions you wish to backup as well.", tmp); if (tmp1 == NULL) { log_to_screen("User has chosen not to backup the machine"); mr_free(tmp); mr_free(tmp1); finish(1); } mr_free(bkpinfo->image_devs); bkpinfo->image_devs = tmp1; } mr_free(tmp); if (bkpinfo->exclude_paths != NULL ) { mr_asprintf(p, "%s", bkpinfo->exclude_paths); } else { mr_asprintf(p, "%s", ""); } tmp = mr_popup_and_get_string("Exclude paths", "Please enter paths which you do NOT want to backup. Separate them with '|'. NB: /tmp and /proc are always excluded. :-) Just hit 'Enter' if you want to do a full system backup.", p); mr_free(p); if (tmp == NULL) { log_to_screen("User has chosen not to backup the machine"); finish(1); } mr_free(bkpinfo->exclude_paths); bkpinfo->exclude_paths = tmp; mr_asprintf(oldtmp, "%s", bkpinfo->tmpdir); if (bkpinfo->tmpdir != NULL ) { mr_asprintf(p, "%s", bkpinfo->tmpdir); } else { mr_asprintf(p, "%s", ""); } tmp = mr_popup_and_get_string("Temporary directory", "Please enter your temporary directory.", p); mr_free(p); if (tmp == NULL) { mr_free(oldtmp); log_to_screen("User has chosen not to backup the machine"); finish(1); } /* if modified to another path */ if (strcmp(tmp,oldtmp) != 0) { setup_tmpdir(tmp); } mr_free(oldtmp); mr_asprintf(oldtmp, "%s", bkpinfo->scratchdir); if (bkpinfo->scratchdir != NULL ) { mr_asprintf(p, "%s", bkpinfo->scratchdir); } else { mr_asprintf(p, "%s", ""); } tmp = mr_popup_and_get_string("Scratch directory", "Please enter your scratch directory.", p); mr_free(p); if (tmp == NULL) { mr_free(oldtmp); log_to_screen("User has chosen not to backup the machine"); finish(1); } /* if modified to another path */ if (strcmp(tmp,oldtmp) != 0) { setup_scratchdir(tmp); } mr_free(oldtmp); if (ask_me_yes_or_no("Do you want to backup extended attributes?")) { mr_free(g_getfattr); g_getfattr = find_home_of_exe("getfattr"); mr_free(g_getfacl); g_getfacl = find_home_of_exe("getfacl"); log_it("Backup of extended attributes"); } // Interactive mode: bkpinfo->backup_data = TRUE; if (strcmp(compression_type,"lzo") == 0) { mr_asprintf(bkpinfo->zip_exe, "%s", "lzop"); mr_asprintf(bkpinfo->zip_suffix, "%s", "lzo"); } else if (strcmp(compression_type,"gzip") == 0) { mr_asprintf(bkpinfo->zip_exe, "%s", "gzip"); mr_asprintf(bkpinfo->zip_suffix, "%s", "gz"); } else if (strcmp(compression_type,"lzma") == 0) { mr_asprintf(bkpinfo->zip_exe, "%s", "xz"); mr_asprintf(bkpinfo->zip_suffix, "%s", "xz"); } else if (strcmp(compression_type,"bzip2") == 0) { mr_asprintf(bkpinfo->zip_exe, "%s", "bzip2"); mr_asprintf(bkpinfo->zip_suffix, "%s", "bz2"); } else { mr_free(bkpinfo->zip_exe); mr_free(bkpinfo->zip_suffix); } mr_free(compression_type); #ifdef __FreeBSD__ #if __FreeBSD__ == 5 mr_asprintf(bkpinfo->kernel_path, "%s", "/boot/kernel/kernel"); #elif __FreeBSD__ == 4 mr_asprintf(bkpinfo->kernel_path, "%s", "/kernel"); #endif #elif linux if (figure_out_kernel_path_interactively_if_necessary(bkpinfo->kernel_path)) { fatal_error("Kernel not found. Please specify manually with the '-k' switch."); } #else #error "I don't know about this system!" #endif bkpinfo->verify_data = ask_me_yes_or_no ("Will you want to verify your backups after Mondo has created them?"); 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 machine"); finish(1); } } else { bkpinfo->restore_data = TRUE; // probably... } if (bkpinfo->backup_media_type == iso || bkpinfo->backup_media_type == netfs) { g_ISO_restore_mode = TRUE; } #ifdef __FreeSD__ // skip #else if (bkpinfo->backup_media_type == netfs) { log_msg(3, "I think the Remote mount is mounted at %s", bkpinfo->isodir); } log_it("isodir = %s", bkpinfo->isodir); if (bkpinfo->netfs_mount) { log_it("netfs_mount = '%s'", bkpinfo->netfs_mount); } if (bkpinfo->netfs_user) { log_it("netfs_user = '%s'", bkpinfo->netfs_user); } if (bkpinfo->netfs_proto) { log_it("netfs_proto = '%s'", bkpinfo->netfs_proto); } #endif log_it("media device = %s", bkpinfo->media_device); log_it("media size = %ld", bkpinfo->media_size); tmp = bkptype_to_string(bkpinfo->backup_media_type); log_it("media type = %s", tmp); mr_free(tmp); if (bkpinfo->prefix != NULL) { log_it("prefix = %s", bkpinfo->prefix); } log_it("compression = %ld", bkpinfo->compression_level); log_it("exclude_path = %s", bkpinfo->exclude_paths); if (bkpinfo->include_paths) { log_it("include_path = %s", bkpinfo->include_paths); } /* Handle devices passed in bkpinfo and print result */ /* the mr_make_devlist_from_pathlist function appends */ /* to the *_paths variables so copy before */ mr_make_devlist_from_pathlist(bkpinfo->exclude_paths, 'E'); mr_make_devlist_from_pathlist(bkpinfo->include_paths, 'I'); log_it("scratchdir = '%s'", bkpinfo->scratchdir); log_it("tmpdir = '%s'", bkpinfo->tmpdir); if (bkpinfo->image_devs) { 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 < 0) { if (archiving_to_media) { fatal_error("Media size is less than zero."); } else { log_msg(2, "Warning - media size is less than zero."); bkpinfo->media_size = 0; } } paranoid_free(sz_size); paranoid_free(tmp1); return (0); } /** * Create a randomly-named FIFO. The format is @p stub "." [random] [random] where * [random] is a random number between 1 and 32767. * @param store_name_here Where to store the new filename. * @param stub A random number will be appended to this to make the FIFO's name. * @ingroup deviceGroup */ void make_fifo(char *store_name_here, char *stub) { char *tmp = NULL; assert_string_is_neither_NULL_nor_zerolength(stub); sprintf(store_name_here, "%s%d%d", stub, (int) (random() % 32768), (int) (random() % 32768)); make_hole_for_file(store_name_here); mkfifo(store_name_here, S_IRWXU | S_IRWXG); mr_asprintf(tmp, "chmod 770 %s", store_name_here); paranoid_system(tmp); mr_free(tmp); } /** * @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; bool ret=FALSE; if (!dev || dev[0] == '\0') { output[0] = '\0'; return(ret); } // assert_string_is_neither_NULL_nor_zerolength(dev); if (!bkpinfo->please_dont_eject) { log_msg(10, "Injecting %s", dev); inject_device(dev); } if (!does_file_exist(dev)) { log_msg(10, "%s doesn't exist. Returning FALSE.", dev); return(ret); } 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); log_msg(4, "Found it - %s", dev); ret = TRUE; } else { output[0] = '\0'; log_msg(4, "It's not %s", dev); } mr_free(command); return(ret); } /** * 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; struct stat statbuf; 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)) { log_msg(1, "curr_fname = %s", curr_fname); mr_asprintf(command, "file %s", curr_fname); tmp = call_program_and_get_last_line_of_output(command); mr_free(command); for (p = tmp + strlen(tmp); p != tmp && *p != '`' && *p != ' '; p--); p++; mr_asprintf(scratch, "%s", p); for (p = scratch; *p != '\0' && *p != '\''; p++); *p = '\0'; log_msg(0, "curr_fname %s --> '%s' --> %s", curr_fname, tmp, scratch); mr_free(tmp); 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); } mr_free(scratch); lstat(curr_fname, &statbuf); } strcpy(output, curr_fname); log_it("resolved %s to %s", incoming, output); } paranoid_free(curr_fname); return (output); } /* @} - end of deviceGroup */ /** * Return the type of partition format (GPT or MBR) */ char *which_partition_format(const char *drive) { char *output = NULL; char *command = NULL; mr_asprintf(command, "mr-disk-type %s", drive); output = call_program_and_get_last_line_of_output(command); mr_free(command); log_msg(0, "Found %s partition table format type on %s", output, drive); return (output); } /* @} - end of deviceGroup */