]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/openzfs/cmd/zgenhostid/zgenhostid.c
MFV 2.0-rc2
[FreeBSD/FreeBSD.git] / sys / contrib / openzfs / cmd / zgenhostid / zgenhostid.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21
22 /*
23  * Copyright (c) 2020, Georgy Yakovlev.  All rights reserved.
24  */
25
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <getopt.h>
29 #include <inttypes.h>
30 #include <limits.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <time.h>
37 #include <unistd.h>
38
39 static void usage(void);
40
41 static void
42 usage(void)
43 {
44         (void) fprintf(stderr,
45             "usage: zgenhostid [-fh] [-o path] [value]\n\n"
46             "  -f\t\t force hostid file write\n"
47             "  -h\t\t print this usage and exit\n"
48             "  -o <filename>\t write hostid to this file\n\n"
49             "If hostid file is not present, store a hostid in it.\n"
50             "The optional value must be an 8-digit hex number between"
51             "1 and 2^32-1.\n"
52             "If no value is provided, a random one will"
53             "be generated.\n"
54             "The value must be unique among your systems.\n");
55         exit(EXIT_FAILURE);
56         /* NOTREACHED */
57 }
58
59 int
60 main(int argc, char **argv)
61 {
62         /* default file path, can be optionally set by user */
63         char path[PATH_MAX] = "/etc/hostid";
64         /* holds converted user input or lrand48() generated value */
65         unsigned long input_i = 0;
66
67         int opt;
68         int pathlen;
69         int force_fwrite = 0;
70         while ((opt = getopt_long(argc, argv, "fo:h?", 0, 0)) != -1) {
71                 switch (opt) {
72                 case 'f':
73                         force_fwrite = 1;
74                         break;
75                 case 'o':
76                         pathlen = snprintf(path, sizeof (path), "%s", optarg);
77                         if (pathlen >= sizeof (path)) {
78                                 fprintf(stderr, "%s\n", strerror(EOVERFLOW));
79                                 exit(EXIT_FAILURE);
80                         } else if (pathlen < 1) {
81                                 fprintf(stderr, "%s\n", strerror(EINVAL));
82                                 exit(EXIT_FAILURE);
83                         }
84                         break;
85                 case 'h':
86                 case '?':
87                         usage();
88                 }
89         }
90
91         char *in_s = argv[optind];
92         if (in_s != NULL) {
93                 /* increment pointer by 2 if string is 0x prefixed */
94                 if (strncasecmp("0x", in_s, 2) == 0) {
95                         in_s += 2;
96                 }
97
98                 /* need to be exactly 8 characters */
99                 const char *hex = "0123456789abcdefABCDEF";
100                 if (strlen(in_s) != 8 || strspn(in_s, hex) != 8) {
101                         fprintf(stderr, "%s\n", strerror(ERANGE));
102                         usage();
103                 }
104
105                 input_i = strtoul(in_s, NULL, 16);
106                 if (errno != 0) {
107                         perror("strtoul");
108                         exit(EXIT_FAILURE);
109                 }
110
111                 if (input_i < 0x1 || input_i > UINT32_MAX) {
112                         fprintf(stderr, "%s\n", strerror(ERANGE));
113                         usage();
114                 }
115         }
116
117         struct stat fstat;
118         if (force_fwrite == 0 && stat(path, &fstat) == 0 &&
119             S_ISREG(fstat.st_mode)) {
120                 fprintf(stderr, "%s: %s\n", path, strerror(EEXIST));
121                         exit(EXIT_FAILURE);
122         }
123
124         /*
125          * generate if not provided by user
126          * also handle unlikely zero return from lrand48()
127          */
128         while (input_i == 0) {
129                 srand48(getpid() ^ time(NULL));
130                 input_i = lrand48();
131         }
132
133         FILE *fp = fopen(path, "wb");
134         if (!fp) {
135                 perror("fopen");
136                 exit(EXIT_FAILURE);
137         }
138
139         /*
140          * we need just 4 bytes in native endianess
141          * not using sethostid() because it may be missing or just a stub
142          */
143         uint32_t hostid = input_i;
144         int written = fwrite(&hostid, 1, 4, fp);
145         if (written != 4) {
146                 perror("fwrite");
147                 exit(EXIT_FAILURE);
148         }
149
150         fclose(fp);
151         exit(EXIT_SUCCESS);
152 }