X-Git-Url: http://git.ipfire.org/?p=ipfire-2.x.git;a=blobdiff_plain;f=src%2Finstaller%2Fmain.c;h=ad388e6a910cf512d891d5dd1610dc2c3bb17ec0;hp=313b35180baf9d887d846168dd84264842bc5e7a;hb=681c9bbe619ada94cc614719d276aa31397e2476;hpb=aa7f55b2dfab3194bbb41bfee671b122eda26da4 diff --git a/src/installer/main.c b/src/installer/main.c index 313b35180b..ad388e6a91 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/downloaded-image.iso" extern char url[STRING_SIZE]; @@ -111,8 +112,11 @@ static int newtWinOkCancel(const char* title, const char* message, int width, in newtTextboxSetText(textbox, message); newtFormAddComponent(form, textbox); - newtComponent btn_ok = newtButton((width - 16) / 3, height - 4, btn_txt_ok); - newtComponent btn_cancel = newtButton((width - 16) / 3 * 2 + 9, height - 4, + 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); newtFormAddComponents(form, btn_ok, btn_cancel, NULL); @@ -159,7 +163,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. */ @@ -203,23 +207,99 @@ 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; char* name; } languages[NUM_LANGS + 1] = { + { "da.utf8", "Danish (Dansk)" }, { "nl_NL.utf8", "Dutch (Nederlands)" }, { "en_US.utf8", "English" }, { "fr_FR.utf8", "French (Français)" }, { "de_DE.utf8", "German (Deutsch)" }, { "pl_PL.utf8", "Polish (Polski)" }, + { "pt_BR.utf8", "Portuguese (Brasil)" }, { "ru_RU.utf8", "Russian (Русский)" }, { "es_ES.utf8", "Spanish (Español)" }, { "tr_TR.utf8", "Turkish (Türkçe)" }, { 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(token, "console=ttyS0") == 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; @@ -233,20 +313,14 @@ int main(int argc, char *argv[]) { int rc = 0; char commandstring[STRING_SIZE]; int choice; - char shortlangname[10]; + char language[STRING_SIZE]; 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; @@ -276,28 +350,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]; @@ -309,21 +373,22 @@ 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), "%s", languages[choice].code); - setenv("LANGUAGE", languages[choice].code, 1); - setlocale(LC_ALL, languages[choice].code); + setenv("LANGUAGE", language, 1); + setlocale(LC_ALL, language); } char* 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); @@ -332,43 +397,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 = 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); @@ -384,14 +518,14 @@ int main(int argc, char *argv[]) { int part_type = HW_PART_TYPE_NORMAL; // Scan for disks to install on. - struct hw_disk** disks = hw_find_disks(hw); + struct hw_disk** disks = hw_find_disks(hw, sourcedrive); struct hw_disk** selected_disks = NULL; unsigned int num_selected_disks = 0; // 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 @@ -400,8 +534,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 @@ -410,7 +546,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; } @@ -436,7 +572,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), @@ -451,7 +592,7 @@ int main(int argc, char *argv[]) { } else if (num_selected_disks == 2) { snprintf(message, sizeof(message), _("The installation program will now set up a RAID configuration on the selected harddisks:\n\n %s\n %s\n\n" - "Do you agree to continue?"), (*selected_disks)->description, (*selected_disks + 1)->description); + "Do you agree to continue?"), selected_disks[0]->description, selected_disks[1]->description); rc = newtWinOkCancel(_("RAID Setup"), message, 50, 14, _("Delete all data"), _("Cancel")); @@ -463,7 +604,8 @@ 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); } if (selected_disks) { @@ -474,7 +616,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.")); @@ -487,21 +629,24 @@ int main(int argc, char *argv[]) { fprintf(flog, " swap : %s (%lluMB)\n", destination->part_swap, BYTES2MB(destination->size_swap)); fprintf(flog, " root : %s (%lluMB)\n", destination->part_root, BYTES2MB(destination->size_root)); fprintf(flog, " data : %s (%lluMB)\n", destination->part_data, BYTES2MB(destination->size_data)); + 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") }, @@ -540,6 +685,10 @@ int main(int argc, char *argv[]) { } newtPopWindow(); + } else { + // We will have to destroy all RAID setups that may have + // been on the devices that we want to use now. + hw_destroy_raid_superblocks(destination, logfile); } // Execute the partitioning... @@ -588,7 +737,7 @@ int main(int argc, char *argv[]) { } /* Save language und local settings */ - write_lang_configs(shortlangname); + write_lang_configs(language); /* Build cache lang file */ snprintf(commandstring, STRING_SIZE, "/usr/sbin/chroot /harddisk /usr/bin/perl -e \"require '" CONFIG_ROOT "/lang.pl'; &Lang::BuildCacheLang\""); @@ -600,23 +749,22 @@ int main(int argc, char *argv[]) { // Installing bootloader... statuswindow(60, 4, title, _("Installing the bootloader...")); - rc = hw_install_bootloader(destination, logfile); - if (rc) { - errorbox(_("Unable to install the bootloader.")); - goto EXIT; - } - - newtPopWindow(); - /* Serial console ? */ - if (serialconsole) { + if (config.serial_console) { /* grub */ - replace("/harddisk/boot/grub/grub.conf", "splashimage", "#splashimage"); - replace("/harddisk/boot/grub/grub.conf", "#serial", "serial"); - replace("/harddisk/boot/grub/grub.conf", "#terminal", "terminal"); - replace("/harddisk/boot/grub/grub.conf", " panic=10 ", " console=ttyS0,115200n8 panic=10 "); + FILE* f = fopen(DESTINATION_MOUNT_PATH "/etc/default/grub", "a"); + if (!f) { + errorbox(_("Unable to open /etc/default/grub for writing.")); + goto EXIT; + } - /*inittab*/ + fprintf(f, "GRUB_TERMINAL=\"serial\"\n"); + fprintf(f, "GRUB_SERIAL_COMMAND=\"serial --unit=0 --speed=%d\"\n", SERIAL_BAUDRATE); + fclose(f); + + replace(DESTINATION_MOUNT_PATH "/etc/default/grub", "panic=10", "panic=10 console=ttyS0,115200n8"); + + /* inittab */ replace("/harddisk/etc/inittab", "1:2345:respawn:", "#1:2345:respawn:"); replace("/harddisk/etc/inittab", "2:2345:respawn:", "#2:2345:respawn:"); replace("/harddisk/etc/inittab", "3:2345:respawn:", "#3:2345:respawn:"); @@ -626,15 +774,36 @@ int main(int argc, char *argv[]) { replace("/harddisk/etc/inittab", "#7:2345:respawn:", "7:2345:respawn:"); } + rc = hw_install_bootloader(destination, logfile); + if (rc) { + errorbox(_("Unable to install the bootloader.")); + goto EXIT; + } + + newtPopWindow(); + /* Set marker that the user has already accepted the gpl */ 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 @@ -647,10 +816,21 @@ int main(int argc, char *argv[]) { // Umount source drive and eject hw_umount(SOURCE_MOUNT_PATH); + // 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; + } + } + snprintf(commandstring, STRING_SIZE, "/usr/bin/eject %s", sourcedrive); mysystem(logfile, commandstring); - if (!unattended) { + 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. "