diff options
author | Georgy Yakovlev <[email protected]> | 2020-09-16 12:25:12 -0700 |
---|---|---|
committer | GitHub <[email protected]> | 2020-09-16 12:25:12 -0700 |
commit | 9cc177baa06e8efc0d092886e2c9b3de4b3ee50f (patch) | |
tree | ff2101589f3bdb76eee55be8ba3d7c8535e9f147 /cmd/zgenhostid | |
parent | 9569c31161453e29ec32e3612b91950d2faed83e (diff) |
cmd/zgenhostid: replace with simple c implementation
It was discovered that dracut scripts and zgenhostid
always generate little-endian /etc/hostid.
This commit provides simple endianess-aware binary
and updates the scripts to use it.
New features include:
-f flag to force overwrite.
-o flag to write to different file (for dracut)
accepting both 0x01234567 and 01234567 values as input
Reviewed-by: Brian Behlendorf <[email protected]>
Reviewed-by: Olaf Faaland <[email protected]>
Signed-off-by: Georgy Yakovlev <[email protected]>
Closes #10887
Closes #10925
Diffstat (limited to 'cmd/zgenhostid')
-rw-r--r-- | cmd/zgenhostid/.gitignore | 1 | ||||
-rw-r--r-- | cmd/zgenhostid/Makefile.am | 6 | ||||
-rwxr-xr-x | cmd/zgenhostid/zgenhostid | 61 | ||||
-rw-r--r-- | cmd/zgenhostid/zgenhostid.c | 152 |
4 files changed, 158 insertions, 62 deletions
diff --git a/cmd/zgenhostid/.gitignore b/cmd/zgenhostid/.gitignore new file mode 100644 index 000000000..072246c73 --- /dev/null +++ b/cmd/zgenhostid/.gitignore @@ -0,0 +1 @@ +/zgenhostid diff --git a/cmd/zgenhostid/Makefile.am b/cmd/zgenhostid/Makefile.am index 69c99ca9d..0ba791f7c 100644 --- a/cmd/zgenhostid/Makefile.am +++ b/cmd/zgenhostid/Makefile.am @@ -1 +1,5 @@ -dist_bin_SCRIPTS = zgenhostid +include $(top_srcdir)/config/Rules.am + +bin_PROGRAMS = zgenhostid + +zgenhostid_SOURCES = zgenhostid.c diff --git a/cmd/zgenhostid/zgenhostid b/cmd/zgenhostid/zgenhostid deleted file mode 100755 index 8b468740c..000000000 --- a/cmd/zgenhostid/zgenhostid +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash - -# Emulate genhostid(1) available on RHEL/CENTOS, for use on distros -# which do not provide that utility. -# -# Usage: -# zgenhostid -# zgenhostid <value> -# -# If /etc/hostid already exists and is size > 0, the script exits immediately -# and changes nothing. Unlike genhostid, this generates an error message. -# -# The first form generates a random hostid and stores it in /etc/hostid. -# The second form checks that the provided value is between 0x1 and 0xFFFFFFFF -# and if so, stores it in /etc/hostid. This form is not supported by -# genhostid(1). - -hostid_file=/etc/hostid - -function usage { - echo "$0 [value]" - echo "If $hostid_file is not present, store a hostid in it." >&2 - echo "The optional value must be an 8-digit hex number between" >&2 - echo "1 and 2^32-1. If no value is provided, a random one will" >&2 - echo "be generated. The value must be unique among your systems." >&2 -} - -# hostid(1) ignores contents of /etc/hostid if size < 4 bytes. It would -# be better if this checked size >= 4 bytes but it the method must be -# widely portable. -if [ -s $hostid_file ]; then - echo "$hostid_file already exists. No change made." >&2 - exit 1 -fi - -if [ -n "$1" ]; then - host_id=$1 -else - # $RANDOM goes from 0..32k-1 - number=$((((RANDOM % 4) * 32768 + RANDOM) * 32768 + RANDOM)) - host_id=$(printf "%08x" $number) -fi - -if egrep -o '^0{8}$' <<< $host_id >/dev/null 2>&1; then - usage - exit 2 -fi - -if ! egrep -o '^[a-fA-F0-9]{8}$' <<< $host_id >/dev/null 2>&1; then - usage - exit 3 -fi - -a=${host_id:6:2} -b=${host_id:4:2} -c=${host_id:2:2} -d=${host_id:0:2} - -echo -ne \\x$a\\x$b\\x$c\\x$d > $hostid_file - -exit 0 diff --git a/cmd/zgenhostid/zgenhostid.c b/cmd/zgenhostid/zgenhostid.c new file mode 100644 index 000000000..562262928 --- /dev/null +++ b/cmd/zgenhostid/zgenhostid.c @@ -0,0 +1,152 @@ +/* + * 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 <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <inttypes.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> + +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 <filename>\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); +} |