/* File : hostname.c Author : Richard A. O'Keefe Updated: 06/20/08 Purpose: Provide a tolerably portable version of the 'hostname' command. Usage : hostname -f full name x.y.z : hostname -s short name x : hostname -d domain name y.z : hostname -a the aliases : hostname whatever gethostname() gives you. Compile: cc -o hostname -O hostname.c -lnsl (Solaris) : cc -o hostname -O hostname.c (Mac OS X) */ #include #include #include #include #include #include #include /* extern int gethostname(char *, size_t); returns the host name as set by sethostname(). sysconf(_SC_HOST_NAME_MAX) gives the maximum size of a host name, but we're warned that it might be "infinite". The simplest and most portable way seems to be to start with 1k and double it until it works. Note that the POSIX specification does not define error results, and that some UNIX manuals incorrectly say int, not size_t. extern struct hostent *gethostbyname(char const *); returns a pointer to a record with char *h_name should be the full name char **h_aliases NULL-ptr terminated array of strings other stuff we don't care about. */ static void eputs(char const *prog, char const *line) { if (puts(line) < 0) { perror(prog); exit(EXIT_FAILURE); } } int main(int argc, char **argv) { char const opt = argc > 2 ? 'x' : argc == 1 ? 'w' : argv[1][0] != '-' ? 'x' : argv[1][1] == '\0' ? 'x' : argv[1][2] != '\0' ? 'x' : tolower(argv[1][1]); char *hname_buff = 0; size_t hname_size = 1024; char *p; struct hostent *e; if (strchr("adfsw", opt) == 0) { fprintf(stderr, "Usage: %s [-[adfsw]]\n", argv[0]); return EXIT_FAILURE; } for (;;) { hname_buff = malloc(hname_size); if (hname_buff == 0) { perror(argv[0]); return EXIT_FAILURE; } if (gethostname(hname_buff, hname_size) == 0) break; #ifdef ENAMETOOLONG if (errno == ENAMETOOLONG) { free(hname_buff); hname_size *= 2; continue; } #endif perror(argv[0]); return EXIT_FAILURE; } if (opt == 'w') { eputs(argv[0], hname_buff); return EXIT_SUCCESS; } p = strchr(hname_buff, '.'); if (opt == 's') { if (p != 0) *p = '\0'; eputs(argv[0], hname_buff); return EXIT_SUCCESS; } /* Now we need a fully qualified name with dots. */ if (p == 0 || opt == 'a') { e = gethostbyname(hname_buff); if (e == 0) { char const *m; /* You may well wonder why I don't use herror() here, */ /* especially as we'd expect herror() to be localised. */ /* Answer: some platforms do not provide herror(). */ switch (h_errno) { case HOST_NOT_FOUND: m = "host not found"; break; case TRY_AGAIN : m = "try again"; break; case NO_RECOVERY : m = "no recovery"; break; case NO_DATA : m = "no data"; break; #if NO_ADDRESS != NO_DATA case NO_ADDRESS : m = "no address"; break; #endif case NETDB_INTERNAL: m = strerror(errno); break; default : m = "gethostbyname() unknown error"; } fprintf(stderr, "%s:%s: %s\n", argv[0], hname_buff, m); return EXIT_FAILURE; } free(hname_buff); hname_buff = e->h_name; p = strchr(hname_buff, '.'); if (opt == 'a') { int i; for (i = 0; e->h_aliases[i] != 0; i++) { eputs(argv[0], e->h_aliases[i]); } return EXIT_SUCCESS; } } if (opt == 'f') { eputs(argv[0], hname_buff); } else { if (p == 0) { fprintf(stderr, "%s: no domain in hostname %s\n", argv[0], hname_buff); return EXIT_FAILURE; } eputs(argv[0], p+1); } return EXIT_SUCCESS; }