chiark / gitweb /
treewide: more log_*_errno + return simplifications
[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_errno(r, "Failed to read os-release file: %m");
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                         return log_error_errno(r, "Failed to query user: %m");
171
172                 if (isempty(p)) {
173                         log_warning("No data entered, skipping.");
174                         return 0;
175                 }
176
177                 r = safe_atou(p, &u);
178                 if (r >= 0) {
179                         char *c;
180
181                         if (u <= 0 || u > strv_length(l)) {
182                                 log_error("Specified entry number out of range.");
183                                 continue;
184                         }
185
186                         log_info("Selected '%s'.", l[u-1]);
187
188                         c = strdup(l[u-1]);
189                         if (!c)
190                                 return log_oom();
191
192                         free(*ret);
193                         *ret = c;
194                         return 0;
195                 }
196
197                 if (!is_valid(p)) {
198                         log_error("Entered data invalid.");
199                         continue;
200                 }
201
202                 free(*ret);
203                 *ret = p;
204                 p = 0;
205                 return 0;
206         }
207 }
208
209 static int prompt_locale(void) {
210         _cleanup_strv_free_ char **locales = NULL;
211         int r;
212
213         if (arg_locale || arg_locale_messages)
214                 return 0;
215
216         if (!arg_prompt_locale)
217                 return 0;
218
219         r = get_locales(&locales);
220         if (r < 0)
221                 return log_error_errno(r, "Cannot query locales list: %m");
222
223         print_welcome();
224
225         printf("\nAvailable Locales:\n\n");
226         r = show_menu(locales, 3, 22, 60);
227         if (r < 0)
228                 return r;
229
230         putchar('\n');
231
232         r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale);
233         if (r < 0)
234                 return r;
235
236         if (isempty(arg_locale))
237                 return 0;
238
239         r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages);
240         if (r < 0)
241                 return r;
242
243         return 0;
244 }
245
246 static int process_locale(void) {
247         const char *etc_localeconf;
248         char* locales[3];
249         unsigned i = 0;
250         int r;
251
252         etc_localeconf = prefix_roota("/etc/locale.conf");
253         if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
254                 return 0;
255
256         if (arg_copy_locale && arg_root) {
257
258                 mkdir_parents(etc_localeconf, 0755);
259                 r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644);
260                 if (r != -ENOENT) {
261                         if (r < 0)
262                                 return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf);
263
264                         log_info("%s copied.", etc_localeconf);
265                         return 0;
266                 }
267         }
268
269         r = prompt_locale();
270         if (r < 0)
271                 return r;
272
273         if (!isempty(arg_locale))
274                 locales[i++] = strappenda("LANG=", arg_locale);
275         if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale))
276                 locales[i++] = strappenda("LC_MESSAGES=", arg_locale_messages);
277
278         if (i == 0)
279                 return 0;
280
281         locales[i] = NULL;
282
283         mkdir_parents(etc_localeconf, 0755);
284         r = write_env_file(etc_localeconf, locales);
285         if (r < 0)
286                 return log_error_errno(r, "Failed to write %s: %m", etc_localeconf);
287
288         log_info("%s written.", etc_localeconf);
289         return 0;
290 }
291
292 static int prompt_timezone(void) {
293         _cleanup_strv_free_ char **zones = NULL;
294         int r;
295
296         if (arg_timezone)
297                 return 0;
298
299         if (!arg_prompt_timezone)
300                 return 0;
301
302         r = get_timezones(&zones);
303         if (r < 0)
304                 return log_error_errno(r, "Cannot query timezone list: %m");
305
306         print_welcome();
307
308         printf("\nAvailable Time Zones:\n\n");
309         r = show_menu(zones, 3, 22, 30);
310         if (r < 0)
311                 return r;
312
313         putchar('\n');
314
315         r = prompt_loop("Please enter timezone name or number", zones, timezone_is_valid, &arg_timezone);
316         if (r < 0)
317                 return r;
318
319         return 0;
320 }
321
322 static int process_timezone(void) {
323         const char *etc_localtime, *e;
324         int r;
325
326         etc_localtime = prefix_roota("/etc/localtime");
327         if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
328                 return 0;
329
330         if (arg_copy_timezone && arg_root) {
331                 _cleanup_free_ char *p = NULL;
332
333                 r = readlink_malloc("/etc/localtime", &p);
334                 if (r != -ENOENT) {
335                         if (r < 0)
336                                 return log_error_errno(r, "Failed to read host timezone: %m");
337
338                         mkdir_parents(etc_localtime, 0755);
339                         if (symlink(p, etc_localtime) < 0) {
340                                 log_error("Failed to create %s symlink: %m", etc_localtime);
341                                 return -errno;
342                         }
343
344                         log_info("%s copied.", etc_localtime);
345                         return 0;
346                 }
347         }
348
349         r = prompt_timezone();
350         if (r < 0)
351                 return r;
352
353         if (isempty(arg_timezone))
354                 return 0;
355
356         e = strappenda("../usr/share/zoneinfo/", arg_timezone);
357
358         mkdir_parents(etc_localtime, 0755);
359         if (symlink(e, etc_localtime) < 0) {
360                 log_error("Failed to create %s symlink: %m", etc_localtime);
361                 return -errno;
362         }
363
364         log_info("%s written", etc_localtime);
365         return 0;
366 }
367
368 static int prompt_hostname(void) {
369         int r;
370
371         if (arg_hostname)
372                 return 0;
373
374         if (!arg_prompt_hostname)
375                 return 0;
376
377         print_welcome();
378         putchar('\n');
379
380         for (;;) {
381                 _cleanup_free_ char *h = NULL;
382
383                 r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET));
384                 if (r < 0)
385                         return log_error_errno(r, "Failed to query hostname: %m");
386
387                 if (isempty(h)) {
388                         log_warning("No hostname entered, skipping.");
389                         break;
390                 }
391
392                 if (!hostname_is_valid(h)) {
393                         log_error("Specified hostname invalid.");
394                         continue;
395                 }
396
397                 arg_hostname = h;
398                 h = NULL;
399                 break;
400         }
401
402         return 0;
403 }
404
405 static int process_hostname(void) {
406         const char *etc_hostname;
407         int r;
408
409         etc_hostname = prefix_roota("/etc/hostname");
410         if (faccessat(AT_FDCWD, etc_hostname, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
411                 return 0;
412
413         r = prompt_hostname();
414         if (r < 0)
415                 return r;
416
417         if (isempty(arg_hostname))
418                 return 0;
419
420         mkdir_parents(etc_hostname, 0755);
421         r = write_string_file(etc_hostname, arg_hostname);
422         if (r < 0)
423                 return log_error_errno(r, "Failed to write %s: %m", etc_hostname);
424
425         log_info("%s written.", etc_hostname);
426         return 0;
427 }
428
429 static int process_machine_id(void) {
430         const char *etc_machine_id;
431         char id[SD_ID128_STRING_MAX];
432         int r;
433
434         etc_machine_id = prefix_roota("/etc/machine-id");
435         if (faccessat(AT_FDCWD, etc_machine_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
436                 return 0;
437
438         if (sd_id128_equal(arg_machine_id, SD_ID128_NULL))
439                 return 0;
440
441         mkdir_parents(etc_machine_id, 0755);
442         r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id));
443         if (r < 0)
444                 return log_error_errno(r, "Failed to write machine id: %m");
445
446         log_info("%s written.", etc_machine_id);
447         return 0;
448 }
449
450 static int prompt_root_password(void) {
451         const char *msg1, *msg2, *etc_shadow;
452         int r;
453
454         if (arg_root_password)
455                 return 0;
456
457         if (!arg_prompt_root_password)
458                 return 0;
459
460         etc_shadow = prefix_roota("/etc/shadow");
461         if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
462                 return 0;
463
464         print_welcome();
465         putchar('\n');
466
467         msg1 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): ");
468         msg2 = strappenda(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: ");
469
470         for (;;) {
471                 _cleanup_free_ char *a = NULL, *b = NULL;
472
473                 r = ask_password_tty(msg1, 0, false, NULL, &a);
474                 if (r < 0)
475                         return log_error_errno(r, "Failed to query root password: %m");
476
477                 if (isempty(a)) {
478                         log_warning("No password entered, skipping.");
479                         break;
480                 }
481
482                 r = ask_password_tty(msg2, 0, false, NULL, &b);
483                 if (r < 0) {
484                         log_error_errno(r, "Failed to query root password: %m");
485                         clear_string(a);
486                         return r;
487                 }
488
489                 if (!streq(a, b)) {
490                         log_error("Entered passwords did not match, please try again.");
491                         clear_string(a);
492                         clear_string(b);
493                         continue;
494                 }
495
496                 clear_string(b);
497                 arg_root_password = a;
498                 a = NULL;
499                 break;
500         }
501
502         return 0;
503 }
504
505 static int write_root_shadow(const char *path, const struct spwd *p) {
506         _cleanup_fclose_ FILE *f = NULL;
507         assert(path);
508         assert(p);
509
510         RUN_WITH_UMASK(0777)
511                 f = fopen(path, "wex");
512         if (!f)
513                 return -errno;
514
515         errno = 0;
516         if (putspent(p, f) != 0)
517                 return errno ? -errno : -EIO;
518
519         return fflush_and_check(f);
520 }
521
522 static int process_root_password(void) {
523
524         static const char table[] =
525                 "abcdefghijklmnopqrstuvwxyz"
526                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
527                 "0123456789"
528                 "./";
529
530         struct spwd item = {
531                 .sp_namp = (char*) "root",
532                 .sp_min = 0,
533                 .sp_max = 99999,
534                 .sp_warn = 7,
535                 .sp_inact = -1,
536                 .sp_expire = -1,
537                 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
538         };
539
540         _cleanup_close_ int lock = -1;
541         char salt[3+16+1+1];
542         uint8_t raw[16];
543         unsigned i;
544         char *j;
545
546         const char *etc_shadow;
547         int r;
548
549         etc_shadow = prefix_roota("/etc/shadow");
550         if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
551                 return 0;
552
553         mkdir_parents(etc_shadow, 0755);
554
555         lock = take_password_lock(arg_root);
556         if (lock < 0)
557                 return lock;
558
559         if (arg_copy_root_password && arg_root) {
560                 struct spwd *p;
561
562                 errno = 0;
563                 p = getspnam("root");
564                 if (p || errno != ENOENT) {
565                         if (!p) {
566                                 if (!errno)
567                                         errno = EIO;
568
569                                 log_error("Failed to find shadow entry for root: %m");
570                                 return -errno;
571                         }
572
573                         r = write_root_shadow(etc_shadow, p);
574                         if (r < 0)
575                                 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
576
577                         log_info("%s copied.", etc_shadow);
578                         return 0;
579                 }
580         }
581
582         r = prompt_root_password();
583         if (r < 0)
584                 return r;
585
586         if (!arg_root_password)
587                 return 0;
588
589         r = dev_urandom(raw, 16);
590         if (r < 0)
591                 return log_error_errno(r, "Failed to get salt: %m");
592
593         /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
594         assert_cc(sizeof(table) == 64 + 1);
595         j = stpcpy(salt, "$6$");
596         for (i = 0; i < 16; i++)
597                 j[i] = table[raw[i] & 63];
598         j[i++] = '$';
599         j[i] = 0;
600
601         errno = 0;
602         item.sp_pwdp = crypt(arg_root_password, salt);
603         if (!item.sp_pwdp) {
604                 if (!errno)
605                         errno = -EINVAL;
606
607                 log_error("Failed to encrypt password: %m");
608                 return -errno;
609         }
610
611         item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
612
613         r = write_root_shadow(etc_shadow, &item);
614         if (r < 0)
615                 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
616
617         log_info("%s written.", etc_shadow);
618         return 0;
619 }
620
621 static void help(void) {
622         printf("%s [OPTIONS...]\n\n"
623                "Configures basic settings of the system.\n\n"
624                "  -h --help                    Show this help\n"
625                "     --version                 Show package version\n"
626                "     --root=PATH               Operate on an alternate filesystem root\n"
627                "     --locale=LOCALE           Set primary locale (LANG=)\n"
628                "     --locale-messages=LOCALE  Set message locale (LC_MESSAGES=)\n"
629                "     --timezone=TIMEZONE       Set timezone\n"
630                "     --hostname=NAME           Set host name\n"
631                "     --machine-ID=ID           Set machine ID\n"
632                "     --root-password=PASSWORD  Set root password\n"
633                "     --root-password-file=FILE Set root password from file\n"
634                "     --prompt-locale           Prompt the user for locale settings\n"
635                "     --prompt-timezone         Prompt the user for timezone\n"
636                "     --prompt-hostname         Prompt the user for hostname\n"
637                "     --prompt-root-password    Prompt the user for root password\n"
638                "     --prompt                  Prompt for locale, timezone, hostname, root password\n"
639                "     --copy-locale             Copy locale from host\n"
640                "     --copy-timezone           Copy timezone from host\n"
641                "     --copy-root-password      Copy root password from host\n"
642                "     --copy                    Copy locale, timezone, root password\n"
643                "     --setup-machine-id        Generate a new random machine ID\n"
644                , program_invocation_short_name);
645 }
646
647 static int parse_argv(int argc, char *argv[]) {
648
649         enum {
650                 ARG_VERSION = 0x100,
651                 ARG_ROOT,
652                 ARG_LOCALE,
653                 ARG_LOCALE_MESSAGES,
654                 ARG_TIMEZONE,
655                 ARG_HOSTNAME,
656                 ARG_MACHINE_ID,
657                 ARG_ROOT_PASSWORD,
658                 ARG_ROOT_PASSWORD_FILE,
659                 ARG_PROMPT,
660                 ARG_PROMPT_LOCALE,
661                 ARG_PROMPT_TIMEZONE,
662                 ARG_PROMPT_HOSTNAME,
663                 ARG_PROMPT_ROOT_PASSWORD,
664                 ARG_COPY,
665                 ARG_COPY_LOCALE,
666                 ARG_COPY_TIMEZONE,
667                 ARG_COPY_ROOT_PASSWORD,
668                 ARG_SETUP_MACHINE_ID,
669         };
670
671         static const struct option options[] = {
672                 { "help",                 no_argument,       NULL, 'h'                      },
673                 { "version",              no_argument,       NULL, ARG_VERSION              },
674                 { "root",                 required_argument, NULL, ARG_ROOT                 },
675                 { "locale",               required_argument, NULL, ARG_LOCALE               },
676                 { "locale-messages",      required_argument, NULL, ARG_LOCALE_MESSAGES      },
677                 { "timezone",             required_argument, NULL, ARG_TIMEZONE             },
678                 { "hostname",             required_argument, NULL, ARG_HOSTNAME             },
679                 { "machine-id",           required_argument, NULL, ARG_MACHINE_ID           },
680                 { "root-password",        required_argument, NULL, ARG_ROOT_PASSWORD        },
681                 { "root-password-file",   required_argument, NULL, ARG_ROOT_PASSWORD_FILE   },
682                 { "prompt",               no_argument,       NULL, ARG_PROMPT               },
683                 { "prompt-locale",        no_argument,       NULL, ARG_PROMPT_LOCALE        },
684                 { "prompt-timezone",      no_argument,       NULL, ARG_PROMPT_TIMEZONE      },
685                 { "prompt-hostname",      no_argument,       NULL, ARG_PROMPT_HOSTNAME      },
686                 { "prompt-root-password", no_argument,       NULL, ARG_PROMPT_ROOT_PASSWORD },
687                 { "copy",                 no_argument,       NULL, ARG_COPY                 },
688                 { "copy-locale",          no_argument,       NULL, ARG_COPY_LOCALE          },
689                 { "copy-timezone",        no_argument,       NULL, ARG_COPY_TIMEZONE        },
690                 { "copy-root-password",   no_argument,       NULL, ARG_COPY_ROOT_PASSWORD   },
691                 { "setup-machine-id",     no_argument,       NULL, ARG_SETUP_MACHINE_ID     },
692                 {}
693         };
694
695         int r, c;
696
697         assert(argc >= 0);
698         assert(argv);
699
700         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
701
702                 switch (c) {
703
704                 case 'h':
705                         help();
706                         return 0;
707
708                 case ARG_VERSION:
709                         puts(PACKAGE_STRING);
710                         puts(SYSTEMD_FEATURES);
711                         return 0;
712
713                 case ARG_ROOT:
714                         free(arg_root);
715                         arg_root = path_make_absolute_cwd(optarg);
716                         if (!arg_root)
717                                 return log_oom();
718
719                         path_kill_slashes(arg_root);
720
721                         if (path_equal(arg_root, "/")) {
722                                 free(arg_root);
723                                 arg_root = NULL;
724                         }
725
726                         break;
727
728                 case ARG_LOCALE:
729                         if (!locale_is_valid(optarg)) {
730                                 log_error("Locale %s is not valid.", optarg);
731                                 return -EINVAL;
732                         }
733
734                         free(arg_locale);
735                         arg_locale = strdup(optarg);
736                         if (!arg_locale)
737                                 return log_oom();
738
739                         break;
740
741                 case ARG_LOCALE_MESSAGES:
742                         if (!locale_is_valid(optarg)) {
743                                 log_error("Locale %s is not valid.", optarg);
744                                 return -EINVAL;
745                         }
746
747                         free(arg_locale_messages);
748                         arg_locale_messages = strdup(optarg);
749                         if (!arg_locale_messages)
750                                 return log_oom();
751
752                         break;
753
754                 case ARG_TIMEZONE:
755                         if (!timezone_is_valid(optarg)) {
756                                 log_error("Timezone %s is not valid.", optarg);
757                                 return -EINVAL;
758                         }
759
760                         free(arg_timezone);
761                         arg_timezone = strdup(optarg);
762                         if (!arg_timezone)
763                                 return log_oom();
764
765                         break;
766
767                 case ARG_ROOT_PASSWORD:
768                         free(arg_root_password);
769                         arg_root_password = strdup(optarg);
770                         if (!arg_root_password)
771                                 return log_oom();
772
773                         break;
774
775                 case ARG_ROOT_PASSWORD_FILE:
776                         free(arg_root_password);
777                         arg_root_password  = NULL;
778
779                         r = read_one_line_file(optarg, &arg_root_password);
780                         if (r < 0)
781                                 return log_error_errno(r, "Failed to read %s: %m", optarg);
782
783                         break;
784
785                 case ARG_HOSTNAME:
786                         if (!hostname_is_valid(optarg)) {
787                                 log_error("Host name %s is not valid.", optarg);
788                                 return -EINVAL;
789                         }
790
791                         free(arg_hostname);
792                         arg_hostname = strdup(optarg);
793                         if (!arg_hostname)
794                                 return log_oom();
795
796                         break;
797
798                 case ARG_MACHINE_ID:
799                         if (sd_id128_from_string(optarg, &arg_machine_id) < 0) {
800                                 log_error("Failed to parse machine id %s.", optarg);
801                                 return -EINVAL;
802                         }
803
804                         break;
805
806                 case ARG_PROMPT:
807                         arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true;
808                         break;
809
810                 case ARG_PROMPT_LOCALE:
811                         arg_prompt_locale = true;
812                         break;
813
814                 case ARG_PROMPT_TIMEZONE:
815                         arg_prompt_timezone = true;
816                         break;
817
818                 case ARG_PROMPT_HOSTNAME:
819                         arg_prompt_hostname = true;
820                         break;
821
822                 case ARG_PROMPT_ROOT_PASSWORD:
823                         arg_prompt_root_password = true;
824                         break;
825
826                 case ARG_COPY:
827                         arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true;
828                         break;
829
830                 case ARG_COPY_LOCALE:
831                         arg_copy_locale = true;
832                         break;
833
834                 case ARG_COPY_TIMEZONE:
835                         arg_copy_timezone = true;
836                         break;
837
838                 case ARG_COPY_ROOT_PASSWORD:
839                         arg_copy_root_password = true;
840                         break;
841
842                 case ARG_SETUP_MACHINE_ID:
843
844                         r = sd_id128_randomize(&arg_machine_id);
845                         if (r < 0)
846                                 return log_error_errno(r, "Failed to generate randomized machine ID: %m");
847
848                         break;
849
850                 case '?':
851                         return -EINVAL;
852
853                 default:
854                         assert_not_reached("Unhandled option");
855                 }
856
857         return 1;
858 }
859
860 int main(int argc, char *argv[]) {
861         int r;
862
863         r = parse_argv(argc, argv);
864         if (r <= 0)
865                 goto finish;
866
867         log_set_target(LOG_TARGET_AUTO);
868         log_parse_environment();
869         log_open();
870
871         umask(0022);
872
873         r = process_locale();
874         if (r < 0)
875                 goto finish;
876
877         r = process_timezone();
878         if (r < 0)
879                 goto finish;
880
881         r = process_hostname();
882         if (r < 0)
883                 goto finish;
884
885         r = process_machine_id();
886         if (r < 0)
887                 goto finish;
888
889         r = process_root_password();
890         if (r < 0)
891                 goto finish;
892
893 finish:
894         free(arg_root);
895         free(arg_locale);
896         free(arg_locale_messages);
897         free(arg_timezone);
898         free(arg_hostname);
899         clear_string(arg_root_password);
900         free(arg_root_password);
901
902         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
903 }