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