diff options
author | Zach van Rijn <me@zv.io> | 2022-05-18 02:56:49 +0000 |
---|---|---|
committer | Zach van Rijn <me@zv.io> | 2022-05-18 02:56:49 +0000 |
commit | 3743d9feaae50465f0b08ae8a30cad1de2110c9b (patch) | |
tree | de608960f5ad0a417a53d7fc3aff5a3c00773620 | |
parent | dbd74ddb26b5a0494ed2f075e886e3450a7ede7b (diff) | |
parent | e65c2374fe0b8fa952bab270213723454ec067b4 (diff) | |
download | shimmy-3743d9feaae50465f0b08ae8a30cad1de2110c9b.tar.gz shimmy-3743d9feaae50465f0b08ae8a30cad1de2110c9b.tar.bz2 shimmy-3743d9feaae50465f0b08ae8a30cad1de2110c9b.tar.xz shimmy-3743d9feaae50465f0b08ae8a30cad1de2110c9b.zip |
Merge branch 'add-hostname' into 'current'
Add hostname(1) implementation
See merge request adelie/shimmy!1
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | hostname/Makefile | 16 | ||||
-rw-r--r-- | hostname/hostname.1 | 36 | ||||
-rw-r--r-- | hostname/hostname.c | 271 |
4 files changed, 330 insertions, 1 deletions
@@ -1,18 +1,24 @@ .POSIX: -all: getconf/getconf localedef/localedef +all: getconf/getconf hostname/hostname localedef/localedef getconf/getconf: cd getconf && $(MAKE) +hostname/hostname: + cd hostname && $(MAKE) + localedef/localedef: cd localedef && ${MAKE} clean: cd getconf && $(MAKE) clean + cd hostname && $(MAKE) clean + cd localedef && $(MAKE) clean install: mkdir -p $(DESTDIR)/usr/bin mkdir -p $(DESTDIR)/usr/share/man/man1 cd getconf && $(MAKE) install + cd hostname && $(MAKE) install cd localedef && $(MAKE) install diff --git a/hostname/Makefile b/hostname/Makefile new file mode 100644 index 0000000..bb4dc1b --- /dev/null +++ b/hostname/Makefile @@ -0,0 +1,16 @@ +.POSIX: + +VERSTR=0.7 + +CFLAGS=-D _POSIX_C_SOURCE=200809L -D _XOPEN_SOURCE=700 -D SHIMMY_VERSION=\"$(VERSTR)\" + +hostname: hostname.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +clean: + -rm hostname + +install: hostname + cp hostname $(DESTDIR)/usr/bin/hostname + cp hostname.1 $(DESTDIR)/usr/share/man/man1/hostname.1 + diff --git a/hostname/hostname.1 b/hostname/hostname.1 new file mode 100644 index 0000000..94e12eb --- /dev/null +++ b/hostname/hostname.1 @@ -0,0 +1,36 @@ +.Dd April 22, 2022 +.Dt GETCONF 1 "Base Commands Manual" +.Os "Adélie Linux" +.Sh NAME +.Nm hostname +.Nd get or set node name +.Sh SYNOPSIS +.Nm +.Nm +.Op Fl F Ar filename +.Ar name +.Sh DESCRIPTION +In the first form, the +.Nm +utility prints the current node name on the running system. +.Pp +In the second form, the +.Nm +utility sets the currenet node name to the value specified. +.Sh OPTIONS +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr gethostname 3 , +.Xr getaddrinfo 3 , +.Xr getnameinfo 3 . +.Sh STANDARDS +The eventual goal for the +.Nm +utility is full conformance to LSB 5.0. +.Sh AUTHORS +.An A. Wilcox +.Aq awilfox@adelielinux.org +.Sh BUGS +Documentation is incomplete. NIS/YP domain name support is not available +in this release. Long options accepted by net-tools are not accepted. diff --git a/hostname/hostname.c b/hostname/hostname.c new file mode 100644 index 0000000..7dcef71 --- /dev/null +++ b/hostname/hostname.c @@ -0,0 +1,271 @@ +/* + * hostname - get or set the node name of the current workstation. + * Copyright (c) 2022 A. Wilcox. All rights reserved. + * Licensed under the NCSA open source license. + * See LICENSE file included with this source for more information. + */ + + +#include <arpa/inet.h> /* inet_ntop */ +#include <errno.h> /* errno */ +#include <stdbool.h> /* bool type and true/false */ +#include <stdio.h> /* stderr, stdout, fprintf, fflush */ +#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */ +#include <string.h> /* strchr */ +#include <unistd.h> /* getopt, sysconf */ + +/* gai and friends */ +#include <sys/socket.h> +#include <netdb.h> + +/* open, read, close */ +#include <sys/stat.h> +#include <fcntl.h> + +int sethostname(const char *value, +#ifdef __linux__ + size_t +#else + int +#endif + namelen); + +int do_print_nodename(bool verbose, bool domain, bool fqdn, bool address, bool shorten) +{ + long hostname = sysconf(_SC_HOST_NAME_MAX); + char buf[hostname]; + struct addrinfo *info; + + if(verbose) + { + fprintf(stderr, "gethostname()=..."); + fflush(stderr); + } + if(gethostname(buf, sizeof buf) != 0) + { + if(verbose) fprintf(stderr, "{error}\n"); + perror("gethostname"); + return EXIT_FAILURE; + } + + if(verbose) fprintf(stderr, "\"%s\"\n", buf); + + if(domain || fqdn || address) + { + struct addrinfo hints = {0}; + char addr_buf[INET6_ADDRSTRLEN]; + + if(verbose) + { + fprintf(stderr, "getaddrinfo(\"%s\")=...", buf); + fflush(stderr); + } + + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + + if(getaddrinfo(buf, NULL, &hints, &info) != 0) + { + if(verbose) fprintf(stderr, "{error}\n"); + perror("getaddrinfo"); + return EXIT_FAILURE; + } + + if(info == NULL || info->ai_canonname == NULL) + { + if(verbose) fprintf(stderr, "{no information}\n"); + fprintf(stderr, "No information available\n"); + return EXIT_FAILURE; + } + + if(getnameinfo(info->ai_addr, info->ai_addrlen, addr_buf, sizeof addr_buf, + NULL, 0, NI_NUMERICHOST) != 0 && address) + { + if(verbose) fprintf(stderr, "{getnameinfo returned error}\n"); + perror("getnameinfo"); + return EXIT_FAILURE; + } + + if(verbose) fprintf(stderr, "resolved: %s at %s\n", info->ai_canonname, addr_buf); + + if(domain) + { + char *domain = strchr(info->ai_canonname, '.'); + if(domain == NULL) + { + fprintf(stdout, "(none)\n"); + return EXIT_SUCCESS; + } + fprintf(stdout, "%s\n", domain + 1); + return EXIT_SUCCESS; + } + + if(fqdn) + { + fprintf(stdout, "%s\n", info->ai_canonname); + return EXIT_SUCCESS; + } + + if(address) + { + fprintf(stdout, "%s\n", addr_buf); + return EXIT_SUCCESS; + } + } + + if(shorten) + { + char *domain = strchr(buf, '.'); + if(domain != NULL) *domain = '\0'; + } + + fprintf(stdout, "%s\n", buf); + return EXIT_SUCCESS; +} + +int do_set_nodename(bool file, const char *value) +{ + size_t len; + + if(file) + { + /* This is insane. There is no validation whatsoever. + * However, strace on net-tools hostname(1) shows this to be + * the exact algorithm used, so... */ + char buf[1025]; + int fildes = open(value, O_RDONLY); + ssize_t result = 0; + + if(fildes == -1) + { + perror("open"); + return EXIT_FAILURE; + } + + while(true) + { + char *curr = buf; + memset(buf, 0, sizeof buf); + result = read(fildes, buf, sizeof buf - 1); + switch(result) + { + case -1: + perror("read"); + close(fildes); + return EXIT_FAILURE; + case 0: + close(fildes); + return EXIT_SUCCESS; + } + do + { + len = strnlen(curr, result); + char *pos = strchr(curr, '\n'); + if(pos) + { + len = pos - curr; + } + if(len == 0 || *curr == '#') continue; + while(len > 63) + { + sethostname(curr, 63); + len -= 63; + curr += 63; + } + sethostname(curr, len); + } while((curr = strchr(curr, '\n')) && curr++); + } + } + + len = strlen(value); + sethostname(value, len); + return EXIT_SUCCESS; +} + +void usage() +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, "hostname {hostname|-F filename}\n"); + fprintf(stderr, "hostname [-v] [-d|-f|-s]\n"); + fprintf(stderr, "\n\tRetrieve or set node name.\n"); +} + +int main(int argc, char *argv[]) +{ + /* Whether to use a file or argument */ + bool is_file = false; + /* Enable verbose output */ + bool verbose = false; + /* Intentionally shorten name to first part */ + bool shorten = false; + /* Show DNS domain name only (if available) */ + bool domain = false; + /* Go out of our way to show FQDN */ + bool fqdn = false; + /* Print the IP address associated with this node */ + bool address = false; + /* An error occurred during argument parsing */ + bool arg_error = false; + /* The current argument being parsed */ + int arg; + + while((arg = getopt(argc, argv, ":FVvdfish")) != -1) + { + switch(arg) + { + case 'v': + verbose = true; + break; + case 'd': + domain = true; + break; + case 'F': + is_file = true; + break; + case 'f': + fqdn = true; + break; + case 'i': + address = true; + break; + case 's': + shorten = true; + break; + case 'h': + usage(); + return EXIT_SUCCESS; + case 'V': + fprintf(stdout, "Shimmy " SHIMMY_VERSION "\n"); + return EXIT_SUCCESS; + case ':': + fprintf(stderr, "required argument to '-%c' missing\n", + optopt); + arg_error = true; + break; + case '?': + default: + fprintf(stderr, "unrecognised option: '-%c'\n", optopt); + arg_error = true; + break; + } + } + + switch(argc - optind) + { + case 0: + if(is_file || arg_error) + { + arg_error = true; + break; + } + return do_print_nodename(verbose, domain, fqdn, address, shorten); + case 1: + return do_set_nodename(is_file, argv[optind++]); + default: + /* just show usage. */ + break; + } + + usage(); + return EXIT_FAILURE; +} |