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