]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/installer/hw.c
installer: Allow to start networking without ISO download
[ipfire-2.x.git] / src / installer / hw.c
index 048aeaaf53ded6f9b6ca158a6db7aa7dabca19d6..4e65a8b7a03b59a3568ecedca577ba17120cead4 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/ioctl.h>
 #include <sys/mount.h>
 #include <sys/swap.h>
+#include <sys/sysinfo.h>
 #include <unistd.h>
 
 #include <linux/fs.h>
@@ -48,12 +49,12 @@ const char* other_filesystems[] = {
        NULL
 };
 
-static int system_chroot(const char* path, const char* cmd) {
+static int system_chroot(const char* output, const char* path, const char* cmd) {
        char chroot_cmd[STRING_SIZE];
 
        snprintf(chroot_cmd, sizeof(chroot_cmd), "/usr/sbin/chroot %s %s", path, cmd);
 
-       return mysystem(chroot_cmd);
+       return mysystem(output, chroot_cmd);
 }
 
 struct hw* hw_init() {
@@ -90,7 +91,7 @@ int hw_mount(const char* source, const char* target, const char* fs, int flags)
 }
 
 int hw_umount(const char* target) {
-       return umount2(target, MNT_DETACH);
+       return umount2(target, 0);
 }
 
 static int hw_test_source_medium(const char* path) {
@@ -165,7 +166,7 @@ static unsigned long long hw_block_device_get_size(const char* dev) {
        return size;
 }
 
-struct hw_disk** hw_find_disks(struct hw* hw) {
+struct hw_disk** hw_find_disks(struct hw* hw, const char* sourcedrive) {
        struct hw_disk** ret = hw_create_disks();
        struct hw_disk** disks = ret;
 
@@ -192,15 +193,15 @@ struct hw_disk** hw_find_disks(struct hw* hw) {
                        continue;
                }
 
-               // DEVTYPE must be disk (otherwise we will see all sorts of partitions here)
-               const char* devtype = udev_device_get_property_value(dev, "DEVTYPE");
-               if (devtype && (strcmp(devtype, "disk") != 0)) {
+               // Skip sourcedrive if we need to
+               if (sourcedrive && (strcmp(dev_path, sourcedrive) == 0)) {
                        udev_device_unref(dev);
                        continue;
                }
 
-               // Skip all source mediums
-               if (hw_test_source_medium(dev_path) == 0) {
+               // DEVTYPE must be disk (otherwise we will see all sorts of partitions here)
+               const char* devtype = udev_device_get_property_value(dev, "DEVTYPE");
+               if (devtype && (strcmp(devtype, "disk") != 0)) {
                        udev_device_unref(dev);
                        continue;
                }
@@ -219,6 +220,7 @@ struct hw_disk** hw_find_disks(struct hw* hw) {
                disk->ref = 1;
 
                strncpy(disk->path, dev_path, sizeof(disk->path));
+               const char* p = disk->path + 5;
 
                disk->size = size;
 
@@ -252,15 +254,15 @@ struct hw_disk** hw_find_disks(struct hw* hw) {
 
                if (*disk->vendor && *disk->model) {
                        snprintf(disk->description, sizeof(disk->description),
-                               "%s | %s - %s", size_str, disk->vendor, disk->model);
+                               "%s - %s - %s - %s", size_str, p, disk->vendor, disk->model);
 
                } else if (*disk->vendor || *disk->model) {
                        snprintf(disk->description, sizeof(disk->description),
-                               "%s | %s", size_str, (*disk->vendor) ? disk->vendor : disk->model);
+                               "%s - %s - %s", size_str, p, (*disk->vendor) ? disk->vendor : disk->model);
 
                } else {
                        snprintf(disk->description, sizeof(disk->description),
-                               "%s | N/A", size_str);
+                               "%s - %s", size_str, p);
                }
 
                *disks++ = disk;
@@ -307,7 +309,7 @@ struct hw_disk** hw_select_disks(struct hw_disk** disks, int* selection) {
        unsigned int num_disks = hw_count_disks(disks);
 
        for (unsigned int i = 0; i < num_disks; i++) {
-               if (selection && selection[i]) {
+               if (!selection || selection[i]) {
                        struct hw_disk *selected_disk = disks[i];
                        selected_disk->ref++;
 
@@ -321,6 +323,27 @@ struct hw_disk** hw_select_disks(struct hw_disk** disks, int* selection) {
        return ret;
 }
 
+struct hw_disk** hw_select_first_disk(const struct hw_disk** disks) {
+       struct hw_disk** ret = hw_create_disks();
+       struct hw_disk** selected_disks = ret;
+
+       unsigned int num_disks = hw_count_disks(disks);
+       assert(num_disks > 0);
+
+       for (unsigned int i = 0; i < num_disks; i++) {
+               struct hw_disk *disk = disks[i];
+               disk->ref++;
+
+               *selected_disks++ = disk;
+               break;
+       }
+
+       // Set sentinel
+       *selected_disks = NULL;
+
+       return ret;
+}
+
 static unsigned long long hw_swap_size(struct hw_destination* dest) {
        unsigned long long memory = hw_memory();
 
@@ -356,11 +379,26 @@ static unsigned long long hw_boot_size(struct hw_destination* dest) {
        return MB2BYTES(64);
 }
 
+static int hw_device_has_p_suffix(const struct hw_destination* dest) {
+       // All RAID devices have the p suffix.
+       if (dest->is_raid)
+               return 1;
+
+       // Devices with a number at the end have the p suffix, too.
+       // e.g. mmcblk0, cciss0
+       unsigned int last_char = strlen(dest->path) - 1;
+       if ((dest->path[last_char] >= '0') && (dest->path[last_char] <= '9'))
+               return 1;
+
+       return 0;
+}
+
 static int hw_calculate_partition_table(struct hw_destination* dest) {
        char path[DEV_SIZE];
        int part_idx = 1;
 
-       snprintf(path, sizeof(path), "%s%s", dest->path, (dest->is_raid) ? "p" : "");
+       snprintf(path, sizeof(path), "%s%s", dest->path,
+               hw_device_has_p_suffix(dest) ? "p" : "");
        dest->part_boot_idx = 0;
 
        // Determine the size of the target block device
@@ -486,28 +524,57 @@ struct hw_destination* hw_make_destination(int part_type, struct hw_disk** disks
 }
 
 unsigned long long hw_memory() {
-       FILE* handle = NULL;
-       char line[STRING_SIZE];
+       struct sysinfo si;
 
-       unsigned long long memory = 0;
+       int r = sysinfo(&si);
+       if (r < 0)
+               return 0;
 
-       /* Calculate amount of memory in machine */
-       if ((handle = fopen("/proc/meminfo", "r"))) {
-               while (fgets(line, sizeof(line), handle)) {
-                       if (!sscanf (line, "MemTotal: %llu kB", &memory)) {
-                               memory = 0;
-                       }
-               }
+       return si.totalram;
+}
+
+static int hw_zero_out_device(const char* path, int bytes) {
+       char block[512];
+       memset(block, 0, sizeof(block));
+
+       int blocks = bytes / sizeof(block);
+
+       int fd = open(path, O_WRONLY);
+       if (fd < 0)
+               return -1;
 
-               fclose(handle);
+       unsigned int bytes_written = 0;
+       while (blocks-- > 0) {
+               bytes_written += write(fd, block, sizeof(block));
        }
 
-       return memory * 1024;
+       fsync(fd);
+       close(fd);
+
+       return bytes_written;
 }
 
-int hw_create_partitions(struct hw_destination* dest) {
-       char* cmd = NULL;
+static int try_open(const char* path) {
+       FILE* f = fopen(path, "r");
+       if (f) {
+               fclose(f);
+               return 0;
+       }
+
+       return -1;
+}
+
+int hw_create_partitions(struct hw_destination* dest, const char* output) {
+       // Before we write a new partition table to the disk, we will erase
+       // the first couple of megabytes at the beginning of the device to
+       // get rid of all left other things like bootloaders and partition tables.
+       // This solves some problems when changing from MBR to GPT partitions or
+       // the other way around.
+       int r = hw_zero_out_device(dest->path, MB2BYTES(10));
+       if (r <= 0)
+               return r;
 
+       char* cmd = NULL;
        asprintf(&cmd, "/usr/sbin/parted -s %s -a optimal", dest->path);
 
        // Set partition type
@@ -558,17 +625,17 @@ int hw_create_partitions(struct hw_destination* dest) {
                part_start += dest->size_data;
        }
 
-       if (dest->part_table == HW_PART_TABLE_MSDOS && dest->part_boot_idx > 0) {
+       if (dest->part_boot_idx > 0)
                asprintf(&cmd, "%s set %d boot on", cmd, dest->part_boot_idx);
 
-       } else if (dest->part_table == HW_PART_TABLE_GPT) {
+       if (dest->part_table == HW_PART_TABLE_GPT) {
                if (*dest->part_bootldr) {
                        asprintf(&cmd, "%s set %d bios_grub on", cmd, dest->part_boot_idx);
                }
                asprintf(&cmd, "%s disk_set pmbr_boot on", cmd);
        }
 
-       int r = mysystem(cmd);
+       r = mysystem(output, cmd);
 
        // Wait until the system re-read the partition table
        if (r == 0) {
@@ -577,19 +644,19 @@ int hw_create_partitions(struct hw_destination* dest) {
                while (counter-- > 0) {
                        sleep(1);
 
-                       if (*dest->part_bootldr && (access(dest->part_bootldr, R_OK) != 0))
+                       if (*dest->part_bootldr && (try_open(dest->part_bootldr) != 0))
                                continue;
 
-                       if (*dest->part_boot && (access(dest->part_boot, R_OK) != 0))
+                       if (*dest->part_boot && (try_open(dest->part_boot) != 0))
                                continue;
 
-                       if (*dest->part_swap && (access(dest->part_swap, R_OK) != 0))
+                       if (*dest->part_swap && (try_open(dest->part_swap) != 0))
                                continue;
 
-                       if (*dest->part_root && (access(dest->part_root, R_OK) != 0))
+                       if (*dest->part_root && (try_open(dest->part_root) != 0))
                                continue;
 
-                       if (*dest->part_data && (access(dest->part_data, R_OK) != 0))
+                       if (*dest->part_data && (try_open(dest->part_data) != 0))
                                continue;
 
                        // All partitions do exist, exiting the loop.
@@ -603,7 +670,7 @@ int hw_create_partitions(struct hw_destination* dest) {
        return r;
 }
 
-static int hw_format_filesystem(const char* path, int fs) {
+static int hw_format_filesystem(const char* path, int fs, const char* output) {
        char cmd[STRING_SIZE] = "\0";
 
        // Swap
@@ -628,36 +695,36 @@ static int hw_format_filesystem(const char* path, int fs) {
 
        assert(*cmd);
 
-       int r = mysystem(cmd);
+       int r = mysystem(output, cmd);
 
        return r;
 }
 
-int hw_create_filesystems(struct hw_destination* dest) {
+int hw_create_filesystems(struct hw_destination* dest, const char* output) {
        int r;
 
        // boot
        if (*dest->part_boot) {
-               r = hw_format_filesystem(dest->part_boot, dest->filesystem);
+               r = hw_format_filesystem(dest->part_boot, dest->filesystem, output);
                if (r)
                        return r;
        }
 
        // swap
        if (*dest->part_swap) {
-               r = hw_format_filesystem(dest->part_swap, HW_FS_SWAP);
+               r = hw_format_filesystem(dest->part_swap, HW_FS_SWAP, output);
                if (r)
                        return r;
        }
 
        // root
-       r = hw_format_filesystem(dest->part_root, dest->filesystem);
+       r = hw_format_filesystem(dest->part_root, dest->filesystem, output);
        if (r)
                return r;
 
        // data
        if (*dest->part_data) {
-               r = hw_format_filesystem(dest->part_data, dest->filesystem);
+               r = hw_format_filesystem(dest->part_data, dest->filesystem, output);
                if (r)
                        return r;
        }
@@ -750,6 +817,9 @@ int hw_mount_filesystems(struct hw_destination* dest, const char* prefix) {
 }
 
 int hw_umount_filesystems(struct hw_destination* dest, const char* prefix) {
+       // Write all buffers to disk before umounting
+       hw_sync();
+
        // boot
        if (*dest->part_boot) {
                hw_umount(dest->part_boot);
@@ -780,12 +850,38 @@ int hw_umount_filesystems(struct hw_destination* dest, const char* prefix) {
        return 0;
 }
 
-int hw_setup_raid(struct hw_destination* dest) {
+int hw_destroy_raid_superblocks(const struct hw_destination* dest, const char* output) {
+       char cmd[STRING_SIZE];
+
+       hw_stop_all_raid_arrays(output);
+       hw_stop_all_raid_arrays(output);
+
+       if (dest->disk1) {
+               snprintf(cmd, sizeof(cmd), "/sbin/mdadm --zero-superblock %s", dest->disk1->path);
+               mysystem(output, cmd);
+       }
+
+       if (dest->disk2) {
+               snprintf(cmd, sizeof(cmd), "/sbin/mdadm --zero-superblock %s", dest->disk2->path);
+               mysystem(output, cmd);
+       }
+
+       return 0;
+}
+
+int hw_setup_raid(struct hw_destination* dest, const char* output) {
        char* cmd = NULL;
+       int r;
 
        assert(dest->is_raid);
 
-       asprintf(&cmd, "echo \"y\" | /sbin/mdadm --create --verbose --metadata=1.2 %s", dest->path);
+       // Stop all RAID arrays that might be around (again).
+       // It seems that there is some sort of race-condition with udev re-enabling
+       // the raid arrays and therefore locking the disks.
+       r = hw_destroy_raid_superblocks(dest, output);
+
+       asprintf(&cmd, "echo \"y\" | /sbin/mdadm --create --verbose --metadata=%s --auto=mdp %s",
+               RAID_METADATA, dest->path);
 
        switch (dest->raid_level) {
                case 1:
@@ -798,13 +894,23 @@ int hw_setup_raid(struct hw_destination* dest) {
 
        if (dest->disk1) {
                asprintf(&cmd, "%s %s", cmd, dest->disk1->path);
+
+               // Clear all data at the beginning
+               r = hw_zero_out_device(dest->disk1->path, MB2BYTES(10));
+               if (r <= 0)
+                       return r;
        }
 
        if (dest->disk2) {
                asprintf(&cmd, "%s %s", cmd, dest->disk2->path);
+
+               // Clear all data at the beginning
+               r = hw_zero_out_device(dest->disk2->path, MB2BYTES(10));
+               if (r <= 0)
+                       return r;
        }
 
-       int r = mysystem(cmd);
+       r = mysystem(output, cmd);
        free(cmd);
 
        // Wait a moment until the device has been properly brought up
@@ -816,45 +922,42 @@ int hw_setup_raid(struct hw_destination* dest) {
                        // If the raid device has not yet been properly brought up,
                        // opening it will fail with the message: Device or resource busy
                        // Hence we will wait a bit until it becomes usable.
-                       FILE* f = fopen(dest->path, "r");
-                       if (f) {
-                               fclose(f);
+                       if (try_open(dest->path) == 0)
                                break;
-                       }
                }
        }
 
        return r;
 }
 
-int hw_stop_all_raid_arrays() {
-       return mysystem("/sbin/mdadm --stop --scan");
+int hw_stop_all_raid_arrays(const char* output) {
+       return mysystem(output, "/sbin/mdadm --stop --scan --verbose");
 }
 
-int hw_install_bootloader(struct hw_destination* dest) {
+int hw_install_bootloader(struct hw_destination* dest, const char* output) {
        char cmd[STRING_SIZE];
        int r;
 
        // Generate configuration file
        snprintf(cmd, sizeof(cmd), "/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg");
-       r = system_chroot(DESTINATION_MOUNT_PATH, cmd);
+       r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
        if (r)
                return r;
 
        char cmd_grub[STRING_SIZE];
        snprintf(cmd_grub, sizeof(cmd_grub), "/usr/sbin/grub-install --no-floppy --recheck");
 
-       if (dest->is_raid && (dest->part_table == HW_PART_TABLE_MSDOS)) {
+       if (dest->is_raid) {
                snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->disk1->path);
-               r = system_chroot(DESTINATION_MOUNT_PATH, cmd);
+               r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
                if (r)
                        return r;
 
                snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->disk2->path);
-               r = system_chroot(DESTINATION_MOUNT_PATH, cmd);
+               r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
        } else {
                snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->path);
-               r = system_chroot(DESTINATION_MOUNT_PATH, cmd);
+               r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
        }
 
        return r;
@@ -928,3 +1031,13 @@ int hw_write_fstab(struct hw_destination* dest) {
 
        return 0;
 }
+
+void hw_sync() {
+       sync();
+       sync();
+       sync();
+}
+
+int hw_start_networking(const char* output) {
+       return mysystem(output, "/usr/bin/start-networking.sh");
+}