chiark / gitweb /
Revert "socket: add support for TCP fast Open"
[elogind.git] / src / firstboot / firstboot.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <shadow.h>
27
28 #include "strv.h"
29 #include "fileio.h"
30 #include "copy.h"
31 #include "build.h"
32 #include "mkdir.h"
33 #include "time-util.h"
34 #include "path-util.h"
35 #include "locale-util.h"
36 #include "ask-password-api.h"
37
38 static char *arg_root = NULL;
39 static char *arg_locale = NULL;  /* $LANG */
40 static char *arg_locale_messages = NULL; /* $LC_MESSAGES */
41 static char *arg_timezone = NULL;
42 static char *arg_hostname = NULL;
43 static sd_id128_t arg_machine_id = {};
44 static char *arg_root_password = NULL;
45 static bool arg_prompt_locale = false;
46 static bool arg_prompt_timezone = false;
47 static bool arg_prompt_hostname = false;
48 static bool arg_prompt_root_password = false;
49 static bool arg_copy_locale = false;
50 static bool arg_copy_timezone = false;
51 static bool arg_copy_root_password = false;
52
53 #define prefix_roota(p) (arg_root ? (const char*) strappenda(arg_root, p) : (const char*) p)
54
55 static void clear_string(char *x) {
56
57         if (!x)
58                 return;
59
60         /* A delicious drop of snake-oil! */
61         memset(x, 'x', strlen(x));
62 }
63
64 static bool press_any_key(void) {
65         char k = 0;
66         bool need_nl = true;
67
68         printf("-- Press any key to proceed --");
69         fflush(stdout);
70
71         read_one_char(stdin, &k, USEC_INFINITY, &need_nl);
72
73         if (need_nl)
74                 putchar('\n');
75
76         return k != 'q';
77 }
78
79 static void print_welcome(void) {
80         _cleanup_free_ char *pretty_name = NULL;
81         const char *os_release = NULL;
82         static bool done = false;
83         int r;
84
85         if (done)
86                 return;
87
88         os_release = prefix_roota("/etc/os-release");
89         r = parse_env_file(os_release, NEWLINE,
90                            "PRETTY_NAME", &pretty_name,
91                            NULL);
92         if (r == -ENOENT) {
93
94                 os_release = prefix_roota("/usr/lib/os-release");
95                 r = parse_env_file(os_release, NEWLINE,
96                                    "PRETTY_NAME", &pretty_name,
97                                    NULL);
98         }
99
100         if (r < 0 && r != -ENOENT)
101                 log_warning("Failed to read os-release file: %s", strerror(-r));
102
103         printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n",
104                isempty(pretty_name) ? "Linux" : pretty_name);
105
106         press_any_key();
107
108         done = true;
109 }
110
111 static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage) {
112         unsigned n, per_column, i, j;
113         unsigned break_lines, break_modulo;
114
115         assert(n_columns > 0);
116
117         n = strv_length(x);
118         per_column = (n + n_columns - 1) / n_columns;
119
120         break_lines = lines();
121         if (break_lines > 2)
122                 break_lines--;
123
124         /* The first page gets two extra lines, since we want to show
125          * a title */
126         break_modulo = break_lines;
127         if (break_modulo > 3)
128                 break_modulo -= 3;
129
130         for (i = 0; i < per_column; i++) {
131
132                 for (j = 0; j < n_columns; j ++) {
133                         _cleanup_free_ char *e = NULL;
134
135                         if (j * per_column + i >= n)
136                                 break;
137
138                         e = ellipsize(x[j * per_column + i], width, percentage);
139                         if (!e)
140                                 return log_oom();
141
142                         printf("%4u) %-*s", j * per_column + i + 1, width, e);
143                 }
144
145                 putchar('\n');
146
147                 /* on the first screen we reserve 2 extra lines for the title */
148                 if (i % break_lines == break_modulo) {
149                         if (!press_any_key())
150                                 return 0;
151                 }
152         }
153
154         return 0;
155 }
156
157 static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *name), char **ret) {
158         int r;
159
160         assert(text);
161         assert(is_valid);
162         assert(ret);
163
164         for (;;) {
165                 _cleanup_free_ char *p = NULL;
166                 unsigned u;
167
168                 r = ask_string(&p, "%s %s (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET), text);
169                 if (r < 0) {
170                         log_error("Failed to query user: %s", strerror(-r));
171                         return r;
172                 }
173
174                 if (isempty(p)) {
175                         log_warning("No data entered, skipping.");
176                         return 0;
177                 }
178
179                 r = safe_atou(p, &u);
180                 if (r >= 0) {
181                         char *c;
182
183                         if (u <= 0 || u > strv_length(l)) {
184                                 log_error("Specified entry number out of range.");
185                                 continue;
186                         }
187
188                         log_info("Selected '%s'.", l[u-1]);
189
190                         c = strdup(l[u-1]);
191                         if (!c)
192                                 return log_oom();
193
194                         free(*ret);
195                         *ret = c;
196                         return 0;
197                 }
198
199                 if (!is_valid(p)) {
200                         log_error("Entered data invalid.");
201                         continue;
202                 }
203
204                 free(*ret);
205                 *ret = p;
206                 p = 0;
207                 return 0;
208         }
209 }
210
211 static int prompt_locale(void) {
212         _cleanup_strv_free_ char **locales = NULL;
213         int r;
214
215         if (arg_locale || arg_locale_messages)
216                 return 0;
217
218         if (!arg_prompt_locale)
219                 return 0;
220
221         r = get_locales(&locales);
222         if (r < 0) {
223                 log_error("Cannot query locales list: %s", strerror(-r));
224                 return r;
225         }
226
227         print_welcome();
228
229         printf("\nAvailable Locales:\n\n");
230         r = show_menu(locales, 3, 22, 60);
231         if (r < 0)
232                 return r;
233
234         putchar('\n');
235
236         r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale);
237         if (r < 0)
238                 return r;
239
240         if (isempty(arg_locale))
241                 return 0;
242
243         r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages);
244         if (r < 0)
245                 return r;
246
247         return 0;
248 }
249
250 static int process_locale(void) {
251         const char *etc_localeconf;
252         char* locales[3];
253         unsigned i = 0;
254         int r;
255
256         etc_localeconf = prefix_roota("/etc/locale.conf");
257         if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
258                 return 0;
259
260         if (arg_copy_locale && arg_root) {
261
262                 mkdir_parents(etc_localeconf, 0755);
263                 r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644);
264                 if (r != -ENOENT) {
265                         if (r < 0) {
266                                 log_error("Failed to copy %s: %s", etc_localeconf, strerror(-r));
267                                 return r;
268                         }
269
270                         log_info("%s copied.", etc_localeconf);
271                         return 0;
272                 }
273         }
274
275         r = prompt_locale();
276         if (r < 0)
277                 return r;
278
279         if (!isempty(arg_locale))
280                 locales[i++] = strappenda("LANG=", arg_locale);
281         if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale))
282                 locales[i++] = strappenda("LC_MESSAGES=", arg_locale_messages);
283
284         if (i == 0)
285                 return 0;
286
287         locales[i] = NULL;
288
289         mkdir_parents(etc_localeconf, 0755);
290         r = write_env_file(etc_localeconf, locales);
291         if (r < 0) {
292                 log_error("Failed to write %s: %s", etc_localeconf, strerror(-r));
293                 return r;
294         }
295
296         log_info("%s written.", etc_localeconf);
297         return 0;
298 }
299
300 static int prompt_timezone(void) {
301         _cleanup_strv_free_ char **zones = NULL;
302         int r;
303
304         if (arg_timezone)
305                 return 0;
306
307         if (!arg_prompt_timezone)
308                 return 0;
309
310         r = get_timezones(&zones);
311         if (r < 0) {
312                 log_error("Cannot query timezone list: %s", strerror(-r));
313                 return r;
314         }
315
316         print_welcome();
317
318         printf("\nAvailable Time Zones:\n\n");
319         r = show_menu(zones, 3, 22, 30);
320         if (r < 0)
321                 return r;
322
323         putchar('\n');
324
325         r = prompt_loop("Please enter timezone name or number", zones, timezone_is_valid, &arg_timezone);
326         if (r < 0)
327                 return r;
328
329         return 0;
330 }
331
332 static int process_timezone(void) {
333         const char *etc_localtime, *e;
334         int r;
335
336         etc_localtime = prefix_roota("/etc/localtime");
337         if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
338                 return 0;
339
340         if (arg_copy_timezone && arg_root) {
341                 _cleanup_free_ char *p = NULL;
342
343                 r = readlink_malloc("/etc/localtime", &p);
344                 if (r != -ENOENT) {
345                         if (r < 0) {
346                                 log_error("Failed to read host timezone: %s", strerror(-r));
347                                 return r;
348                         }
349
350                         mkdir_parents(etc_localtime, 0755);
351                         if (symlink(p, etc_localtime) < 0) {
352                                 log_error("Failed to create %s symlink: %m", etc_localtime);
353                                 return -errno;
354                         }
355
356                         log_info("%s copied.", etc_localtime);
357                         return 0;
358                 }
359         }
360
361         r = prompt_timezone();
362         if (r < 0)
363                 return r;
364
365         if (isempty(arg_timezone))
366                 return 0;
367
368         e = strappenda("../usr/share/zoneinfo/", arg_timezone);
369
370         mkdir_parents(etc_localtime, 0755);
371         if (symlink(e, etc_localtime) < 0) {
372                 log_error("Failed to create %s symlink: %m", etc_localtime);
373                 return -errno;
374         }
375
376         log_info("%s written", etc_localtime);
377         return 0;
378 }
379
380 static int prompt_hostname(void) {
381         int r;
382
383         if (arg_hostname)
384                 return 0;
385
386         if (!arg_prompt_hostname)
387                 return 0;
388
389         print_welcome();
390         putchar('\n');
391
392         for (;;) {
393                 _cleanup_free_ char *h = NULL;
394
395                 r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET));
396                 if (r < 0) {
397                         log_error("Failed to query hostname: %s", strerror(-r));
398                         return r;
399                 }
400
401                 if (isempty(h)) {
402                         log_warning("No hostname entered, skipping.");
403                         break;
404                 }
405
406                 if (!hostname_is_valid(h)) {
407                         log_error("Specified hostname invalid.");
408                         continue;
409                 }
410
411                 arg_hostname = h;
412                 h = NULL;
413                 break;
414         }
415
416         return 0;
417 }
418
419 static int process_hostname(void) {
420         const char *etc_hostname;
421         int r;
422
423         etc_hostname = prefix_roota("/etc/hostname");
424         if (faccessat(AT_FDCWD, etc_hostname, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
425                 return 0;
426
427         r = prompt_hostname();
428         if (r < 0)
429                 return r;
430
431         if (isempty(arg_hostname))
432                 return 0;
433
434         mkdir_parents(etc_hostname, 0755);
435         r = write_string_file(etc_hostname, arg_hostname);
436         if (r < 0) {
437                 log_error("Failed to write %s: %s", etc_hostname, strerror(-r));
438                 return r;
439         }
440
441         log_info("%s written.", etc_hostname);
442         return 0;
443 }
444
445 static int process_machine_id(void) {
446         const char *etc_machine_id;
447         char id[SD_ID128_STRING_MAX];
448         int r;
449
450         etc_machine_id = prefix_roota("/etc/machine-id");
451         if (faccessat(AT_FDCWD, etc_machine_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
452                 return 0;
453
454         if (!arg_root)
455                 return 0;
456
457         if (sd_id128_equal(arg_machine_id, SD_ID128_NULL))
458                 return 0;
459
460         mkdir_parents(etc_machine_id, 0755);
461         r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id));
462         if (r < 0) {
463                 log_error("Failed to write machine id: %s", strerror(-r));
464                 return r;
465         }
466
467         log_info("%s written.", etc_machine_id);
468         return 0;
469 }
470
471 static int prompt_root_password(void) {
472         const char *msg1, *msg2, *etc_shadow;
473         int r;
474
475         if (arg_root_password)
476                 return 0;
477
478         if (!arg_prompt_root_password)
479                 return 0;
480
481         etc_shadow = prefix_roota("/etc/shadow");
482         if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
483                 return 0;
484
485         print_welcome();
486         putchar('\n');
487
488         msg1 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
489         msg2 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: ");
490
491         for (;;) {
492                 _cleanup_free_ char *a = NULL, *b = NULL;
493
494                 r = ask_password_tty(msg1, 0, NULL, &a);
495                 if (r < 0) {
496                         log_error("Failed to query root password: %s", strerror(-r));
497                         return r;
498                 }
499
500                 if (isempty(a)) {
501                         log_warning("No password entered, skipping.");
502                         break;
503                 }
504
505                 r = ask_password_tty(msg2, 0, NULL, &b);
506                 if (r < 0) {
507                         log_error("Failed to query root password: %s", strerror(-r));
508                         clear_string(a);
509                         return r;
510                 }
511
512                 if (!streq(a, b)) {
513                         log_error("Entered passwords did not match, please try again.");
514                         clear_string(a);
515                         clear_string(b);
516                         continue;
517                 }
518
519                 clear_string(b);
520                 arg_root_password = a;
521                 a = NULL;
522                 break;
523         }
524
525         return 0;
526 }
527
528 static int write_root_shadow(const char *path, const struct spwd *p) {
529         _cleanup_fclose_ FILE *f = NULL;
530         assert(path);
531         assert(p);
532
533         RUN_WITH_UMASK(0777)
534                 f = fopen(path, "wex");
535         if (!f)
536                 return -errno;
537
538         errno = 0;
539         if (putspent(p, f) != 0)
540                 return errno ? -errno : -EIO;
541
542         return fflush_and_check(f);
543 }
544
545 static int process_root_password(void) {
546
547         static const char table[] =
548                 "abcdefghijklmnopqrstuvwxyz"
549                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
550                 "0123456789"
551                 "./";
552
553         struct spwd item = {
554                 .sp_namp = (char*) "root",
555                 .sp_min = 0,
556                 .sp_max = 99999,
557                 .sp_warn = 7,
558                 .sp_inact = -1,
559                 .sp_expire = -1,
560                 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
561         };
562
563         _cleanup_close_ int lock = -1;
564         char salt[3+16+1+1];
565         uint8_t raw[16];
566         unsigned i;
567         char *j;
568
569         const char *etc_shadow;
570         int r;
571
572         etc_shadow = prefix_roota("/etc/shadow");
573         if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
574                 return 0;
575
576         mkdir_parents(etc_shadow, 0755);
577
578         lock = take_password_lock(arg_root);
579         if (lock < 0)
580                 return lock;
581
582         if (arg_copy_root_password && arg_root) {
583                 struct spwd *p;
584
585                 errno = 0;
586                 p = getspnam("root");
587                 if (p || errno != ENOENT) {
588                         if (!p) {
589                                 if (!errno)
590                                         errno = EIO;
591
592                                 log_error("Failed to find shadow entry for root: %m");
593                                 return -errno;
594                         }
595
596                         r = write_root_shadow(etc_shadow, p);
597                         if (r < 0) {
598                                 log_error("Failed to write %s: %s", etc_shadow, strerror(-r));
599                                 return r;
600                         }
601
602                         log_info("%s copied.", etc_shadow);
603                         return 0;
604                 }
605         }
606
607         r = prompt_root_password();
608         if (r < 0)
609                 return r;
610
611         if (!arg_root_password)
612                 return 0;
613
614         r = dev_urandom(raw, 16);
615         if (r < 0) {
616                 log_error("Failed to get salt: %s", strerror(-r));
617                 return r;
618         }
619
620         /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
621         assert_cc(sizeof(table) == 64 + 1);
622         j = stpcpy(salt, "$6$");
623         for (i = 0; i < 16; i++)
624                 j[i] = table[raw[i] & 63];
625         j[i++] = '$';
626         j[i] = 0;
627
628         errno = 0;
629         item.sp_pwdp = crypt(arg_root_password, salt);
630         if (!item.sp_pwdp) {
631                 if (!errno)
632                         errno = -EINVAL;
633
634                 log_error("Failed to encrypt password: %m");
635                 return -errno;
636         }
637
638         item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
639
640         r = write_root_shadow(etc_shadow, &item);
641         if (r < 0) {
642                 log_error("Failed to write %s: %s", etc_shadow, strerror(-r));
643                 return r;
644         }
645
646         log_info("%s written.", etc_shadow);
647         return 0;
648 }
649
650 static void help(void) {
651         printf("%s [OPTIONS...]\n\n"
652                "Configures basic settings of the system.\n\n"
653                "  -h --help                    Show this help\n"
654                "     --version                 Show package version\n"
655                "     --root=PATH               Operate on an alternate filesystem root\n"
656                "     --locale=LOCALE           Set primary locale (LANG=)\n"
657                "     --locale-messages=LOCALE  Set message locale (LC_MESSAGES=)\n"
658                "     --timezone=TIMEZONE       Set timezone\n"
659                "     --hostname=NAME           Set host name\n"
660                "     --machine-ID=ID           Set machine ID\n"
661                "     --root-password=PASSWORD  Set root password\n"
662                "     --root-password-file=FILE Set root password from file\n"
663                "     --prompt-locale           Prompt the user for locale settings\n"
664                "     --prompt-timezone         Prompt the user for timezone\n"
665                "     --prompt-hostname         Prompt the user for hostname\n"
666                "     --prompt-root-password    Prompt the user for root password\n"
667                "     --prompt                  Prompt for locale, timezone, hostname, root password\n"
668                "     --copy-locale             Copy locale from host\n"
669                "     --copy-timezone           Copy timezone from host\n"
670                "     --copy-root-password      Copy root password from host\n"
671                "     --copy                    Copy locale, timezone, root password\n"
672                "     --setup-machine-id        Generate a new random machine ID\n"
673                , program_invocation_short_name);
674 }
675
676 static int parse_argv(int argc, char *argv[]) {
677
678         enum {
679                 ARG_VERSION = 0x100,
680                 ARG_ROOT,
681                 ARG_LOCALE,
682                 ARG_LOCALE_MESSAGES,
683                 ARG_TIMEZONE,
684                 ARG_HOSTNAME,
685                 ARG_MACHINE_ID,
686                 ARG_ROOT_PASSWORD,
687                 ARG_ROOT_PASSWORD_FILE,
688                 ARG_PROMPT,
689                 ARG_PROMPT_LOCALE,
690                 ARG_PROMPT_TIMEZONE,
691                 ARG_PROMPT_HOSTNAME,
692                 ARG_PROMPT_ROOT_PASSWORD,
693                 ARG_COPY,
694                 ARG_COPY_LOCALE,
695                 ARG_COPY_TIMEZONE,
696                 ARG_COPY_ROOT_PASSWORD,
697                 ARG_SETUP_MACHINE_ID,
698         };
699
700         static const struct option options[] = {
701                 { "help",                 no_argument,       NULL, 'h'                      },
702                 { "version",              no_argument,       NULL, ARG_VERSION              },
703                 { "root",                 required_argument, NULL, ARG_ROOT                 },
704                 { "locale",               required_argument, NULL, ARG_LOCALE               },
705                 { "locale-messages",      required_argument, NULL, ARG_LOCALE_MESSAGES      },
706                 { "timezone",             required_argument, NULL, ARG_TIMEZONE             },
707                 { "hostname",             required_argument, NULL, ARG_HOSTNAME             },
708                 { "machine-id",           required_argument, NULL, ARG_MACHINE_ID           },
709                 { "root-password",        required_argument, NULL, ARG_ROOT_PASSWORD        },
710                 { "root-password-file",   required_argument, NULL, ARG_ROOT_PASSWORD_FILE   },
711                 { "prompt",               no_argument,       NULL, ARG_PROMPT               },
712                 { "prompt-locale",        no_argument,       NULL, ARG_PROMPT_LOCALE        },
713                 { "prompt-timezone",      no_argument,       NULL, ARG_PROMPT_TIMEZONE      },
714                 { "prompt-hostname",      no_argument,       NULL, ARG_PROMPT_HOSTNAME      },
715                 { "prompt-root-password", no_argument,       NULL, ARG_PROMPT_ROOT_PASSWORD },
716                 { "copy",                 no_argument,       NULL, ARG_COPY                 },
717                 { "copy-locale",          no_argument,       NULL, ARG_COPY_LOCALE          },
718                 { "copy-timezone",        no_argument,       NULL, ARG_COPY_TIMEZONE        },
719                 { "copy-root-password",   no_argument,       NULL, ARG_COPY_ROOT_PASSWORD   },
720                 { "setup-machine-id",     no_argument,       NULL, ARG_SETUP_MACHINE_ID     },
721                 {}
722         };
723
724         int r, c;
725
726         assert(argc >= 0);
727         assert(argv);
728
729         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
730
731                 switch (c) {
732
733                 case 'h':
734                         help();
735                         return 0;
736
737                 case ARG_VERSION:
738                         puts(PACKAGE_STRING);
739                         puts(SYSTEMD_FEATURES);
740                         return 0;
741
742                 case ARG_ROOT:
743                         free(arg_root);
744                         arg_root = path_make_absolute_cwd(optarg);
745                         if (!arg_root)
746                                 return log_oom();
747
748                         path_kill_slashes(arg_root);
749
750                         if (path_equal(arg_root, "/")) {
751                                 free(arg_root);
752                                 arg_root = NULL;
753                         }
754
755                         break;
756
757                 case ARG_LOCALE:
758                         if (!locale_is_valid(optarg)) {
759                                 log_error("Locale %s is not valid.", optarg);
760                                 return -EINVAL;
761                         }
762
763                         free(arg_locale);
764                         arg_locale = strdup(optarg);
765                         if (!arg_locale)
766                                 return log_oom();
767
768                         break;
769
770                 case ARG_LOCALE_MESSAGES:
771                         if (!locale_is_valid(optarg)) {
772                                 log_error("Locale %s is not valid.", optarg);
773                                 return -EINVAL;
774                         }
775
776                         free(arg_locale_messages);
777                         arg_locale_messages = strdup(optarg);
778                         if (!arg_locale_messages)
779                                 return log_oom();
780
781                         break;
782
783                 case ARG_TIMEZONE:
784                         if (!timezone_is_valid(optarg)) {
785                                 log_error("Timezone %s is not valid.", optarg);
786                                 return -EINVAL;
787                         }
788
789                         free(arg_timezone);
790                         arg_timezone = strdup(optarg);
791                         if (!arg_timezone)
792                                 return log_oom();
793
794                         break;
795
796                 case ARG_ROOT_PASSWORD:
797                         free(arg_root_password);
798                         arg_root_password = strdup(optarg);
799                         if (!arg_root_password)
800                                 return log_oom();
801
802                         break;
803
804                 case ARG_ROOT_PASSWORD_FILE:
805                         free(arg_root_password);
806                         arg_root_password  = NULL;
807
808                         r = read_one_line_file(optarg, &arg_root_password);
809                         if (r < 0) {
810                                 log_error("Failed to read %s: %s", optarg, strerror(-r));
811                                 return r;
812                         }
813
814                         break;
815
816                 case ARG_HOSTNAME:
817                         if (!hostname_is_valid(optarg)) {
818                                 log_error("Host name %s is not valid.", optarg);
819                                 return -EINVAL;
820                         }
821
822                         free(arg_hostname);
823                         arg_hostname = strdup(optarg);
824                         if (!arg_hostname)
825                                 return log_oom();
826
827                         break;
828
829                 case ARG_MACHINE_ID:
830                         if (sd_id128_from_string(optarg, &arg_machine_id) < 0) {
831                                 log_error("Failed to parse machine id %s.", optarg);
832                                 return -EINVAL;
833                         }
834
835                         break;
836
837                 case ARG_PROMPT:
838                         arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true;
839                         break;
840
841                 case ARG_PROMPT_LOCALE:
842                         arg_prompt_locale = true;
843                         break;
844
845                 case ARG_PROMPT_TIMEZONE:
846                         arg_prompt_timezone = true;
847                         break;
848
849                 case ARG_PROMPT_HOSTNAME:
850                         arg_prompt_hostname = true;
851                         break;
852
853                 case ARG_PROMPT_ROOT_PASSWORD:
854                         arg_prompt_root_password = true;
855                         break;
856
857                 case ARG_COPY:
858                         arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true;
859
860                 case ARG_COPY_LOCALE:
861                         arg_copy_locale = true;
862                         break;
863
864                 case ARG_COPY_TIMEZONE:
865                         arg_copy_timezone = true;
866                         break;
867
868                 case ARG_COPY_ROOT_PASSWORD:
869                         arg_copy_root_password = true;
870                         break;
871
872                 case ARG_SETUP_MACHINE_ID:
873
874                         r = sd_id128_randomize(&arg_machine_id);
875                         if (r < 0) {
876                                 log_error("Failed to generate randomized machine ID: %s", strerror(-r));
877                                 return r;
878                         }
879
880                         break;
881
882                 case '?':
883                         return -EINVAL;
884
885                 default:
886                         assert_not_reached("Unhandled option");
887                 }
888
889         return 1;
890 }
891
892 int main(int argc, char *argv[]) {
893         int r;
894
895         r = parse_argv(argc, argv);
896         if (r <= 0)
897                 goto finish;
898
899         log_set_target(LOG_TARGET_AUTO);
900         log_parse_environment();
901         log_open();
902
903         umask(0022);
904
905         r = process_locale();
906         if (r < 0)
907                 goto finish;
908
909         r = process_timezone();
910         if (r < 0)
911                 goto finish;
912
913         r = process_hostname();
914         if (r < 0)
915                 goto finish;
916
917         r = process_machine_id();
918         if (r < 0)
919                 goto finish;
920
921         r = process_root_password();
922         if (r < 0)
923                 goto finish;
924
925 finish:
926         free(arg_root);
927         free(arg_locale);
928         free(arg_locale_messages);
929         free(arg_timezone);
930         free(arg_hostname);
931         clear_string(arg_root_password);
932         free(arg_root_password);
933
934         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
935 }