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