/*
* 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>
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)
{
fprintf(stderr, "setting nodenamet to %s %s not yet supported\n",
(file ? "contents of file" : "literal string"), value);
return 1;
}
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;
}