/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2020, Georgy Yakovlev. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void); static void usage(void) { (void) fprintf(stderr, "usage: zgenhostid [-fh] [-o path] [value]\n\n" " -f\t\t force hostid file write\n" " -h\t\t print this usage and exit\n" " -o \t write hostid to this file\n\n" "If hostid file is not present, store a hostid in it.\n" "The optional value must be an 8-digit hex number between" "1 and 2^32-1.\n" "If no value is provided, a random one will" "be generated.\n" "The value must be unique among your systems.\n"); exit(EXIT_FAILURE); /* NOTREACHED */ } int main(int argc, char **argv) { /* default file path, can be optionally set by user */ char path[PATH_MAX] = "/etc/hostid"; /* holds converted user input or lrand48() generated value */ unsigned long input_i = 0; int opt; int pathlen; int force_fwrite = 0; while ((opt = getopt_long(argc, argv, "fo:h?", 0, 0)) != -1) { switch (opt) { case 'f': force_fwrite = 1; break; case 'o': pathlen = snprintf(path, sizeof (path), "%s", optarg); if (pathlen >= sizeof (path)) { fprintf(stderr, "%s\n", strerror(EOVERFLOW)); exit(EXIT_FAILURE); } else if (pathlen < 1) { fprintf(stderr, "%s\n", strerror(EINVAL)); exit(EXIT_FAILURE); } break; case 'h': case '?': usage(); } } char *in_s = argv[optind]; if (in_s != NULL) { /* increment pointer by 2 if string is 0x prefixed */ if (strncasecmp("0x", in_s, 2) == 0) { in_s += 2; } /* need to be exactly 8 characters */ const char *hex = "0123456789abcdefABCDEF"; if (strlen(in_s) != 8 || strspn(in_s, hex) != 8) { fprintf(stderr, "%s\n", strerror(ERANGE)); usage(); } input_i = strtoul(in_s, NULL, 16); if (errno != 0) { perror("strtoul"); exit(EXIT_FAILURE); } if (input_i < 0x1 || input_i > UINT32_MAX) { fprintf(stderr, "%s\n", strerror(ERANGE)); usage(); } } struct stat fstat; if (force_fwrite == 0 && stat(path, &fstat) == 0 && S_ISREG(fstat.st_mode)) { fprintf(stderr, "%s: %s\n", path, strerror(EEXIST)); exit(EXIT_FAILURE); } /* * generate if not provided by user * also handle unlikely zero return from lrand48() */ while (input_i == 0) { srand48(getpid() ^ time(NULL)); input_i = lrand48(); } FILE *fp = fopen(path, "wb"); if (!fp) { perror("fopen"); exit(EXIT_FAILURE); } /* * we need just 4 bytes in native endianess * not using sethostid() because it may be missing or just a stub */ uint32_t hostid = input_i; int written = fwrite(&hostid, 1, 4, fp); if (written != 4) { perror("fwrite"); exit(EXIT_FAILURE); } fclose(fp); exit(EXIT_SUCCESS); }