chiark / gitweb /
core: introduce ConditionSecurity=audit
[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         (void) 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 (sd_id128_equal(arg_machine_id, SD_ID128_NULL))
455                 return 0;
456
457         mkdir_parents(etc_machine_id, 0755);
458         r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id));
459         if (r < 0) {
460                 log_error("Failed to write machine id: %s", strerror(-r));
461                 return r;
462         }
463
464         log_info("%s written.", etc_machine_id);
465         return 0;
466 }
467
468 static int prompt_root_password(void) {
469         const char *msg1, *msg2, *etc_shadow;
470         int r;
471
472         if (arg_root_password)
473                 return 0;
474
475         if (!arg_prompt_root_password)
476                 return 0;
477
478         etc_shadow = prefix_roota("/etc/shadow");
479         if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
480                 return 0;
481
482         print_welcome();
483         putchar('\n');
484
485         msg1 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
486         msg2 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: ");
487
488         for (;;) {
489                 _cleanup_free_ char *a = NULL, *b = NULL;
490
491                 r = ask_password_tty(msg1, 0, false, NULL, &a);
492                 if (r < 0) {
493                         log_error("Failed to query root password: %s", strerror(-r));
494                         return r;
495                 }
496
497                 if (isempty(a)) {
498                         log_warning("No password entered, skipping.");
499                         break;
500                 }
501
502                 r = ask_password_tty(msg2, 0, false, NULL, &b);
503                 if (r < 0) {
504                         log_error("Failed to query root password: %s", strerror(-r));
505                         clear_string(a);
506                         return r;
507                 }
508
509                 if (!streq(a, b)) {
510                         log_error("Entered passwords did not match, please try again.");
511                         clear_string(a);
512                         clear_string(b);
513                         continue;
514                 }
515
516                 clear_string(b);
517                 arg_root_password = a;
518                 a = NULL;
519                 break;
520         }
521
522         return 0;
523 }
524
525 static int write_root_shadow(const char *path, const struct spwd *p) {
526         _cleanup_fclose_ FILE *f = NULL;
527         assert(path);
528         assert(p);
529
530         RUN_WITH_UMASK(0777)
531                 f = fopen(path, "wex");
532         if (!f)
533                 return -errno;
534
535         errno = 0;
536         if (putspent(p, f) != 0)
537                 return errno ? -errno : -EIO;
538
539         return fflush_and_check(f);
540 }
541
542 static int process_root_password(void) {
543
544         static const char table[] =
545                 "abcdefghijklmnopqrstuvwxyz"
546                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
547                 "0123456789"
548                 "./";
549
550         struct spwd item = {
551                 .sp_namp = (char*) "root",
552                 .sp_min = 0,
553                 .sp_max = 99999,
554                 .sp_warn = 7,
555                 .sp_inact = -1,
556                 .sp_expire = -1,
557                 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
558         };
559
560         _cleanup_close_ int lock = -1;
561         char salt[3+16+1+1];
562         uint8_t raw[16];
563         unsigned i;
564         char *j;
565
566         const char *etc_shadow;
567         int r;
568
569         etc_shadow = prefix_roota("/etc/shadow");
570         if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
571                 return 0;
572
573         mkdir_parents(etc_shadow, 0755);
574
575         lock = take_password_lock(arg_root);
576         if (lock < 0)
577                 return lock;
578
579         if (arg_copy_root_password && arg_root) {
580                 struct spwd *p;
581
582                 errno = 0;
583                 p = getspnam("root");
584                 if (p || errno != ENOENT) {
585                         if (!p) {
586                                 if (!errno)
587                                         errno = EIO;
588
589                                 log_error("Failed to find shadow entry for root: %m");
590                                 return -errno;
591                         }
592
593                         r = write_root_shadow(etc_shadow, p);
594                         if (r < 0) {
595                                 log_error("Failed to write %s: %s", etc_shadow, strerror(-r));
596                                 return r;
597                         }
598
599                         log_info("%s copied.", etc_shadow);
600                         return 0;
601                 }
602         }
603
604         r = prompt_root_password();
605         if (r < 0)
606                 return r;
607
608         if (!arg_root_password)
609                 return 0;
610
611         r = dev_urandom(raw, 16);
612         if (r < 0) {
613                 log_error("Failed to get salt: %s", strerror(-r));
614                 return r;
615         }
616
617         /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
618         assert_cc(sizeof(table) == 64 + 1);
619         j = stpcpy(salt, "$6$");
620         for (i = 0; i < 16; i++)
621                 j[i] = table[raw[i] & 63];
622         j[i++] = '$';
623         j[i] = 0;
624
625         errno = 0;
626         item.sp_pwdp = crypt(arg_root_password, salt);
627         if (!item.sp_pwdp) {
628                 if (!errno)
629                         errno = -EINVAL;
630
631                 log_error("Failed to encrypt password: %m");
632                 return -errno;
633         }
634
635         item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
636
637         r = write_root_shadow(etc_shadow, &item);
638         if (r < 0) {
639                 log_error("Failed to write %s: %s", etc_shadow, strerror(-r));
640                 return r;
641         }
642
643         log_info("%s written.", etc_shadow);
644         return 0;
645 }
646
647 static void help(void) {
648         printf("%s [OPTIONS...]\n\n"
649                "Configures basic settings of the system.\n\n"
650                "  -h --help                    Show this help\n"
651                "     --version                 Show package version\n"
652                "     --root=PATH               Operate on an alternate filesystem root\n"
653                "     --locale=LOCALE           Set primary locale (LANG=)\n"
654                "     --locale-messages=LOCALE  Set message locale (LC_MESSAGES=)\n"
655                "     --timezone=TIMEZONE       Set timezone\n"
656                "     --hostname=NAME           Set host name\n"
657                "     --machine-ID=ID           Set machine ID\n"
658                "     --root-password=PASSWORD  Set root password\n"
659                "     --root-password-file=FILE Set root password from file\n"
660                "     --prompt-locale           Prompt the user for locale settings\n"
661                "     --prompt-timezone         Prompt the user for timezone\n"
662                "     --prompt-hostname         Prompt the user for hostname\n"
663                "     --prompt-root-password    Prompt the user for root password\n"
664                "     --prompt                  Prompt for locale, timezone, hostname, root password\n"
665                "     --copy-locale             Copy locale from host\n"
666                "     --copy-timezone           Copy timezone from host\n"
667                "     --copy-root-password      Copy root password from host\n"
668                "     --copy                    Copy locale, timezone, root password\n"
669                "     --setup-machine-id        Generate a new random machine ID\n"
670                , program_invocation_short_name);
671 }
672
673 static int parse_argv(int argc, char *argv[]) {
674
675         enum {
676                 ARG_VERSION = 0x100,
677                 ARG_ROOT,
678                 ARG_LOCALE,
679                 ARG_LOCALE_MESSAGES,
680                 ARG_TIMEZONE,
681                 ARG_HOSTNAME,
682                 ARG_MACHINE_ID,
683                 ARG_ROOT_PASSWORD,
684                 ARG_ROOT_PASSWORD_FILE,
685                 ARG_PROMPT,
686                 ARG_PROMPT_LOCALE,
687                 ARG_PROMPT_TIMEZONE,
688                 ARG_PROMPT_HOSTNAME,
689                 ARG_PROMPT_ROOT_PASSWORD,
690                 ARG_COPY,
691                 ARG_COPY_LOCALE,
692                 ARG_COPY_TIMEZONE,
693                 ARG_COPY_ROOT_PASSWORD,
694                 ARG_SETUP_MACHINE_ID,
695         };
696
697         static const struct option options[] = {
698                 { "help",                 no_argument,       NULL, 'h'                      },
699                 { "version",              no_argument,       NULL, ARG_VERSION              },
700                 { "root",                 required_argument, NULL, ARG_ROOT                 },
701                 { "locale",               required_argument, NULL, ARG_LOCALE               },
702                 { "locale-messages",      required_argument, NULL, ARG_LOCALE_MESSAGES      },
703                 { "timezone",             required_argument, NULL, ARG_TIMEZONE             },
704                 { "hostname",             required_argument, NULL, ARG_HOSTNAME             },
705                 { "machine-id",           required_argument, NULL, ARG_MACHINE_ID           },
706                 { "root-password",        required_argument, NULL, ARG_ROOT_PASSWORD        },
707                 { "root-password-file",   required_argument, NULL, ARG_ROOT_PASSWORD_FILE   },
708                 { "prompt",               no_argument,       NULL, ARG_PROMPT               },
709                 { "prompt-locale",        no_argument,       NULL, ARG_PROMPT_LOCALE        },
710                 { "prompt-timezone",      no_argument,       NULL, ARG_PROMPT_TIMEZONE      },
711                 { "prompt-hostname",      no_argument,       NULL, ARG_PROMPT_HOSTNAME      },
712                 { "prompt-root-password", no_argument,       NULL, ARG_PROMPT_ROOT_PASSWORD },
713                 { "copy",                 no_argument,       NULL, ARG_COPY                 },
714                 { "copy-locale",          no_argument,       NULL, ARG_COPY_LOCALE          },
715                 { "copy-timezone",        no_argument,       NULL, ARG_COPY_TIMEZONE        },
716                 { "copy-root-password",   no_argument,       NULL, ARG_COPY_ROOT_PASSWORD   },
717                 { "setup-machine-id",     no_argument,       NULL, ARG_SETUP_MACHINE_ID     },
718                 {}
719         };
720
721         int r, c;
722
723         assert(argc >= 0);
724         assert(argv);
725
726         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
727
728                 switch (c) {
729
730                 case 'h':
731                         help();
732                         return 0;
733
734                 case ARG_VERSION:
735                         puts(PACKAGE_STRING);
736                         puts(SYSTEMD_FEATURES);
737                         return 0;
738
739                 case ARG_ROOT:
740                         free(arg_root);
741                         arg_root = path_make_absolute_cwd(optarg);
742                         if (!arg_root)
743                                 return log_oom();
744
745                         path_kill_slashes(arg_root);
746
747                         if (path_equal(arg_root, "/")) {
748                                 free(arg_root);
749                                 arg_root = NULL;
750                         }
751
752                         break;
753
754                 case ARG_LOCALE:
755                         if (!locale_is_valid(optarg)) {
756                                 log_error("Locale %s is not valid.", optarg);
757                                 return -EINVAL;
758                         }
759
760                         free(arg_locale);
761                         arg_locale = strdup(optarg);
762                         if (!arg_locale)
763                                 return log_oom();
764
765                         break;
766
767                 case ARG_LOCALE_MESSAGES:
768                         if (!locale_is_valid(optarg)) {
769                                 log_error("Locale %s is not valid.", optarg);
770                                 return -EINVAL;
771                         }
772
773                         free(arg_locale_messages);
774                         arg_locale_messages = strdup(optarg);
775                         if (!arg_locale_messages)
776                                 return log_oom();
777
778                         break;
779
780                 case ARG_TIMEZONE:
781                         if (!timezone_is_valid(optarg)) {
782                                 log_error("Timezone %s is not valid.", optarg);
783                                 return -EINVAL;
784                         }
785
786                         free(arg_timezone);
787                         arg_timezone = strdup(optarg);
788                         if (!arg_timezone)
789                                 return log_oom();
790
791                         break;
792
793                 case ARG_ROOT_PASSWORD:
794                         free(arg_root_password);
795                         arg_root_password = strdup(optarg);
796                         if (!arg_root_password)
797                                 return log_oom();
798
799                         break;
800
801                 case ARG_ROOT_PASSWORD_FILE:
802                         free(arg_root_password);
803                         arg_root_password  = NULL;
804
805                         r = read_one_line_file(optarg, &arg_root_password);
806                         if (r < 0) {
807                                 log_error("Failed to read %s: %s", optarg, strerror(-r));
808                                 return r;
809                         }
810
811                         break;
812
813                 case ARG_HOSTNAME:
814                         if (!hostname_is_valid(optarg)) {
815                                 log_error("Host name %s is not valid.", optarg);
816                                 return -EINVAL;
817                         }
818
819                         free(arg_hostname);
820                         arg_hostname = strdup(optarg);
821                         if (!arg_hostname)
822                                 return log_oom();
823
824                         break;
825
826                 case ARG_MACHINE_ID:
827                         if (sd_id128_from_string(optarg, &arg_machine_id) < 0) {
828                                 log_error("Failed to parse machine id %s.", optarg);
829                                 return -EINVAL;
830                         }
831
832                         break;
833
834                 case ARG_PROMPT:
835                         arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true;
836                         break;
837
838                 case ARG_PROMPT_LOCALE:
839                         arg_prompt_locale = true;
840                         break;
841
842                 case ARG_PROMPT_TIMEZONE:
843                         arg_prompt_timezone = true;
844                         break;
845
846                 case ARG_PROMPT_HOSTNAME:
847                         arg_prompt_hostname = true;
848                         break;
849
850                 case ARG_PROMPT_ROOT_PASSWORD:
851                         arg_prompt_root_password = true;
852                         break;
853
854                 case ARG_COPY:
855                         arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true;
856                         break;
857
858                 case ARG_COPY_LOCALE:
859                         arg_copy_locale = true;
860                         break;
861
862                 case ARG_COPY_TIMEZONE:
863                         arg_copy_timezone = true;
864                         break;
865
866                 case ARG_COPY_ROOT_PASSWORD:
867                         arg_copy_root_password = true;
868                         break;
869
870                 case ARG_SETUP_MACHINE_ID:
871
872                         r = sd_id128_randomize(&arg_machine_id);
873                         if (r < 0) {
874                                 log_error("Failed to generate randomized machine ID: %s", strerror(-r));
875                                 return r;
876                         }
877
878                         break;
879
880                 case '?':
881                         return -EINVAL;
882
883                 default:
884                         assert_not_reached("Unhandled option");
885                 }
886
887         return 1;
888 }
889
890 int main(int argc, char *argv[]) {
891         int r;
892
893         r = parse_argv(argc, argv);
894         if (r <= 0)
895                 goto finish;
896
897         log_set_target(LOG_TARGET_AUTO);
898         log_parse_environment();
899         log_open();
900
901         umask(0022);
902
903         r = process_locale();
904         if (r < 0)
905                 goto finish;
906
907         r = process_timezone();
908         if (r < 0)
909                 goto finish;
910
911         r = process_hostname();
912         if (r < 0)
913                 goto finish;
914
915         r = process_machine_id();
916         if (r < 0)
917                 goto finish;
918
919         r = process_root_password();
920         if (r < 0)
921                 goto finish;
922
923 finish:
924         free(arg_root);
925         free(arg_locale);
926         free(arg_locale_messages);
927         free(arg_timezone);
928         free(arg_hostname);
929         clear_string(arg_root_password);
930         free(arg_root_password);
931
932         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
933 }