X-Git-Url: http://git.ipfire.org/?p=ipfire-2.x.git;a=blobdiff_plain;f=src%2Finstaller%2Fmain.c;h=6e151fbd55119bf837abea78042be8adbc7e5675;hp=e5da6ec9baf205a3d94ea6defabe8688cd3bc19d;hb=bb75bc3b140749a736040d767ff89d25177ae330;hpb=7f6e04258a78b4b58cebbb434ece7ed26d8ce1bb diff --git a/src/installer/main.c b/src/installer/main.c index e5da6ec9ba..6e151fbd55 100644 --- a/src/installer/main.c +++ b/src/installer/main.c @@ -7,24 +7,25 @@ * Contains main entry point, and misc functions.6 * */ +#define _GNU_SOURCE #include #include +#include #include #include #include #include #include "hw.h" -#include "install.h" // Translation #include #define _(x) dgettext("installer", x) #define INST_FILECOUNT 21000 -#define UNATTENDED_CONF "/cdrom/boot/unattended.conf" #define LICENSE_FILE "/cdrom/COPYING" +#define SOURCE_TEMPFILE "/tmp/downloads/image.iso" extern char url[STRING_SIZE]; @@ -103,6 +104,37 @@ static int newtWinOkCancel(const char* title, const char* message, int width, in const char* btn_txt_ok, const char* btn_txt_cancel) { int ret = 1; + unsigned int btn_width_ok = strlen(btn_txt_ok); + unsigned int btn_width_cancel = strlen(btn_txt_cancel); + + // Maybe make the box wider to fix both buttons inside + unsigned int min_width = btn_width_ok + btn_width_cancel + 5; + if (width < min_width) + width = min_width; + + unsigned int btn_pos_ok = (width / 3) - (btn_width_ok / 2) - 1; + unsigned int btn_pos_cancel = (width * 2 / 3) - (btn_width_cancel / 2) - 1; + + // Move buttons a bit if they overlap + while ((btn_pos_ok + btn_width_ok + 5) > btn_pos_cancel) { + // Move the cancel button to the right if there is enough space left + if ((btn_pos_cancel + btn_width_cancel + 2) < width) { + ++btn_pos_cancel; + continue; + } + + // Move the OK button to the left if possible + if (btn_pos_ok > 1) { + --btn_pos_ok; + continue; + } + + // If they still overlap, we cannot fix the situtation + // and break. Should actually never get here, because we + // adjust the width of the window earlier. + break; + } + newtCenteredWindow(width, height, title); newtComponent form = newtForm(NULL, NULL, 0); @@ -111,12 +143,8 @@ static int newtWinOkCancel(const char* title, const char* message, int width, in newtTextboxSetText(textbox, message); newtFormAddComponent(form, textbox); - unsigned int btn_width_ok = strlen(btn_txt_ok); - unsigned int btn_width_cancel = strlen(btn_txt_cancel); - - newtComponent btn_ok = newtButton((width / 3) - (btn_width_ok / 2) - 2, height - 4, btn_txt_ok); - newtComponent btn_cancel = newtButton((width * 2 / 3) - (btn_width_cancel / 2) - 2, height - 4, - btn_txt_cancel); + newtComponent btn_ok = newtButton(btn_pos_ok, height - 4, btn_txt_ok); + newtComponent btn_cancel = newtButton(btn_pos_cancel, height - 4, btn_txt_cancel); newtFormAddComponents(form, btn_ok, btn_cancel, NULL); @@ -162,7 +190,7 @@ static int newtLicenseBox(const char* title, const char* text, int width, int he return ret; } -int write_lang_configs(const char *lang) { +int write_lang_configs(char* lang) { struct keyvalue *kv = initkeyvalues(); /* default stuff for main/settings. */ @@ -206,7 +234,7 @@ static char* center_string(const char* str, int width) { } #define DEFAULT_LANG "English" -#define NUM_LANGS 8 +#define NUM_LANGS 10 static struct lang { const char* code; @@ -225,6 +253,80 @@ static struct lang { { NULL, NULL }, }; +static struct config { + int unattended; + int serial_console; + int require_networking; + int perform_download; + int disable_swap; + char download_url[STRING_SIZE]; + char postinstall[STRING_SIZE]; +} config = { + .unattended = 0, + .serial_console = 0, + .require_networking = 0, + .perform_download = 0, + .disable_swap = 0, + .download_url = DOWNLOAD_URL, + .postinstall = "\0", +}; + +static void parse_command_line(struct config* c) { + char buffer[STRING_SIZE]; + char cmdline[STRING_SIZE]; + + FILE* f = fopen("/proc/cmdline", "r"); + if (!f) + return; + + int r = fread(&cmdline, 1, sizeof(cmdline) - 1, f); + if (r > 0) { + char* token = strtok(cmdline, " "); + + while (token) { + strncpy(buffer, token, sizeof(buffer)); + char* val = buffer; + char* key = strsep(&val, "="); + + // serial console + if ((strcmp(key, "console") == 0) && (strncmp(val, "ttyS", 4) == 0)) + c->serial_console = 1; + + // enable networking? + else if (strcmp(token, "installer.net") == 0) + c->require_networking = 1; + + // unattended mode + else if (strcmp(token, "installer.unattended") == 0) + c->unattended = 1; + + // disable swap + else if (strcmp(token, "installer.disable-swap") == 0) + c->disable_swap = 1; + + // download url + else if (strcmp(key, "installer.download-url") == 0) { + strncpy(c->download_url, val, sizeof(c->download_url)); + c->perform_download = 1; + + // Require networking for the download + c->require_networking = 1; + + // postinstall script + } else if (strcmp(key, "installer.postinstall") == 0) { + strncpy(c->postinstall, val, sizeof(c->postinstall)); + + // Require networking for the download + c->require_networking = 1; + } + + token = strtok(NULL, " "); + } + } + + fclose(f); +} + int main(int argc, char *argv[]) { struct hw* hw = hw_init(); const char* logfile = NULL; @@ -242,16 +344,10 @@ int main(int argc, char *argv[]) { char message[STRING_SIZE]; char title[STRING_SIZE]; int allok = 0; - FILE *handle, *cmdfile, *copying; - char line[STRING_SIZE]; - - int unattended = 0; - int serialconsole = 0; - struct keyvalue *unattendedkv = initkeyvalues(); - char restore_file[STRING_SIZE] = ""; + FILE *copying; - setlocale (LC_ALL, ""); - sethostname( SNAME , 10); + setlocale(LC_ALL, ""); + sethostname(SNAME, 10); /* Log file/terminal stuff. */ FILE* flog = NULL; @@ -281,28 +377,18 @@ int main(int argc, char *argv[]) { snprintf(title, sizeof(title), "%s - %s", NAME, SLOGAN); - if (! (cmdfile = fopen("/proc/cmdline", "r"))) - { - fprintf(flog, "Couldn't open commandline: /proc/cmdline\n"); - } else { - fgets(line, STRING_SIZE, cmdfile); - - // check if we have to make an unattended install - if (strstr (line, "unattended") != NULL) { - unattended = 1; - runcommandwithstatus("/bin/sleep 10", title, "WARNING: Unattended installation will start in 10 seconds...", NULL); - } - // check if we have to patch for serial console - if (strstr (line, "console=ttyS0") != NULL) { - serialconsole = 1; - } + // Parse parameters from the kernel command line + parse_command_line(&config); + + if (config.unattended) { + splashWindow(title, _("Warning: Unattended installation will start in 10 seconds..."), 10); } // Load common modules mysystem(logfile, "/sbin/modprobe vfat"); // USB key hw_stop_all_raid_arrays(logfile); - if (!unattended) { + if (!config.unattended) { // Language selection char* langnames[NUM_LANGS + 1]; @@ -314,22 +400,28 @@ int main(int argc, char *argv[]) { } langnames[NUM_LANGS] = NULL; - rc = newtWinMenu(_("Language selection"), _("Select the language you wish to use for the installation."), + rc = newtWinMenu(_("Language selection"), _("Select the language you wish to use for the installation."), 50, 5, 5, 8, langnames, &choice, _("OK"), NULL); assert(choice <= NUM_LANGS); fprintf(flog, "Selected language: %s (%s)\n", languages[choice].name, languages[choice].code); - snprintf(language, sizeof(language), languages[choice].code); + snprintf(language, sizeof(language), "%s", languages[choice].code); setenv("LANGUAGE", language, 1); setlocale(LC_ALL, language); } - char* helpline = center_string(_("/ between elements | selects | next screen"), screen_cols); + // Set helpline + char* helpline = NULL; + if (config.unattended) + helpline = center_string(_("Unattended mode"), screen_cols); + else + helpline = center_string(_("/ between elements | selects | next screen"), screen_cols); + newtPushHelpLine(helpline); - if (!unattended) { + if (!config.unattended) { snprintf(message, sizeof(message), _("Welcome to the %s installation program.\n\n" "Selecting Cancel on any of the following screens will reboot the computer."), NAME); @@ -338,43 +430,112 @@ int main(int argc, char *argv[]) { /* Search for a source drive that holds the right * version of the image we are going to install. */ - sourcedrive = hw_find_source_medium(hw); - - fprintf(flog, "Source drive: %s\n", sourcedrive); - if (!sourcedrive) { - newtWinMessage(title, _("OK"), _("No local source media found. Starting download.")); - runcommandwithstatus("/bin/downloadsource.sh", title, _("Downloading installation image ..."), logfile); - if ((handle = fopen("/tmp/source_device", "r")) == NULL) { - errorbox(_("Download error")); - goto EXIT; + if (!config.perform_download) { + sourcedrive = hw_find_source_medium(hw); + fprintf(flog, "Source drive: %s\n", sourcedrive); + } + + /* If we could not find a source drive, we will try + * downloading the install image */ + if (!sourcedrive) + config.perform_download = 1; + + if (config.perform_download) { + if (!config.unattended) { + // Show the right message to the user + char reason[STRING_SIZE]; + if (config.perform_download) { + snprintf(reason, sizeof(reason), + _("The installer will now try downloading the installation image.")); + } else { + snprintf(reason, sizeof(reason), + _("No source drive could be found.\n\n" + "You can try downloading the required installation image.")); + } + snprintf(message, sizeof(message), "%s %s", reason, + _("Please make sure to connect your machine to a network and " + "the installer will try connect to acquire an IP address.")); + + rc = newtWinOkCancel(title, message, 55, 12, + _("Download installation image"), _("Cancel")); + + if (rc != 0) + goto EXIT; } - fgets(sourcedrive, 5, handle); - fclose(handle); + // Make sure that we enable networking before download + config.require_networking = 1; } - assert(sourcedrive); + // Try starting the networking if we require it + if (config.require_networking) { + while (1) { + statuswindow(60, 4, title, _("Trying to start networking (DHCP)...")); - int r = hw_mount(sourcedrive, SOURCE_MOUNT_PATH, "iso9660", MS_RDONLY); - if (r) { - fprintf(flog, "Could not mount %s to %s\n", sourcedrive, SOURCE_MOUNT_PATH); - fprintf(flog, strerror(errno)); - exit(1); + rc = hw_start_networking(logfile); + newtPopWindow(); + + // Networking was successfully started + if (rc == 0) { + break; + + // An error happened, ask the user what to do + } else { + rc = newtWinOkCancel(title, _("Networking could not be started " + "but is required to go on with the installation.\n\n" + "Please connect your machine to a network with a " + "DHCP server and retry."), 50, 10, _("Retry"), _("Cancel")); + + if (rc) + goto EXIT; + } + } + + // Download the image if required + if (config.perform_download) { + fprintf(flog, "Download URL: %s\n", config.download_url); + snprintf(commandstring, sizeof(commandstring), "/usr/bin/downloadsource.sh %s %s", + SOURCE_TEMPFILE, config.download_url); + + while (!sourcedrive) { + rc = runcommandwithstatus(commandstring, title, _("Downloading installation image..."), logfile); + + FILE* f = fopen(SOURCE_TEMPFILE, "r"); + if (f) { + sourcedrive = strdup(SOURCE_TEMPFILE); + fclose(f); + } else { + char reason[STRING_SIZE] = "-"; + if (rc == 2) + snprintf(reason, sizeof(STRING_SIZE), _("MD5 checksum mismatch")); + + snprintf(message, sizeof(message), + _("The installation image could not be downloaded.\n Reason: %s\n\n%s"), + reason, config.download_url); + + rc = newtWinOkCancel(title, message, 75, 12, _("Retry"), _("Cancel")); + if (rc) + goto EXIT; + } + } + } } - /* load unattended configuration */ - if (unattended) { - fprintf(flog, "unattended: Reading unattended.conf\n"); + assert(sourcedrive); - (void) readkeyvalues(unattendedkv, UNATTENDED_CONF); - findkey(unattendedkv, "RESTORE_FILE", restore_file); + int r = hw_mount(sourcedrive, SOURCE_MOUNT_PATH, "iso9660", MS_RDONLY); + if (r) { + snprintf(message, sizeof(message), _("Could not mount %s to %s:\n %s\n"), + sourcedrive, SOURCE_MOUNT_PATH, strerror(errno)); + errorbox(message); + goto EXIT; } - if (!unattended) { + if (!config.unattended) { // Read the license file. if (!(copying = fopen(LICENSE_FILE, "r"))) { sprintf(discl_msg, "Could not open license file: %s\n", LICENSE_FILE); - fprintf(flog, discl_msg); + fprintf(flog, "%s", discl_msg); } else { fread(discl_msg, 1, 40000, copying); fclose(copying); @@ -397,7 +558,7 @@ int main(int argc, char *argv[]) { // Check how many disks have been found and what // we can do with them. - unsigned int num_disks = hw_count_disks(disks); + unsigned int num_disks = hw_count_disks((const struct hw_disk**)disks); while (1) { // no harddisks found @@ -406,8 +567,10 @@ int main(int argc, char *argv[]) { goto EXIT; // exactly one disk has been found - } else if (num_disks == 1) { - selected_disks = hw_select_disks(disks, NULL); + // or if we are running in unattended mode, we will select + // the first disk and go with that one + } else if ((num_disks == 1) || (config.unattended && num_disks >= 1)) { + selected_disks = hw_select_first_disk((const struct hw_disk**)disks); // more than one usable disk has been found and // the user needs to choose what to do with them @@ -416,7 +579,7 @@ int main(int argc, char *argv[]) { int disk_selection[num_disks]; for (unsigned int i = 0; i < num_disks; i++) { - disk_names[i] = &disks[i]->description; + disk_names[i] = disks[i]->description; disk_selection[i] = 0; } @@ -442,7 +605,12 @@ int main(int argc, char *argv[]) { } } - num_selected_disks = hw_count_disks(selected_disks); + // Don't print the auto-selected harddisk setup in + // unattended mode. + if (config.unattended) + break; + + num_selected_disks = hw_count_disks((const struct hw_disk**)selected_disks); if (num_selected_disks == 1) { snprintf(message, sizeof(message), @@ -469,7 +637,7 @@ int main(int argc, char *argv[]) { // Currently not supported } else { - errorbox(_("You disk configuration is currently not supported.")); + errorbox(_("Your disk configuration is currently not supported.")); fprintf(flog, "Num disks selected: %d\n", num_selected_disks); } @@ -481,7 +649,7 @@ int main(int argc, char *argv[]) { hw_free_disks(disks); - struct hw_destination* destination = hw_make_destination(part_type, selected_disks); + struct hw_destination* destination = hw_make_destination(part_type, selected_disks, config.disable_swap); if (!destination) { errorbox(_("Your harddisk is too small.")); @@ -497,19 +665,21 @@ int main(int argc, char *argv[]) { fprintf(flog, "Memory : %lluMB\n", BYTES2MB(hw_memory())); // Warn the user if there is not enough space to create a swap partition - if (!unattended && !*destination->part_swap) { - rc = newtWinChoice(title, _("OK"), _("Cancel"), - _("Your harddisk is very small, but you can continue without a swap partition.")); + if (!config.unattended) { + if (!config.disable_swap && !*destination->part_swap) { + rc = newtWinChoice(title, _("OK"), _("Cancel"), + _("Your harddisk is very small, but you can continue without a swap partition.")); - if (rc != 1) - goto EXIT; + if (rc != 1) + goto EXIT; + } } // Filesystem selection - if (!unattended) { + if (!config.unattended) { struct filesystems { int fstype; - const char* description; + char* description; } filesystems[] = { { HW_FS_EXT4, _("ext4 Filesystem") }, { HW_FS_EXT4_WO_JOURNAL, _("ext4 Filesystem without journal") }, @@ -613,7 +783,7 @@ int main(int argc, char *argv[]) { statuswindow(60, 4, title, _("Installing the bootloader...")); /* Serial console ? */ - if (serialconsole) { + if (config.serial_console) { /* grub */ FILE* f = fopen(DESTINATION_MOUNT_PATH "/etc/default/grub", "a"); if (!f) { @@ -649,11 +819,24 @@ int main(int argc, char *argv[]) { mysystem(logfile, "/usr/bin/touch /harddisk/var/ipfire/main/gpl_accepted"); /* Copy restore file from cdrom */ - if (unattended && (strlen(restore_file) > 0)) { - fprintf(flog, "unattended: Copy restore file\n"); - snprintf(commandstring, STRING_SIZE, - "cp /cdrom/%s /harddisk/var/ipfire/backup", restore_file); - mysystem(logfile, commandstring); + char* backup_file = hw_find_backup_file(logfile, SOURCE_MOUNT_PATH); + if (backup_file) { + rc = 0; + if (!config.unattended) { + rc = newtWinOkCancel(title, _("A backup file has been found on the installation image.\n\n" + "Do you want to restore the backup?"), 50, 10, _("Yes"), _("No")); + } + + if (rc == 0) { + rc = hw_restore_backup(logfile, backup_file, DESTINATION_MOUNT_PATH); + + if (rc) { + errorbox(_("An error occured when the backup file was restored.")); + goto EXIT; + } + } + + free(backup_file); } // Umount the destination drive @@ -666,10 +849,30 @@ int main(int argc, char *argv[]) { // Umount source drive and eject hw_umount(SOURCE_MOUNT_PATH); - snprintf(commandstring, STRING_SIZE, "/usr/bin/eject %s", sourcedrive); - mysystem(logfile, commandstring); + // Free downloaded ISO image + if (strcmp(sourcedrive, SOURCE_TEMPFILE) == 0) { + rc = unlink(sourcedrive); + if (rc) + fprintf(flog, "Could not free downloaded ISO image: %s\n", sourcedrive); - if (!unattended) { + // or eject real images + } else { + snprintf(commandstring, STRING_SIZE, "/usr/bin/eject %s", sourcedrive); + mysystem(logfile, commandstring); + } + + // Download and execute the postinstall script + if (*config.postinstall) { + snprintf(commandstring, sizeof(commandstring), + "/usr/bin/execute-postinstall.sh %s %s", DESTINATION_MOUNT_PATH, config.postinstall); + + if (runcommandwithstatus(commandstring, title, _("Running post-install script..."), logfile)) { + errorbox(_("Post-install script failed.")); + goto EXIT; + } + } + + if (!config.unattended) { snprintf(message, sizeof(message), _( "%s was successfully installed!\n\n" "Please remove any installation mediums from this system and hit the reboot button. " @@ -692,19 +895,28 @@ EXIT: newtFinished(); // Free resources - free(system_release); - free(roottext); - free(helpline); + if (system_release) + free(system_release); + + if (roottext) + free(roottext); + + if (helpline) + free(helpline); + + if (sourcedrive) + free(sourcedrive); - free(sourcedrive); - free(destination); + if (destination) + free(destination); hw_stop_all_raid_arrays(logfile); if (selected_disks) hw_free_disks(selected_disks); - hw_free(hw); + if (hw) + hw_free(hw); fcloseall();