1/* Display hostname in various forms.
2   Copyright (C) 2001-2003, 2006-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18
19#ifdef HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include <errno.h>
24#include <getopt.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <locale.h>
29
30#if defined _WIN32 || defined __WIN32__
31# define WIN32_NATIVE
32#endif
33
34/* Get gethostname().  */
35#include <unistd.h>
36
37#ifdef WIN32_NATIVE
38/* Native Woe32 API lacks gethostname() but has GetComputerName() instead.  */
39# include <windows.h>
40#else
41/* Some systems, like early Solaris versions, lack gethostname() but
42   have uname() instead.  */
43# if !HAVE_GETHOSTNAME
44#  include <sys/utsname.h>
45# endif
46#endif
47
48/* Get MAXHOSTNAMELEN.  */
49#include <sys/param.h>
50#ifndef MAXHOSTNAMELEN
51# define MAXHOSTNAMELEN 64
52#endif
53
54/* Support for using gethostbyname().  */
55#if HAVE_GETHOSTBYNAME
56# include <sys/types.h>
57# include <sys/socket.h> /* defines AF_INET, AF_INET6 */
58# include <netinet/in.h> /* declares ntohs(), defines struct sockaddr_in */
59# if HAVE_ARPA_INET_H
60#  include <arpa/inet.h> /* declares inet_ntoa(), inet_ntop() */
61# endif
62# if HAVE_IPV6
63#  if !defined(__CYGWIN__) /* Cygwin has only s6_addr, no s6_addr16 */
64#   if defined(__APPLE__) && defined(__MACH__) /* MacOS X */
65#    define in6_u __u6_addr
66#    define u6_addr16 __u6_addr16
67#   endif
68    /* Use s6_addr16 for portability.  See RFC 2553.  */
69#   ifndef s6_addr16
70#    define s6_addr16 in6_u.u6_addr16
71#   endif
72#   define HAVE_IN6_S6_ADDR16 1
73#  endif
74# endif
75# include <netdb.h> /* defines struct hostent, declares gethostbyname() */
76#endif
77
78/* Include this after <sys/socket.h>, to avoid a syntax error on BeOS.  */
79#include <stdbool.h>
80
81#include "closeout.h"
82#include "error.h"
83#include "error-progname.h"
84#include "progname.h"
85#include "relocatable.h"
86#include "basename.h"
87#include "xalloc.h"
88#include "propername.h"
89#include "gettext.h"
90
91#define _(str) gettext (str)
92
93
94/* Output format.  */
95static enum { default_format, short_format, long_format, ip_format } format;
96
97/* Long options.  */
98static const struct option long_options[] =
99{
100  { "fqdn", no_argument, NULL, 'f' },
101  { "help", no_argument, NULL, 'h' },
102  { "ip-address", no_argument, NULL, 'i' },
103  { "long", no_argument, NULL, 'f' },
104  { "short", no_argument, NULL, 's' },
105  { "version", no_argument, NULL, 'V' },
106  { NULL, 0, NULL, 0 }
107};
108
109
110/* Forward declaration of local functions.  */
111static void usage (int status)
112#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
113     __attribute__ ((noreturn))
114#endif
115;
116static void print_hostname (void);
117
118int
119main (int argc, char *argv[])
120{
121  int optchar;
122  bool do_help;
123  bool do_version;
124
125  /* Set program name for messages.  */
126  set_program_name (argv[0]);
127  error_print_progname = maybe_print_progname;
128
129#ifdef HAVE_SETLOCALE
130  /* Set locale via LC_ALL.  */
131  setlocale (LC_ALL, "");
132#endif
133
134  /* Set the text message domain.  */
135  bindtextdomain (PACKAGE, relocate (LOCALEDIR));
136  textdomain (PACKAGE);
137
138  /* Ensure that write errors on stdout are detected.  */
139  atexit (close_stdout);
140
141  /* Set default values for variables.  */
142  do_help = false;
143  do_version = false;
144  format = default_format;
145
146  /* Parse command line options.  */
147  while ((optchar = getopt_long (argc, argv, "fhisV", long_options, NULL))
148	 != EOF)
149    switch (optchar)
150    {
151    case '\0':		/* Long option.  */
152      break;
153    case 'f':
154      format = long_format;
155      break;
156    case 's':
157      format = short_format;
158      break;
159    case 'i':
160      format = ip_format;
161      break;
162    case 'h':
163      do_help = true;
164      break;
165    case 'V':
166      do_version = true;
167      break;
168    default:
169      usage (EXIT_FAILURE);
170      /* NOTREACHED */
171    }
172
173  /* Version information requested.  */
174  if (do_version)
175    {
176      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
177      /* xgettext: no-wrap */
178      printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
179License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
180This is free software: you are free to change and redistribute it.\n\
181There is NO WARRANTY, to the extent permitted by law.\n\
182"),
183	      "2001-2003, 2006-2007");
184      printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
185      exit (EXIT_SUCCESS);
186    }
187
188  /* Help is requested.  */
189  if (do_help)
190    usage (EXIT_SUCCESS);
191
192  /* Test for extraneous arguments.  */
193  if (optind != argc)
194    error (EXIT_FAILURE, 0, _("too many arguments"));
195
196  /* Get and print the hostname.  */
197  print_hostname ();
198
199  exit (EXIT_SUCCESS);
200}
201
202/* Display usage information and exit.  */
203static void
204usage (int status)
205{
206  if (status != EXIT_SUCCESS)
207    fprintf (stderr, _("Try `%s --help' for more information.\n"),
208	     program_name);
209  else
210    {
211      printf (_("\
212Usage: %s [OPTION]\n\
213"), program_name);
214      printf ("\n");
215      printf (_("\
216Print the machine's hostname.\n"));
217      printf ("\n");
218      printf (_("\
219Output format:\n"));
220      printf (_("\
221  -s, --short                 short host name\n"));
222      printf (_("\
223  -f, --fqdn, --long          long host name, includes fully qualified domain\n\
224                                name, and aliases\n"));
225      printf (_("\
226  -i, --ip-address            addresses for the hostname\n"));
227      printf ("\n");
228      printf (_("\
229Informative output:\n"));
230      printf (_("\
231  -h, --help                  display this help and exit\n"));
232      printf (_("\
233  -V, --version               output version information and exit\n"));
234      printf ("\n");
235      /* TRANSLATORS: The placeholder indicates the bug-reporting address
236         for this package.  Please add _another line_ saying
237         "Report translation bugs to <...>\n" with the address for translation
238         bugs (typically your translation team's web or email address).  */
239      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
240	     stdout);
241    }
242
243  exit (status);
244}
245
246/* Returns an xmalloc()ed string containing the machine's host name.  */
247static char *
248xgethostname ()
249{
250#ifdef WIN32_NATIVE
251  char hostname[MAX_COMPUTERNAME_LENGTH+1];
252  DWORD size = sizeof (hostname);
253
254  if (!GetComputerName (hostname, &size))
255    error (EXIT_FAILURE, 0, _("could not get host name"));
256  return xstrdup (hostname);
257#elif HAVE_GETHOSTNAME
258  char hostname[MAXHOSTNAMELEN+1];
259
260  if (gethostname (hostname, MAXHOSTNAMELEN) < 0)
261    error (EXIT_FAILURE, errno, _("could not get host name"));
262  hostname[MAXHOSTNAMELEN] = '\0';
263  return xstrdup (hostname);
264#else
265  struct utsname utsname;
266
267  if (uname (&utsname) < 0)
268    error (EXIT_FAILURE, errno, _("could not get host name"));
269  return xstrdup (utsname.nodename);
270#endif
271}
272
273/* Converts an AF_INET address to a printable, presentable format.
274   BUFFER is an array with at least 15+1 bytes.  ADDR is 'struct in_addr'.  */
275#if HAVE_INET_NTOP
276# define ipv4_ntop(buffer,addr) \
277    inet_ntop (AF_INET, &addr, buffer, 15+1)
278#else
279# define ipv4_ntop(buffer,addr) \
280    strcpy (buffer, inet_ntoa (addr))
281#endif
282
283#if HAVE_IPV6
284/* Converts an AF_INET6 address to a printable, presentable format.
285   BUFFER is an array with at least 45+1 bytes.  ADDR is 'struct in6_addr'.  */
286# if HAVE_INET_NTOP
287#  define ipv6_ntop(buffer,addr) \
288     inet_ntop (AF_INET6, &addr, buffer, 45+1)
289# elif HAVE_IN6_S6_ADDR16
290#  define ipv6_ntop(buffer,addr) \
291     sprintf (buffer, "%x:%x:%x:%x:%x:%x:%x:%x", \
292	      ntohs ((addr).s6_addr16[0]), \
293	      ntohs ((addr).s6_addr16[1]), \
294	      ntohs ((addr).s6_addr16[2]), \
295	      ntohs ((addr).s6_addr16[3]), \
296	      ntohs ((addr).s6_addr16[4]), \
297	      ntohs ((addr).s6_addr16[5]), \
298	      ntohs ((addr).s6_addr16[6]), \
299	      ntohs ((addr).s6_addr16[7]))
300# else
301#  define ipv6_ntop(buffer,addr) \
302     sprintf (buffer, "%x:%x:%x:%x:%x:%x:%x:%x", \
303	      ((addr).s6_addr[0] << 8) | (addr).s6_addr[1], \
304	      ((addr).s6_addr[2] << 8) | (addr).s6_addr[3], \
305	      ((addr).s6_addr[4] << 8) | (addr).s6_addr[5], \
306	      ((addr).s6_addr[6] << 8) | (addr).s6_addr[7], \
307	      ((addr).s6_addr[8] << 8) | (addr).s6_addr[9], \
308	      ((addr).s6_addr[10] << 8) | (addr).s6_addr[11], \
309	      ((addr).s6_addr[12] << 8) | (addr).s6_addr[13], \
310	      ((addr).s6_addr[14] << 8) | (addr).s6_addr[15])
311# endif
312#endif
313
314/* Print the hostname according to the specified format.  */
315static void
316print_hostname ()
317{
318  char *hostname;
319  char *dot;
320#if HAVE_GETHOSTBYNAME
321  struct hostent *h;
322  size_t i;
323#endif
324
325  hostname = xgethostname ();
326
327  switch (format)
328    {
329    case default_format:
330      /* Print the hostname, as returned by the system call.  */
331      printf ("%s\n", hostname);
332      break;
333
334    case short_format:
335      /* Print only the part before the first dot.  */
336      dot = strchr (hostname, '.');
337      if (dot != NULL)
338	*dot = '\0';
339      printf ("%s\n", hostname);
340      break;
341
342    case long_format:
343      /* Look for netwide usable hostname and aliases using gethostbyname().  */
344#if HAVE_GETHOSTBYNAME
345      h = gethostbyname (hostname);
346      if (h != NULL)
347	{
348	  printf ("%s\n", h->h_name);
349	  if (h->h_aliases != NULL)
350	    for (i = 0; h->h_aliases[i] != NULL; i++)
351	      printf ("%s\n", h->h_aliases[i]);
352	}
353      else
354#endif
355	printf ("%s\n", hostname);
356      break;
357
358    case ip_format:
359      /* Look for netwide usable IP addresses using gethostbyname().  */
360#if HAVE_GETHOSTBYNAME
361      h = gethostbyname (hostname);
362      if (h != NULL && h->h_addr_list != NULL)
363	for (i = 0; h->h_addr_list[i] != NULL; i++)
364	  {
365#if HAVE_IPV6
366	    if (h->h_addrtype == AF_INET6)
367	      {
368		char buffer[45+1];
369		ipv6_ntop (buffer, *(const struct in6_addr*) h->h_addr_list[i]);
370		printf("[%s]\n", buffer);
371	      }
372	    else
373#endif
374	    if (h->h_addrtype == AF_INET)
375	      {
376		char buffer[15+1];
377		ipv4_ntop (buffer, *(const struct in_addr*) h->h_addr_list[i]);
378		printf("[%s]\n", buffer);
379	      }
380	  }
381#endif
382      break;
383
384    default:
385      abort ();
386    }
387}
388