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