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 39static void 40usage(void) 41{ 42 (void) fprintf(stderr, 43 "usage: zgenhostid [-fh] [-o path] [value]\n\n" 44 " -f\t\t force hostid file write\n" 45 " -h\t\t print this usage and exit\n" 46 " -o <filename>\t write hostid to this file\n\n" 47 "If hostid file is not present, store a hostid in it.\n" 48 "The optional value should be an 8-digit hex number between" 49 " 1 and 2^32-1.\n" 50 "If the value is 0 or no value is provided, a random one" 51 " will be generated.\n" 52 "The value must be unique among your systems.\n"); 53 exit(EXIT_FAILURE); 54 /* NOTREACHED */ 55} 56 57int 58main(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