]> git.ipfire.org Git - ipfire-2.x.git/blame - src/installer/main.c
installer: Allow writing to the debug console from anywhere
[ipfire-2.x.git] / src / installer / main.c
CommitLineData
d6aaa55d
MT
1/* SmoothWall install program.
2 *
3 * This program is distributed under the terms of the GNU General Public
4 * Licence. See the file COPYING for details.
5 *
6 * (c) Lawrence Manning, 2001
f9cc0d70 7 * Contains main entry point, and misc functions.6
d6aaa55d 8 *
d6aaa55d 9 */
335c5bd1 10#define _GNU_SOURCE
10bc6f06 11
f0fa1795
MT
12#include <assert.h>
13#include <errno.h>
07d6f947 14#include <libsmooth.h>
b83b8f70 15#include <stdio.h>
f0fa1795
MT
16#include <stdlib.h>
17#include <string.h>
18#include <sys/mount.h>
19
20#include "hw.h"
5315fae6
MT
21
22// Translation
23#include <libintl.h>
24#define _(x) dgettext("installer", x)
25
9d77a9a5 26#define INST_FILECOUNT 30000
918546e2 27#define LICENSE_FILE "/cdrom/COPYING"
3e1145e1 28#define SOURCE_TEMPFILE "/tmp/downloads/image.iso"
d6aaa55d 29
d6aaa55d
MT
30extern char url[STRING_SIZE];
31
8e3b022a
SS
32FILE* flog = NULL;
33
d7dd283b
MT
34static int newtChecklist(const char* title, const char* message,
35 unsigned int width, unsigned int height, unsigned int num_entries,
36 const char** entries, int* states) {
37 int ret;
38 const int list_height = 4;
39
40 char cbstates[num_entries];
41
42 for (unsigned int i = 0; i < num_entries; i++) {
43 cbstates[i] = states[i] ? '*' : ' ';
44 }
45
46 newtCenteredWindow(width, height, title);
47
48 newtComponent textbox = newtTextbox(1, 1, width - 2, height - 6 - list_height,
49 NEWT_FLAG_WRAP);
50 newtTextboxSetText(textbox, message);
51
52 int top = newtTextboxGetNumLines(textbox) + 2;
53
54 newtComponent form = newtForm(NULL, NULL, 0);
55
56 newtComponent sb = NULL;
57 if (list_height < num_entries) {
58 sb = newtVerticalScrollbar(
59 width - 4, top + 1, list_height,
60 NEWT_COLORSET_CHECKBOX, NEWT_COLORSET_ACTCHECKBOX);
61
62 newtFormAddComponent(form, sb);
63 }
64
65 newtComponent subform = newtForm(sb, NULL, 0);
66 newtFormSetBackground(subform, NEWT_COLORSET_CHECKBOX);
67
68 newtFormSetHeight(subform, list_height);
69 newtFormSetWidth(subform, width - 10);
70
71 for (unsigned int i = 0; i < num_entries; i++) {
72 newtComponent cb = newtCheckbox(4, top + i, entries[i], cbstates[i],
73 NULL, &cbstates[i]);
74
75 newtFormAddComponent(subform, cb);
76 }
77
78 newtFormAddComponents(form, textbox, subform, NULL);
79
5315fae6
MT
80 newtComponent btn_okay = newtButton((width - 18) / 3, height - 4, _("OK"));
81 newtComponent btn_cancel = newtButton((width - 18) / 3 * 2 + 9, height - 4, _("Cancel"));
d7dd283b
MT
82 newtFormAddComponents(form, btn_okay, btn_cancel, NULL);
83
84 newtComponent answer = newtRunForm(form);
85
86 if ((answer == NULL) || (answer == btn_cancel)) {
87 ret = -1;
88 } else {
89 ret = 0;
90
91 for (unsigned int i = 0; i < num_entries; i++) {
92 states[i] = (cbstates[i] != ' ');
93
94 if (states[i])
95 ret++;
96 }
97 }
98
99 newtFormDestroy(form);
100 newtPopWindow();
101
102 return ret;
103}
104
105static int newtWinOkCancel(const char* title, const char* message, int width, int height,
106 const char* btn_txt_ok, const char* btn_txt_cancel) {
107 int ret = 1;
108
8f6e4298
MT
109 unsigned int btn_width_ok = strlen(btn_txt_ok);
110 unsigned int btn_width_cancel = strlen(btn_txt_cancel);
111
112 // Maybe make the box wider to fix both buttons inside
113 unsigned int min_width = btn_width_ok + btn_width_cancel + 5;
114 if (width < min_width)
115 width = min_width;
116
117 unsigned int btn_pos_ok = (width / 3) - (btn_width_ok / 2) - 1;
118 unsigned int btn_pos_cancel = (width * 2 / 3) - (btn_width_cancel / 2) - 1;
119
120 // Move buttons a bit if they overlap
121 while ((btn_pos_ok + btn_width_ok + 5) > btn_pos_cancel) {
122 // Move the cancel button to the right if there is enough space left
123 if ((btn_pos_cancel + btn_width_cancel + 2) < width) {
124 ++btn_pos_cancel;
125 continue;
126 }
127
128 // Move the OK button to the left if possible
129 if (btn_pos_ok > 1) {
130 --btn_pos_ok;
131 continue;
132 }
133
134 // If they still overlap, we cannot fix the situtation
135 // and break. Should actually never get here, because we
136 // adjust the width of the window earlier.
137 break;
138 }
139
d7dd283b
MT
140 newtCenteredWindow(width, height, title);
141
142 newtComponent form = newtForm(NULL, NULL, 0);
143
144 newtComponent textbox = newtTextbox(1, 1, width - 2, height - 6, NEWT_FLAG_WRAP);
145 newtTextboxSetText(textbox, message);
146 newtFormAddComponent(form, textbox);
147
8f6e4298
MT
148 newtComponent btn_ok = newtButton(btn_pos_ok, height - 4, btn_txt_ok);
149 newtComponent btn_cancel = newtButton(btn_pos_cancel, height - 4, btn_txt_cancel);
d7dd283b
MT
150
151 newtFormAddComponents(form, btn_ok, btn_cancel, NULL);
152
153 newtComponent answer = newtRunForm(form);
154
155 if (answer == btn_ok) {
156 ret = 0;
157 }
158
159 newtFormDestroy(form);
160 newtPopWindow();
161
162 return ret;
163}
164
bd5e7c29
MT
165static int newtLicenseBox(const char* title, const char* text, int width, int height) {
166 int ret = 1;
167
168 newtCenteredWindow(width, height, title);
169
170 newtComponent form = newtForm(NULL, NULL, 0);
171
172 newtComponent textbox = newtTextbox(1, 1, width - 2, height - 7,
173 NEWT_FLAG_WRAP|NEWT_FLAG_SCROLL);
174 newtTextboxSetText(textbox, text);
175 newtFormAddComponent(form, textbox);
176
177 char choice;
5315fae6 178 newtComponent checkbox = newtCheckbox(3, height - 3, _("I accept this license"),
bd5e7c29
MT
179 ' ', " *", &choice);
180
5315fae6 181 newtComponent btn = newtButton(width - 15, height - 4, _("OK"));
bd5e7c29
MT
182
183 newtFormAddComponents(form, checkbox, btn, NULL);
184
185 newtComponent answer = newtRunForm(form);
186 if (answer == btn && choice == '*')
187 ret = 0;
188
189 newtFormDestroy(form);
190 newtPopWindow();
191
192 return ret;
193}
194
335c5bd1 195int write_lang_configs(char* lang) {
5315fae6
MT
196 struct keyvalue *kv = initkeyvalues();
197
198 /* default stuff for main/settings. */
199 replacekeyvalue(kv, "LANGUAGE", lang);
fb76fc51 200 replacekeyvalue(kv, "HOSTNAME", DISTRO_SNAME);
5315fae6
MT
201 replacekeyvalue(kv, "THEME", "ipfire");
202 writekeyvalues(kv, "/harddisk" CONFIG_ROOT "/main/settings");
203 freekeyvalues(kv);
204
205 return 1;
206}
207
b83b8f70
MT
208static char* get_system_release() {
209 char system_release[STRING_SIZE] = "\0";
210
211 FILE* f = fopen("/etc/system-release", "r");
212 if (f) {
213 fgets(system_release, sizeof(system_release), f);
214 fclose(f);
215 }
216
217 return strdup(system_release);
218}
219
220static char* center_string(const char* str, int width) {
7db3e17a
MT
221 if (!str)
222 return NULL;
223
224 char* string = NULL;
b83b8f70
MT
225 unsigned int str_len = strlen(str);
226
7db3e17a
MT
227 if (str_len == width) {
228 string = strdup(str);
b83b8f70 229
7db3e17a
MT
230 } else if (str_len > width) {
231 string = strdup(str);
232 string[width - 1] = '\0';
b83b8f70 233
7db3e17a
MT
234 } else {
235 unsigned int indent_length = (width - str_len) / 2;
236 char indent[indent_length + 1];
237
238 for (unsigned int i = 0; i < indent_length; i++) {
239 indent[i] = ' ';
240 }
241 indent[indent_length] = '\0';
242
243 if (asprintf(&string, "%s%s", indent, str) < 0)
244 return NULL;
245 }
b83b8f70
MT
246
247 return string;
248}
249
f8cdaa10 250#define DEFAULT_LANG "en.utf8"
8cc7ba90 251#define NUM_LANGS 13
37f3421a
MT
252
253static struct lang {
254 const char* code;
255 char* name;
256} languages[NUM_LANGS + 1] = {
8cc7ba90
MT
257 { "fa.utf8", "فارسی (Persian)" },
258 { "da.utf8", "Dansk (Danish)" },
259 { "es.utf8", "Español (Spanish)" },
260 { "en.utf8", "English" },
261 { "fr.utf8", "Français (French)" },
262 { "hr.utf8", "Hrvatski (Croatian)" },
263 { "it.utf8", "Italiano (Italian)" },
264 { "de.utf8", "Deutsch (German)" },
265 { "nl.utf8", "Nederlands (Dutch)" },
266 { "pl.utf8", "Polski (Polish)" },
267 { "pt.utf8", "Portuguese (Brasil)" },
268 { "ru.utf8", "Русский (Russian)" },
269 { "tr.utf8", "Türkçe (Turkish)" },
37f3421a
MT
270 { NULL, NULL },
271};
272
211c7984 273static struct config {
2404450b 274 int unattended;
211c7984 275 int serial_console;
e0d006cd 276 int novga;
211c7984 277 int require_networking;
c0511f3a 278 int perform_download;
a8fca245 279 int disable_swap;
c0511f3a 280 char download_url[STRING_SIZE];
681c9bbe 281 char postinstall[STRING_SIZE];
b097fdd6 282 char* language;
211c7984 283} config = {
2404450b 284 .unattended = 0,
211c7984 285 .serial_console = 0,
e0d006cd 286 .novga = 0,
211c7984 287 .require_networking = 0,
c0511f3a 288 .perform_download = 0,
a8fca245 289 .disable_swap = 0,
c0511f3a 290 .download_url = DOWNLOAD_URL,
681c9bbe 291 .postinstall = "\0",
b097fdd6 292 .language = DEFAULT_LANG,
211c7984
MT
293};
294
646d6b06 295static void parse_command_line(FILE* flog, struct config* c) {
c0511f3a 296 char buffer[STRING_SIZE];
2404450b
MT
297 char cmdline[STRING_SIZE];
298
299 FILE* f = fopen("/proc/cmdline", "r");
646d6b06
MT
300 if (!f) {
301 fprintf(flog, "Could not open /proc/cmdline: %m");
2404450b 302 return;
646d6b06 303 }
2404450b
MT
304
305 int r = fread(&cmdline, 1, sizeof(cmdline) - 1, f);
306 if (r > 0) {
646d6b06
MT
307 // Remove the trailing newline
308 if (cmdline[r-1] == '\n')
309 cmdline[r-1] = '\0';
2404450b 310
646d6b06 311 char* token = strtok(cmdline, " ");
2404450b 312 while (token) {
c0511f3a 313 strncpy(buffer, token, sizeof(buffer));
335c5bd1 314 char* val = buffer;
c0511f3a
MT
315 char* key = strsep(&val, "=");
316
2404450b 317 // serial console
bb75bc3b 318 if ((strcmp(key, "console") == 0) && (strncmp(val, "ttyS", 4) == 0))
2404450b
MT
319 c->serial_console = 1;
320
e0d006cd
AF
321 // novga
322 else if (strcmp(key, "novga") == 0)
323 c->novga = 1;
324
2404450b
MT
325 // enable networking?
326 else if (strcmp(token, "installer.net") == 0)
327 c->require_networking = 1;
328
329 // unattended mode
330 else if (strcmp(token, "installer.unattended") == 0)
331 c->unattended = 1;
332
a8fca245
MT
333 // disable swap
334 else if (strcmp(token, "installer.disable-swap") == 0)
335 c->disable_swap = 1;
336
c0511f3a
MT
337 // download url
338 else if (strcmp(key, "installer.download-url") == 0) {
335c5bd1 339 strncpy(c->download_url, val, sizeof(c->download_url));
c0511f3a
MT
340 c->perform_download = 1;
341
681c9bbe
MT
342 // Require networking for the download
343 c->require_networking = 1;
344
345 // postinstall script
346 } else if (strcmp(key, "installer.postinstall") == 0) {
347 strncpy(c->postinstall, val, sizeof(c->postinstall));
348
c0511f3a
MT
349 // Require networking for the download
350 c->require_networking = 1;
351 }
352
2404450b
MT
353 token = strtok(NULL, " ");
354 }
355 }
356
357 fclose(f);
358}
359
f0fa1795
MT
360int main(int argc, char *argv[]) {
361 struct hw* hw = hw_init();
46b56e20 362 const char* logfile = NULL;
e0bbaf87 363
b83b8f70
MT
364 // Read /etc/system-release
365 char* system_release = get_system_release();
366
e0bbaf87
AF
367 char discl_msg[40000] = "Disclaimer\n";
368
f0fa1795 369 char* sourcedrive = NULL;
72d80898 370 int rc = 0;
d6aaa55d 371 char commandstring[STRING_SIZE];
d6aaa55d 372 int choice;
d7dd283b 373 char message[STRING_SIZE];
d6aaa55d
MT
374 char title[STRING_SIZE];
375 int allok = 0;
335c5bd1
MT
376 FILE *copying;
377
378 setlocale(LC_ALL, "");
fb76fc51 379 sethostname(DISTRO_SNAME, 10);
72d80898 380
d6aaa55d 381 /* Log file/terminal stuff. */
46b56e20
MT
382 if (argc >= 2) {
383 logfile = argv[1];
384
385 if (!(flog = fopen(logfile, "w+")))
d6aaa55d 386 return 0;
46b56e20 387 } else {
d6aaa55d 388 return 0;
46b56e20
MT
389 }
390
d6aaa55d 391 fprintf(flog, "Install program started.\n");
5fb499f1
MT
392 if (hw->efi)
393 fprintf(flog, "EFI mode enabled\n");
394
d6aaa55d
MT
395 newtInit();
396 newtCls();
397
b83b8f70
MT
398 // Determine the size of the screen
399 int screen_cols = 0;
400 int screen_rows = 0;
401
402 newtGetScreenSize(&screen_cols, &screen_rows);
403
404 // Draw title
405 char* roottext = center_string(system_release, screen_cols);
7db3e17a
MT
406 if (roottext)
407 newtDrawRootText(0, 0, roottext);
b83b8f70 408
fb76fc51 409 snprintf(title, sizeof(title), "%s - %s", DISTRO_NAME, DISTRO_SLOGAN);
7ea444c8 410
2404450b 411 // Parse parameters from the kernel command line
646d6b06 412 parse_command_line(flog, &config);
a3e135c8 413
2404450b
MT
414 if (config.unattended) {
415 splashWindow(title, _("Warning: Unattended installation will start in 10 seconds..."), 10);
d6aaa55d 416 }
0db33b56 417
7ea444c8 418 // Load common modules
68a50dd1
AF
419 mysystem(logfile, "/sbin/modprobe vfat"); // USB key
420 mysystem(logfile, "/sbin/modprobe ntfs3"); // USB key
46b56e20 421 hw_stop_all_raid_arrays(logfile);
4a0d9bef 422
2404450b 423 if (!config.unattended) {
37f3421a
MT
424 // Language selection
425 char* langnames[NUM_LANGS + 1];
72d80898 426
37f3421a 427 for (unsigned int i = 0; i < NUM_LANGS; i++) {
b097fdd6 428 if (strcmp(languages[i].code, DEFAULT_LANG) == 0)
37f3421a
MT
429 choice = i;
430
431 langnames[i] = languages[i].name;
432 }
433 langnames[NUM_LANGS] = NULL;
434
a3e135c8 435 rc = newtWinMenu(_("Language selection"), _("Select the language you wish to use for the installation."),
37f3421a
MT
436 50, 5, 5, 8, langnames, &choice, _("OK"), NULL);
437
438 assert(choice <= NUM_LANGS);
439
440 fprintf(flog, "Selected language: %s (%s)\n", languages[choice].name, languages[choice].code);
b097fdd6 441 config.language = languages[choice].code;
d04d4d58 442
b097fdd6
MT
443 setlocale(LC_ALL, config.language);
444 setenv("LANGUAGE", config.language, 1);
37f3421a 445 }
3d6e1202 446
6a91a5a7
MT
447 // Set helpline
448 char* helpline = NULL;
449 if (config.unattended)
450 helpline = center_string(_("Unattended mode"), screen_cols);
451 else
452 helpline = center_string(_("<Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen"), screen_cols);
453
7db3e17a
MT
454 if (helpline)
455 newtPushHelpLine(helpline);
0db33b56 456
2404450b 457 if (!config.unattended) {
5315fae6 458 snprintf(message, sizeof(message),
ae5edf16 459 _("Welcome to the %s installation program.\n\n"
fb76fc51 460 "Selecting Cancel on any of the following screens will reboot the computer."), DISTRO_NAME);
5315fae6 461 newtWinMessage(title, _("Start installation"), message);
e0bbaf87
AF
462 }
463
f0fa1795
MT
464 /* Search for a source drive that holds the right
465 * version of the image we are going to install. */
c0511f3a
MT
466 if (!config.perform_download) {
467 sourcedrive = hw_find_source_medium(hw);
468 fprintf(flog, "Source drive: %s\n", sourcedrive);
469 }
7d114284
MT
470
471 /* If we could not find a source drive, we will try
472 * downloading the install image */
c0511f3a
MT
473 if (!sourcedrive)
474 config.perform_download = 1;
475
476 if (config.perform_download) {
2404450b 477 if (!config.unattended) {
35853bb4
MT
478 // Show the right message to the user
479 char reason[STRING_SIZE];
c0511f3a 480 if (config.perform_download) {
35853bb4
MT
481 snprintf(reason, sizeof(reason),
482 _("The installer will now try downloading the installation image."));
483 } else {
484 snprintf(reason, sizeof(reason),
485 _("No source drive could be found.\n\n"
486 "You can try downloading the required installation image."));
487 }
488 snprintf(message, sizeof(message), "%s %s", reason,
489 _("Please make sure to connect your machine to a network and "
490 "the installer will try connect to acquire an IP address."));
491
492 rc = newtWinOkCancel(title, message, 55, 12,
493 _("Download installation image"), _("Cancel"));
7d114284
MT
494
495 if (rc != 0)
496 goto EXIT;
497 }
35853bb4 498
c0511f3a 499 // Make sure that we enable networking before download
211c7984 500 config.require_networking = 1;
7d114284
MT
501 }
502
503 // Try starting the networking if we require it
211c7984 504 if (config.require_networking) {
35853bb4
MT
505 while (1) {
506 statuswindow(60, 4, title, _("Trying to start networking (DHCP)..."));
7d114284 507
35853bb4
MT
508 rc = hw_start_networking(logfile);
509 newtPopWindow();
7d114284 510
35853bb4
MT
511 // Networking was successfully started
512 if (rc == 0) {
513 break;
514
515 // An error happened, ask the user what to do
516 } else {
517 rc = newtWinOkCancel(title, _("Networking could not be started "
518 "but is required to go on with the installation.\n\n"
519 "Please connect your machine to a network with a "
520 "DHCP server and retry."), 50, 10, _("Retry"), _("Cancel"));
521
522 if (rc)
523 goto EXIT;
524 }
0f680bcc 525 }
f0fa1795 526
7d114284 527 // Download the image if required
c0511f3a
MT
528 if (config.perform_download) {
529 fprintf(flog, "Download URL: %s\n", config.download_url);
530 snprintf(commandstring, sizeof(commandstring), "/usr/bin/downloadsource.sh %s %s",
531 SOURCE_TEMPFILE, config.download_url);
532
533 while (!sourcedrive) {
534 rc = runcommandwithstatus(commandstring, title, _("Downloading installation image..."), logfile);
535
536 FILE* f = fopen(SOURCE_TEMPFILE, "r");
537 if (f) {
3e1145e1 538 sourcedrive = strdup(SOURCE_TEMPFILE);
c0511f3a
MT
539 fclose(f);
540 } else {
541 char reason[STRING_SIZE] = "-";
542 if (rc == 2)
f97c8963 543 snprintf(reason, sizeof(STRING_SIZE), _("BLAKE2 checksum mismatch"));
c0511f3a
MT
544
545 snprintf(message, sizeof(message),
546 _("The installation image could not be downloaded.\n Reason: %s\n\n%s"),
547 reason, config.download_url);
548
549 rc = newtWinOkCancel(title, message, 75, 12, _("Retry"), _("Cancel"));
550 if (rc)
551 goto EXIT;
552 }
7d114284 553 }
7d114284 554 }
72d80898 555 }
0f680bcc 556
f0fa1795
MT
557 assert(sourcedrive);
558
25fcce25 559 int r = hw_mount(sourcedrive, SOURCE_MOUNT_PATH, "iso9660", MS_RDONLY);
68a50dd1
AF
560 if (r) r = hw_mount(sourcedrive, SOURCE_MOUNT_PATH, "ntfs3", MS_RDONLY);
561 if (r) r = hw_mount(sourcedrive, SOURCE_MOUNT_PATH, "vfat", MS_RDONLY);
562 if (r)
563 {
c0511f3a
MT
564 snprintf(message, sizeof(message), _("Could not mount %s to %s:\n %s\n"),
565 sourcedrive, SOURCE_MOUNT_PATH, strerror(errno));
566 errorbox(message);
567 goto EXIT;
f0fa1795 568 }
918546e2 569
2404450b 570 if (!config.unattended) {
918546e2
MT
571 // Read the license file.
572 if (!(copying = fopen(LICENSE_FILE, "r"))) {
573 sprintf(discl_msg, "Could not open license file: %s\n", LICENSE_FILE);
335c5bd1 574 fprintf(flog, "%s", discl_msg);
918546e2
MT
575 } else {
576 fread(discl_msg, 1, 40000, copying);
577 fclose(copying);
578
ae5edf16 579 if (newtLicenseBox(_("License Agreement"), discl_msg, 75, 20)) {
5315fae6 580 errorbox(_("License not accepted!"));
bd5e7c29 581
918546e2
MT
582 goto EXIT;
583 }
584 }
585 }
586
d7dd283b 587 int part_type = HW_PART_TYPE_NORMAL;
ee78a5ef 588
d7dd283b 589 // Scan for disks to install on.
ee00d203 590 struct hw_disk** disks = hw_find_disks(hw, sourcedrive);
d7dd283b
MT
591
592 struct hw_disk** selected_disks = NULL;
593 unsigned int num_selected_disks = 0;
594
595 // Check how many disks have been found and what
596 // we can do with them.
335c5bd1 597 unsigned int num_disks = hw_count_disks((const struct hw_disk**)disks);
d7dd283b
MT
598
599 while (1) {
600 // no harddisks found
601 if (num_disks == 0) {
5315fae6 602 errorbox(_("No hard disk found."));
d7dd283b
MT
603 goto EXIT;
604
605 // exactly one disk has been found
a3e135c8
MT
606 // or if we are running in unattended mode, we will select
607 // the first disk and go with that one
2404450b 608 } else if ((num_disks == 1) || (config.unattended && num_disks >= 1)) {
335c5bd1 609 selected_disks = hw_select_first_disk((const struct hw_disk**)disks);
d7dd283b
MT
610
611 // more than one usable disk has been found and
612 // the user needs to choose what to do with them
613 } else {
614 const char* disk_names[num_disks];
615 int disk_selection[num_disks];
616
617 for (unsigned int i = 0; i < num_disks; i++) {
335c5bd1 618 disk_names[i] = disks[i]->description;
d7dd283b
MT
619 disk_selection[i] = 0;
620 }
621
622 while (!selected_disks) {
5315fae6
MT
623 rc = newtChecklist(_("Disk Selection"),
624 _("Select the disk(s) you want to install IPFire on. "
625 "First those will be partitioned, and then the partitions will have a filesystem put on them.\n\n"
626 "ALL DATA ON THE DISK WILL BE DESTROYED."),
d7dd283b
MT
627 50, 20, num_disks, disk_names, disk_selection);
628
629 // Error
630 if (rc < 0) {
631 goto EXIT;
632
633 // Nothing has been selected
634 } else if (rc == 0) {
5315fae6
MT
635 errorbox(_("No disk has been selected.\n\n"
636 "Please select one or more disks you want to install IPFire on."));
d7dd283b
MT
637
638 } else {
639 selected_disks = hw_select_disks(disks, disk_selection);
640 }
641 }
642 }
643
a3e135c8
MT
644 // Don't print the auto-selected harddisk setup in
645 // unattended mode.
2404450b 646 if (config.unattended)
a3e135c8
MT
647 break;
648
335c5bd1 649 num_selected_disks = hw_count_disks((const struct hw_disk**)selected_disks);
d7dd283b
MT
650
651 if (num_selected_disks == 1) {
5315fae6
MT
652 snprintf(message, sizeof(message),
653 _("The installation program will now prepare the chosen harddisk:\n\n %s\n\n"
654 "Do you agree to continue?"), (*selected_disks)->description);
655 rc = newtWinOkCancel(_("Disk Setup"), message, 50, 10,
656 _("Delete all data"), _("Cancel"));
d7dd283b
MT
657
658 if (rc == 0)
56b548f1 659 break;
d7dd283b
MT
660
661 } else if (num_selected_disks == 2) {
5315fae6
MT
662 snprintf(message, sizeof(message),
663 _("The installation program will now set up a RAID configuration on the selected harddisks:\n\n %s\n %s\n\n"
5a3b3718 664 "Do you agree to continue?"), selected_disks[0]->description, selected_disks[1]->description);
5315fae6
MT
665 rc = newtWinOkCancel(_("RAID Setup"), message, 50, 14,
666 _("Delete all data"), _("Cancel"));
d7dd283b
MT
667
668 if (rc == 0) {
669 part_type = HW_PART_TYPE_RAID1;
670
ee78a5ef 671 break;
d7dd283b
MT
672 }
673
674 // Currently not supported
675 } else {
77192e97 676 errorbox(_("Your disk configuration is currently not supported."));
ee43f517 677 fprintf(flog, "Num disks selected: %d\n", num_selected_disks);
aee3027d 678 }
ee78a5ef 679
d7dd283b
MT
680 if (selected_disks) {
681 hw_free_disks(selected_disks);
682 selected_disks = NULL;
683 }
212cab4f 684 }
212cab4f 685
d7dd283b 686 hw_free_disks(disks);
72d80898 687
92e78233
MT
688 struct hw_destination* destination = hw_make_destination(hw, part_type,
689 selected_disks, config.disable_swap);
5057b611 690
25fcce25 691 if (!destination) {
5315fae6 692 errorbox(_("Your harddisk is too small."));
cd8dd8dd 693 goto EXIT;
25fcce25 694 }
cd8dd8dd 695
25fcce25 696 fprintf(flog, "Destination drive: %s\n", destination->path);
48d6a112
MT
697 fprintf(flog, " bootldr: %s (%lluMB)\n", destination->part_bootldr, BYTES2MB(destination->size_bootldr));
698 fprintf(flog, " boot : %s (%lluMB)\n", destination->part_boot, BYTES2MB(destination->size_boot));
92e78233 699 fprintf(flog, " ESP : %s (%lluMB)\n", destination->part_boot_efi, BYTES2MB(destination->size_boot_efi));
48d6a112
MT
700 fprintf(flog, " swap : %s (%lluMB)\n", destination->part_swap, BYTES2MB(destination->size_swap));
701 fprintf(flog, " root : %s (%lluMB)\n", destination->part_root, BYTES2MB(destination->size_root));
5be66d81 702 fprintf(flog, "Memory : %lluMB\n", BYTES2MB(hw_memory()));
72d80898 703
25fcce25 704 // Warn the user if there is not enough space to create a swap partition
a8fca245
MT
705 if (!config.unattended) {
706 if (!config.disable_swap && !*destination->part_swap) {
707 rc = newtWinChoice(title, _("OK"), _("Cancel"),
708 _("Your harddisk is very small, but you can continue without a swap partition."));
4f26ff7f 709
a8fca245
MT
710 if (rc != 1)
711 goto EXIT;
712 }
72d80898 713 }
9a3d3d95 714
25fcce25 715 // Filesystem selection
2404450b 716 if (!config.unattended) {
25fcce25
MT
717 struct filesystems {
718 int fstype;
a8fca245 719 char* description;
25fcce25 720 } filesystems[] = {
5315fae6
MT
721 { HW_FS_EXT4, _("ext4 Filesystem") },
722 { HW_FS_EXT4_WO_JOURNAL, _("ext4 Filesystem without journal") },
723 { HW_FS_XFS, _("XFS Filesystem") },
130815d3 724 { HW_FS_BTRFS, _("BTRFS Filesystem (EXPERIMENTAL)") },
25fcce25
MT
725 { 0, NULL },
726 };
727 unsigned int num_filesystems = sizeof(filesystems) / sizeof(*filesystems);
728
729 char* fs_names[num_filesystems];
730 int fs_choice = 0;
731 for (unsigned int i = 0; i < num_filesystems; i++) {
732 if (HW_FS_DEFAULT == filesystems[i].fstype)
733 fs_choice = i;
734
a8fca245 735 fs_names[i] = filesystems[i].description;
25fcce25 736 }
72d80898 737
5315fae6 738 rc = newtWinMenu(_("Filesystem Selection"), _("Please choose your filesystem:"),
e2617fc0 739 50, 5, 5, 5, fs_names, &fs_choice, _("OK"), _("Cancel"), NULL);
25fcce25 740
eb3ff46e 741 if (rc == 2)
72d80898 742 goto EXIT;
139cb500
MT
743
744 destination->filesystem = filesystems[fs_choice].fstype;
72d80898
MT
745 }
746
4a0d9bef
MT
747 // Setting up RAID if needed.
748 if (destination->is_raid) {
5315fae6 749 statuswindow(60, 4, title, _("Building RAID..."));
4a0d9bef 750
46b56e20 751 rc = hw_setup_raid(destination, logfile);
4a0d9bef 752 if (rc) {
5315fae6 753 errorbox(_("Unable to build the RAID."));
4a0d9bef
MT
754 goto EXIT;
755 }
756
757 newtPopWindow();
d78fffa0
MT
758 } else {
759 // We will have to destroy all RAID setups that may have
760 // been on the devices that we want to use now.
761 hw_destroy_raid_superblocks(destination, logfile);
4a0d9bef
MT
762 }
763
25fcce25 764 // Execute the partitioning...
5315fae6 765 statuswindow(60, 4, title, _("Partitioning disk..."));
72d80898 766
46b56e20 767 rc = hw_create_partitions(destination, logfile);
25fcce25 768 if (rc) {
5315fae6 769 errorbox(_("Unable to partition the disk."));
72d80898 770 goto EXIT;
9607771a 771 }
72d80898 772
25fcce25 773 newtPopWindow();
72d80898 774
25fcce25 775 // Execute the formatting...
5315fae6 776 statuswindow(60, 4, title, _("Creating filesystems..."));
b8e2d108 777
46b56e20 778 rc = hw_create_filesystems(destination, logfile);
25fcce25 779 if (rc) {
5315fae6 780 errorbox(_("Unable to create filesystems."));
72d80898
MT
781 goto EXIT;
782 }
25fcce25
MT
783
784 rc = hw_mount_filesystems(destination, DESTINATION_MOUNT_PATH);
785 if (rc) {
5315fae6 786 errorbox(_("Unable to mount filesystems."));
72d80898 787 goto EXIT;
9607771a 788 }
c78a77eb 789
25fcce25
MT
790 newtPopWindow();
791
792 // Extract files...
03d956be 793 snprintf(commandstring, STRING_SIZE,
5621b0ef 794 "/bin/tar --acls --xattrs --xattrs-include='*' -C /harddisk -xvf /cdrom/distro.img --zstd 2>/dev/null");
5315fae6 795
edd536b6 796 if (runcommandwithprogress(60, 4, title, commandstring, INST_FILECOUNT,
46b56e20 797 _("Installing the system..."), logfile)) {
5315fae6 798 errorbox(_("Unable to install the system."));
d6aaa55d
MT
799 goto EXIT;
800 }
7f69d8a4
MT
801
802 // Write fstab
803 rc = hw_write_fstab(destination);
804 if (rc) {
805 fprintf(flog, "Could not write /etc/fstab\n");
806 goto EXIT;
807 }
808
406f019f 809 /* Save language und local settings */
b097fdd6 810 write_lang_configs(config.language);
d6aaa55d 811
330345c2 812 /* Build cache lang file */
6cf9e770 813 snprintf(commandstring, STRING_SIZE, "/usr/sbin/chroot /harddisk /usr/bin/perl -e \"require '" CONFIG_ROOT "/lang.pl'; &Lang::BuildCacheLang\"");
46b56e20 814 if (runcommandwithstatus(commandstring, title, _("Installing the language cache..."), logfile)) {
5315fae6 815 errorbox(_("Unable to install the language cache."));
330345c2
MT
816 goto EXIT;
817 }
818
fa8b3ea7
AF
819 /* trigger udev to add disk-by-uuid entries */
820 snprintf(commandstring, STRING_SIZE, "/usr/sbin/chroot /harddisk /sbin/udevadm trigger");
821 if (runcommandwithstatus(commandstring, title, _("Trigger udev to redetect partitions..."), logfile)) {
822 errorbox(_("Error triggering udev to redetect partitions."));
823 goto EXIT;
824 }
825
f5007e9c 826 // Installing bootloader...
5315fae6 827 statuswindow(60, 4, title, _("Installing the bootloader..."));
423400cf 828
5faa66cf 829 /* Serial console ? */
211c7984 830 if (config.serial_console) {
5faa66cf 831 /* grub */
9dd16c6d
MT
832 FILE* f = fopen(DESTINATION_MOUNT_PATH "/etc/default/grub", "a");
833 if (!f) {
834 errorbox(_("Unable to open /etc/default/grub for writing."));
835 goto EXIT;
836 }
5faa66cf 837
7f6e0425 838 fprintf(f, "GRUB_TERMINAL=\"serial\"\n");
9dd16c6d
MT
839 fprintf(f, "GRUB_SERIAL_COMMAND=\"serial --unit=0 --speed=%d\"\n", SERIAL_BAUDRATE);
840 fclose(f);
841
842 replace(DESTINATION_MOUNT_PATH "/etc/default/grub", "panic=10", "panic=10 console=ttyS0,115200n8");
5faa66cf
AF
843 }
844
e0d006cd
AF
845 /* novga */
846 if (config.novga) {
847 /* grub */
848 FILE* f = fopen(DESTINATION_MOUNT_PATH "/etc/default/grub", "a");
849 if (!f) {
850 errorbox(_("Unable to open /etc/default/grub for writing."));
851 goto EXIT;
852 }
853
854 fprintf(f, "GRUB_GFXMODE=\"none\"\n");
855 fclose(f);
856 }
857
92e78233 858 rc = hw_install_bootloader(hw, destination, logfile);
9dd16c6d
MT
859 if (rc) {
860 errorbox(_("Unable to install the bootloader."));
861 goto EXIT;
862 }
863
864 newtPopWindow();
865
53103ab5
MT
866 /* Set marker that the user has already accepted the GPL if the license has been shown
867 * in the installation process. In unatteded mode, the user will be presented the
868 * license when he or she logs on to the web user interface for the first time. */
869 if (!config.unattended)
870 mysystem(logfile, "/usr/bin/touch /harddisk/var/ipfire/main/gpl_accepted");
dfa59dbd 871
c25a0343 872 /* Copy restore file from cdrom */
38c6822d
MT
873 char* backup_file = hw_find_backup_file(logfile, SOURCE_MOUNT_PATH);
874 if (backup_file) {
875 rc = 0;
2404450b 876 if (!config.unattended) {
38c6822d
MT
877 rc = newtWinOkCancel(title, _("A backup file has been found on the installation image.\n\n"
878 "Do you want to restore the backup?"), 50, 10, _("Yes"), _("No"));
879 }
880
881 if (rc == 0) {
882 rc = hw_restore_backup(logfile, backup_file, DESTINATION_MOUNT_PATH);
883
884 if (rc) {
885 errorbox(_("An error occured when the backup file was restored."));
886 goto EXIT;
887 }
888 }
889
890 free(backup_file);
c25a0343 891 }
2c9c458c 892
ade96ba8
MT
893 // Download and execute the postinstall script
894 if (*config.postinstall) {
895 snprintf(commandstring, sizeof(commandstring),
896 "/usr/bin/execute-postinstall.sh %s %s", DESTINATION_MOUNT_PATH, config.postinstall);
897
898 if (runcommandwithstatus(commandstring, title, _("Running post-install script..."), logfile)) {
899 errorbox(_("Post-install script failed."));
900 goto EXIT;
901 }
902 }
903
ddd32a5c 904 // Umount the destination drive
ade96ba8 905 statuswindow(60, 4, title, _("Umounting filesystems..."));
ddd32a5c 906
ade96ba8
MT
907 rc = hw_umount_filesystems(destination, DESTINATION_MOUNT_PATH);
908 if (rc) {
909 // Show an error message if filesystems could not be umounted properly
910 snprintf(message, sizeof(message),
911 _("Could not umount all filesystems successfully:\n\n %s"), strerror(errno));
912 errorbox(message);
913 goto EXIT;
914 }
ddd32a5c 915
2c9c458c 916 // Umount source drive and eject
ced15fdf 917 hw_umount(SOURCE_MOUNT_PATH, NULL);
2c9c458c 918
3e1145e1
MT
919 // Free downloaded ISO image
920 if (strcmp(sourcedrive, SOURCE_TEMPFILE) == 0) {
921 rc = unlink(sourcedrive);
922 if (rc)
923 fprintf(flog, "Could not free downloaded ISO image: %s\n", sourcedrive);
924
925 // or eject real images
926 } else {
927 snprintf(commandstring, STRING_SIZE, "/usr/bin/eject %s", sourcedrive);
928 mysystem(logfile, commandstring);
929 }
ade96ba8 930 newtPopWindow();
3e1145e1 931
ade96ba8
MT
932 // Stop the RAID array if we are using RAID
933 if (destination->is_raid)
934 hw_stop_all_raid_arrays(logfile);
681c9bbe 935
ade96ba8
MT
936 // Show a short message that the installation went well and
937 // wait a moment so that all disk caches get flushed.
938 if (config.unattended) {
939 splashWindow(title, _("Unattended installation has finished. The system will be shutting down in a moment..."), 5);
681c9bbe 940
ade96ba8 941 } else {
ae5edf16
MT
942 snprintf(message, sizeof(message), _(
943 "%s was successfully installed!\n\n"
944 "Please remove any installation mediums from this system and hit the reboot button. "
945 "Once the system has restarted you will be asked to setup networking and system passwords. "
946 "After that, you should point your web browser at https://%s:444 (or what ever you name "
fb76fc51 947 "your %s) for the web configuration console."), DISTRO_NAME, DISTRO_SNAME, DISTRO_NAME);
5315fae6 948 newtWinMessage(_("Congratulations!"), _("Reboot"), message);
73d9a908 949 }
cd8dd8dd 950
22b9e405 951 allok = 1;
edd536b6 952
d6aaa55d 953EXIT:
46b56e20 954 fprintf(flog, "Install program ended.\n");
ae5edf16
MT
955 fflush(flog);
956 fclose(flog);
d6aaa55d 957
ae5edf16
MT
958 if (!allok)
959 newtWinMessage(title, _("OK"), _("Setup has failed. Press Ok to reboot."));
3a1019f6
MT
960
961 newtFinished();
962
f0fa1795 963 // Free resources
3e1145e1
MT
964 if (system_release)
965 free(system_release);
966
967 if (roottext)
968 free(roottext);
969
970 if (helpline)
971 free(helpline);
972
973 if (sourcedrive)
974 free(sourcedrive);
b83b8f70 975
3e1145e1
MT
976 if (destination)
977 free(destination);
d7dd283b 978
46b56e20 979 hw_stop_all_raid_arrays(logfile);
4a0d9bef 980
d7dd283b
MT
981 if (selected_disks)
982 hw_free_disks(selected_disks);
983
3e1145e1
MT
984 if (hw)
985 hw_free(hw);
f0fa1795 986
25fcce25
MT
987 fcloseall();
988
f0a61a0a
MT
989 if (allok == 1)
990 return 0;
25fcce25 991
f0a61a0a 992 return 1;
d6aaa55d 993}