summaryrefslogtreecommitdiff
path: root/hostname/hostname.c
diff options
context:
space:
mode:
Diffstat (limited to 'hostname/hostname.c')
-rw-r--r--hostname/hostname.c271
1 files changed, 271 insertions, 0 deletions
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;
+}