summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--hostname/Makefile16
-rw-r--r--hostname/hostname.136
-rw-r--r--hostname/hostname.c271
4 files changed, 330 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 26b890c..4e355cb 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
+}