Expand PMF_FN_* macros.
[netbsd-mini2440.git] / distrib / utils / sysinst / net.c
blob8d5ab982d005c2bfe7a7926ac9098fa826223076
1 /* $NetBSD: net.c,v 1.122 2009/04/07 11:49:18 joerg Exp $ */
3 /*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Piermont Information Systems Inc.
21 * 4. The name of Piermont Information Systems Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
25 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35 * THE POSSIBILITY OF SUCH DAMAGE.
39 /* net.c -- routines to fetch files off the network. */
41 #include <err.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <curses.h>
46 #include <time.h>
47 #include <unistd.h>
48 #include <sys/param.h>
49 #include <sys/stat.h>
50 #include <sys/statvfs.h>
51 #ifdef INET6
52 #include <sys/sysctl.h>
53 #endif
54 #include <sys/socket.h>
55 #include <sys/ioctl.h>
56 #include <sys/statvfs.h>
57 #include <netinet/in.h>
58 #include <net/if.h>
59 #include <net/if_media.h>
60 #include <arpa/inet.h>
61 #include "defs.h"
62 #include "md.h"
63 #include "msg_defs.h"
64 #include "menu_defs.h"
65 #include "txtwalk.h"
67 #include <sys/wait.h>
68 #include <sys/resource.h>
69 #include <sys/sysctl.h>
71 int network_up = 0;
72 /* Access to network information */
73 static char *net_devices;
74 static char *net_up;
75 static char net_dev[STRSIZE];
76 static char net_domain[STRSIZE];
77 static char net_host[STRSIZE];
78 static char net_ip[SSTRSIZE];
79 static char net_srv_ip[SSTRSIZE];
80 static char net_mask[SSTRSIZE];
81 static char net_namesvr[STRSIZE];
82 static char net_defroute[STRSIZE];
83 static char net_media[STRSIZE];
84 static char sl_flags[STRSIZE];
85 static int net_dhcpconf;
86 #define DHCPCONF_IPADDR 0x01
87 #define DHCPCONF_NAMESVR 0x02
88 #define DHCPCONF_HOST 0x04
89 #define DHCPCONF_DOMAIN 0x08
90 #ifdef INET6
91 static char net_ip6[STRSIZE];
92 char net_namesvr6[STRSIZE];
93 static int net_ip6conf;
94 #define IP6CONF_AUTOHOST 0x01
95 #endif
98 /* URL encode unsafe characters. */
100 static char *url_encode (char *dst, const char *src, const char *ep,
101 const char *safe_chars,
102 int encode_leading_slash);
104 static void write_etc_hosts(FILE *f);
106 #define DHCPCD "/sbin/dhcpcd"
107 #include <signal.h>
108 static int config_dhcp(char *);
109 static void get_dhcp_value(char *, size_t, const char *);
111 #ifdef INET6
112 static int is_v6kernel (void);
113 static void init_v6kernel (int);
114 static int get_v6wait (void);
115 #endif
118 * URL encode unsafe characters. See RFC 1738.
120 * Copies src string to dst, encoding unsafe or reserved characters
121 * in %hex form as it goes, and returning a pointer to the result.
122 * The result is always a nul-terminated string even if it had to be
123 * truncated to avoid overflowing the available space.
125 * This url_encode() function does not operate on complete URLs, it
126 * operates on strings that make up parts of URLs. For example, in a
127 * URL like "ftp://username:password@host/path", the username, password,
128 * host and path should each be encoded separately before they are
129 * joined together with the punctuation characters.
131 * In most ordinary use, the path portion of a URL does not start with
132 * a slash; the slash is a separator between the host portion and the
133 * path portion, and is dealt with by software outside the url_encode()
134 * function. However, it is valid for url_encode() to be passed a
135 * string that does begin with a slash. For example, the string might
136 * represent a password, or a path part of a URL that the user really
137 * does want to begin with a slash.
139 * len is the length of the destination buffer. The result will be
140 * truncated if necessary to fit in the destination buffer.
142 * safe_chars is a string of characters that should not be encoded. If
143 * safe_chars is non-NULL, any characters in safe_chars as well as any
144 * alphanumeric characters will be copied from src to dst without
145 * encoding. Some potentially useful settings for this parameter are:
147 * NULL Everything is encoded (even alphanumerics)
148 * "" Everything except alphanumerics are encoded
149 * "/" Alphanumerics and '/' remain unencoded
150 * "$-_.+!*'()," Consistent with a strict reading of RFC 1738
151 * "$-_.+!*'(),/" As above, except '/' is not encoded
152 * "-_.+!,/" As above, except shell special characters are encoded
154 * encode_leading_slash is a flag that determines whether or not to
155 * encode a leading slash in a string. If this flag is set, and if the
156 * first character in the src string is '/', then the leading slash will
157 * be encoded (as "%2F"), even if '/' is one of the characters in the
158 * safe_chars string. Note that only the first character of the src
159 * string is affected by this flag, and that leading slashes are never
160 * deleted, but either retained unchanged or encoded.
162 * Unsafe and reserved characters are defined in RFC 1738 section 2.2.
163 * The most important parts are:
165 * The characters ";", "/", "?", ":", "@", "=" and "&" are the
166 * characters which may be reserved for special meaning within a
167 * scheme. No other characters may be reserved within a scheme.
168 * [...]
170 * Thus, only alphanumerics, the special characters "$-_.+!*'(),",
171 * and reserved characters used for their reserved purposes may be
172 * used unencoded within a URL.
176 #define RFC1738_SAFE "$-_.+!*'(),"
177 #define RFC1738_SAFE_LESS_SHELL "-_.+!,"
178 #define RFC1738_SAFE_LESS_SHELL_PLUS_SLASH "-_.+!,/"
180 static char *
181 url_encode(char *dst, const char *src, const char *ep,
182 const char *safe_chars, int encode_leading_slash)
184 int ch;
186 ep--;
188 for (; dst < ep; src++) {
189 ch = *src & 0xff;
190 if (ch == 0)
191 break;
192 if (safe_chars != NULL &&
193 (ch != '/' || !encode_leading_slash) &&
194 (isalnum(ch) || strchr(safe_chars, ch))) {
195 *dst++ = ch;
196 } else {
197 /* encode this char */
198 if (ep - dst < 3)
199 break;
200 snprintf(dst, ep - dst, "%%%02X", ch);
201 dst += 3;
203 encode_leading_slash = 0;
205 *dst = '\0';
206 return dst;
210 static const char *ignored_if_names[] = {
211 "eon", /* netiso */
212 "gre", /* net */
213 "ipip", /* netinet */
214 "gif", /* netinet6 */
215 "faith", /* netinet6 */
216 "lo", /* net */
217 #if 0
218 "mdecap", /* netinet -- never in IF list (?) XXX */
219 #endif
220 "nsip", /* netns */
221 "ppp", /* net */
222 #if 0
223 "sl", /* net */
224 #endif
225 "strip", /* net */
226 "tun", /* net */
227 /* XXX others? */
228 NULL,
231 static void
232 get_ifconfig_info(void)
234 char *textbuf;
235 char *t, *nt;
236 const char **ignore;
237 int textsize;
238 ulong fl;
239 char *cp;
241 free(net_devices);
242 net_devices = NULL;
243 free(net_up);
244 net_up = NULL;
246 /* Get ifconfig information */
248 textsize = collect(T_OUTPUT, &textbuf, "/sbin/ifconfig -a 2>/dev/null");
249 if (textsize < 0) {
250 if (logging)
251 (void)fprintf(logfp,
252 "Aborting: Could not run ifconfig.\n");
253 (void)fprintf(stderr, "Could not run ifconfig.");
254 exit(1);
257 for (t = textbuf; t != NULL && *t != 0; t = nt) {
258 /* find entry for next interface */
259 for (nt = t; (nt = strchr(nt, '\n')); ) {
260 if (*++nt != '\t')
261 break;
263 if (memcmp(t, "lo0:", 4) == 0)
264 /* completely ignore loopback interface */
265 continue;
266 cp = strchr(t, '=');
267 if (cp == NULL)
268 break;
269 /* get interface flags */
270 fl = strtoul(cp + 1, &cp, 16);
271 if (*cp != '<')
272 break;
274 for (ignore = ignored_if_names; *ignore != NULL; ignore++) {
275 size_t len = strlen(*ignore);
276 if (strncmp(t, *ignore, len) == 0 &&
277 isdigit((unsigned char)t[len]))
278 break;
280 if (*ignore != NULL)
281 continue;
283 if (fl & IFF_UP) {
284 /* This interface might be connected to the server */
285 cp = strchr(t, ':');
286 if (cp == NULL)
287 break;
288 asprintf(&cp, "%s%.*s ",
289 net_up ? net_up : "", (int)(cp - t), t);
290 free(net_up);
291 net_up = cp;
294 cp = strchr(t, ':');
295 if (cp == NULL)
296 break;
297 asprintf(&cp, "%s%.*s ",
298 net_devices ? net_devices : "", (int)(cp - t), t);
299 free(net_devices);
300 net_devices = cp;
302 free(textbuf);
305 static int
306 do_ifreq(struct ifmediareq *ifmr, unsigned long cmd)
308 int sock;
309 int rval;
311 sock = socket(PF_INET, SOCK_DGRAM, 0);
312 if (sock == -1)
313 return -1;
315 memset(ifmr, 0, sizeof *ifmr);
316 strncpy(ifmr->ifm_name, net_dev, sizeof ifmr->ifm_name);
317 rval = ioctl(sock, cmd, ifmr);
318 close(sock);
320 return rval;
323 /* Fill in defaults network values for the selected interface */
324 static void
325 get_ifinterface_info(void)
327 struct ifmediareq ifmr;
328 struct sockaddr_in *sa_in = (void *)&((struct ifreq *)&ifmr)->ifr_addr;
329 int modew;
330 const char *media_opt;
331 const char *sep;
333 if (do_ifreq(&ifmr, SIOCGIFADDR) == 0 && sa_in->sin_addr.s_addr != 0)
334 strlcpy(net_ip, inet_ntoa(sa_in->sin_addr), sizeof net_ip);
336 if (do_ifreq(&ifmr, SIOCGIFNETMASK) == 0 && sa_in->sin_addr.s_addr != 0)
337 strlcpy(net_mask, inet_ntoa(sa_in->sin_addr), sizeof net_mask);
339 if (do_ifreq(&ifmr, SIOCGIFMEDIA) == 0) {
340 /* Get the name of the media word */
341 modew = ifmr.ifm_current;
342 strlcpy(net_media, get_media_subtype_string(modew),
343 sizeof net_media);
344 /* and add any media options */
345 sep = " mediaopt ";
346 while ((media_opt = get_media_option_string(&modew)) != NULL) {
347 strlcat(net_media, sep, sizeof net_media);
348 strlcat(net_media, media_opt, sizeof net_media);
349 sep = ",";
354 #ifndef INET6
355 #define get_if6interface_info()
356 #else
357 static void
358 get_if6interface_info(void)
360 char *textbuf, *t;
361 int textsize;
363 textsize = collect(T_OUTPUT, &textbuf,
364 "/sbin/ifconfig %s inet6 2>/dev/null", net_dev);
365 if (textsize >= 0) {
366 char *p;
368 (void)strtok(textbuf, "\n"); /* ignore first line */
369 while ((t = strtok(NULL, "\n")) != NULL) {
370 if (strncmp(t, "\tinet6 ", 7) != 0)
371 continue;
372 t += 7;
373 if (strstr(t, "tentative") || strstr(t, "duplicated"))
374 continue;
375 if (strncmp(t, "fe80:", 5) == 0)
376 continue;
378 p = t;
379 while (*p && *p != ' ' && *p != '\n')
380 p++;
381 *p = '\0';
382 strlcpy(net_ip6, t, sizeof(net_ip6));
383 break;
386 free(textbuf);
388 #endif
390 static void
391 get_host_info(void)
393 char hostname[MAXHOSTNAMELEN + 1];
394 char *dot;
396 /* Check host (and domain?) name */
397 if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != 0) {
398 hostname[sizeof(hostname) - 1] = 0;
399 /* check for a . */
400 dot = strchr(hostname, '.');
401 if (dot == NULL) {
402 /* if not found its just a host, punt on domain */
403 strlcpy(net_host, hostname, sizeof net_host);
404 } else {
405 /* split hostname into host/domain parts */
406 *dot++ = 0;
407 strlcpy(net_host, hostname, sizeof net_host);
408 strlcpy(net_domain, dot, sizeof net_domain);
414 * recombine name parts split in get_host_info and config_network
415 * (common code moved here from write_etc_hosts)
417 static char *
418 recombine_host_domain(void)
420 static char recombined[MAXHOSTNAMELEN + 1];
421 int l = strlen(net_host) - strlen(net_domain);
423 strlcpy(recombined, net_host, sizeof(recombined));
425 if (l <= 0 ||
426 net_host[l - 1] != '.' ||
427 strcasecmp(net_domain, net_host + l) != 0) {
428 /* net_host isn't an FQDN. */
429 strlcat(recombined, ".", sizeof(recombined));
430 strlcat(recombined, net_domain, sizeof(recombined));
432 return recombined;
435 #ifdef INET6
436 static int
437 is_v6kernel(void)
439 int s;
441 s = socket(PF_INET6, SOCK_DGRAM, 0);
442 if (s < 0)
443 return 0;
444 close(s);
445 return 1;
449 * initialize as v6 client.
450 * we are sure that we will never become router with boot floppy :-)
451 * (include and use sysctl(8) if you are willing to)
453 static void
454 init_v6kernel(int autoconf)
456 int v;
457 int mib[4] = {CTL_NET, PF_INET6, IPPROTO_IPV6, 0};
459 mib[3] = IPV6CTL_FORWARDING;
460 v = 0;
461 (void)sysctl(mib, 4, NULL, NULL, (void *)&v, sizeof(v));
463 mib[3] = IPV6CTL_ACCEPT_RTADV;
464 v = autoconf ? 1 : 0;
465 (void)sysctl(mib, 4, NULL, NULL, (void *)&v, sizeof(v));
468 static int
469 get_v6wait(void)
471 size_t len = sizeof(int);
472 int v;
473 int mib[4] = {CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_DAD_COUNT};
475 len = sizeof(v);
476 if (sysctl(mib, 4, (void *)&v, &len, NULL, 0) < 0) {
477 /* warn("sysctl(net.inet6.ip6.dadcount)"); */
478 return 1; /* guess */
480 return v;
482 #endif
484 static int
485 handle_license(const char *dev)
487 static struct {
488 const char *dev;
489 const char *lic;
490 } licdev[] = {
491 { "iwi", "/libdata/firmware/if_iwi/LICENSE.ipw2200-fw" },
492 { "ipw", "/libdata/firmware/if_ipw/LICENSE" },
495 size_t i;
497 for (i = 0; i < __arraycount(licdev); i++)
498 if (strncmp(dev, licdev[i].dev, 3) == 0) {
499 char buf[64];
500 int val;
501 size_t len = sizeof(int);
502 (void)snprintf(buf, sizeof(buf), "hw.%s.accept_eula",
503 licdev[i].dev);
504 if (sysctlbyname(buf, &val, &len, NULL, 0) != -1
505 && val != 0)
506 return 1;
507 msg_display(MSG_license, dev, licdev[i].lic);
508 process_menu(MENU_yesno, NULL);
509 if (yesno) {
510 val = 1;
511 if (sysctlbyname(buf, NULL, NULL, &val,
512 0) == -1)
513 return 0;
514 add_sysctl_conf("%s=1", buf);
515 return 1;
516 } else
517 return 0;
519 return 1;
523 * Get the information to configure the network, configure it and
524 * make sure both the gateway and the name server are up.
527 config_network(void)
529 char *tp;
530 char *defname;
531 const char *prompt;
532 char *textbuf;
533 int octet0;
534 int dhcp_config;
535 int nfs_root = 0;
536 int slip = 0;
537 int pid, status;
538 char **ap, *slcmd[10], *in_buf;
539 char buffer[STRSIZE];
540 struct statvfs sb;
542 int l;
543 char dhcp_host[STRSIZE];
544 #ifdef INET6
545 int v6config = 1;
546 #endif
548 FILE *f;
549 time_t now;
551 if (network_up)
552 return (1);
554 get_ifconfig_info();
556 if (net_up != NULL) {
557 /* XXX: some retry loops come here... */
558 /* active interfaces found */
559 msg_display(MSG_netup, net_up);
560 process_menu(MENU_yesno, NULL);
561 if (!yesno)
562 return 0;
565 if (net_devices == NULL) {
566 /* No network interfaces found! */
567 msg_display(MSG_nonet);
568 process_menu(MENU_ok, NULL);
569 return (-1);
571 network_up = 1;
573 again:
574 tp = strchr(net_devices, ' ');
575 asprintf(&defname, "%.*s", (int)(tp - net_devices), net_devices);
576 for (prompt = MSG_asknetdev;; prompt = MSG_badnet) {
577 msg_prompt(prompt, defname, net_dev, sizeof net_dev - 1,
578 net_devices);
579 l = strlen(net_dev);
580 net_dev[l] = ' ';
581 net_dev[l + 1] = 0;
582 tp = strstr(net_devices, net_dev);
583 if (tp == NULL)
584 continue;
585 if (tp != net_devices && tp[-1] != ' ')
586 continue;
587 net_dev[l] = 0;
588 break;
590 free(defname);
591 if (!handle_license(net_dev))
592 goto done;
594 slip = net_dev[0] == 's' && net_dev[1] == 'l' &&
595 isdigit((unsigned char)net_dev[2]);
597 /* If root is on NFS do not reconfigure the interface. */
598 if (statvfs("/", &sb) == 0 && strcmp(sb.f_fstypename, "nfs") == 0) {
599 nfs_root = 1;
600 dhcp_config = 0;
601 get_ifinterface_info();
602 get_if6interface_info();
603 get_host_info();
604 } else if (slip) {
605 dhcp_config = 0;
606 } else {
607 /* Preload any defaults we can find */
608 get_ifinterface_info();
609 get_if6interface_info();
610 get_host_info();
612 /* domain and host */
613 msg_display(MSG_netinfo);
615 /* ethernet medium */
616 for (;;) {
617 msg_prompt_add(MSG_net_media, net_media, net_media,
618 sizeof net_media);
621 * ifconfig does not allow media specifiers on
622 * IFM_MANUAL interfaces. Our UI gives no way
623 * to set an option back
624 * to null-string if it gets accidentally set.
625 * Check for plausible alternatives.
627 if (strcmp(net_media, "<default>") == 0 ||
628 strcmp(net_media, "default") == 0 ||
629 strcmp(net_media, "<manual>") == 0 ||
630 strcmp(net_media, "manual") == 0 ||
631 strcmp(net_media, "<none>") == 0 ||
632 strcmp(net_media, "none") == 0 ||
633 strcmp(net_media, " ") == 0) {
634 *net_media = '\0';
637 if (*net_media == '\0')
638 break;
640 * We must set the media type here - to give dhcp
641 * a chance
643 if (run_program(0, "/sbin/ifconfig %s media %s",
644 net_dev, net_media) == 0)
645 break;
646 /* Failed to set - output the supported values */
647 if (collect(T_OUTPUT, &textbuf, "/sbin/ifconfig -m %s |"
648 "while IFS=; read line;"
649 " do [ \"$line\" = \"${line#*media}\" ] || "
650 "echo $line;"
651 " done", net_dev ) > 0)
652 msg_display(textbuf);
653 free(textbuf);
656 net_dhcpconf = 0;
657 /* try a dhcp configuration */
658 dhcp_config = config_dhcp(net_dev);
659 if (dhcp_config) {
660 /* Get newly configured data off interface. */
661 get_ifinterface_info();
662 get_if6interface_info();
663 get_host_info();
665 net_dhcpconf |= DHCPCONF_IPADDR;
668 * Extract default route from output of
669 * 'route -n show'
671 if (collect(T_OUTPUT, &textbuf,
672 "/sbin/route -n show | "
673 "while read dest gateway flags;"
674 " do [ \"$dest\" = default ] && {"
675 " echo $gateway; break; };"
676 " done" ) > 0)
677 strlcpy(net_defroute, textbuf,
678 sizeof net_defroute);
679 free(textbuf);
681 /* pull nameserver info out of /etc/resolv.conf */
682 if (collect(T_OUTPUT, &textbuf,
683 "cat /etc/resolv.conf 2>/dev/null |"
684 " while read keyword address rest;"
685 " do [ \"$keyword\" = nameserver "
686 " -a \"${address#*:}\" = "
687 "\"${address}\" ] && {"
688 " echo $address; break; };"
689 " done" ) > 0)
690 strlcpy(net_namesvr, textbuf,
691 sizeof net_namesvr);
692 free(textbuf);
693 if (net_namesvr[0] != '\0')
694 net_dhcpconf |= DHCPCONF_NAMESVR;
696 /* pull domainname out of leases file */
697 get_dhcp_value(net_domain, sizeof(net_domain),
698 "domain-name");
699 if (net_domain[0] != '\0')
700 net_dhcpconf |= DHCPCONF_DOMAIN;
702 /* pull hostname out of leases file */
703 dhcp_host[0] = 0;
704 get_dhcp_value(dhcp_host, sizeof(dhcp_host),
705 "host-name");
706 if (dhcp_host[0] != '\0') {
707 net_dhcpconf |= DHCPCONF_HOST;
708 strlcpy(net_host, dhcp_host, sizeof net_host);
713 msg_prompt_add(MSG_net_domain, net_domain, net_domain,
714 sizeof net_domain);
715 msg_prompt_add(MSG_net_host, net_host, net_host, sizeof net_host);
717 if (!dhcp_config) {
718 /* Manually configure IPv4 */
719 if (!nfs_root)
720 msg_prompt_add(MSG_net_ip, net_ip, net_ip,
721 sizeof net_ip);
722 if (slip)
723 msg_prompt_add(MSG_net_srv_ip, net_srv_ip, net_srv_ip,
724 sizeof net_srv_ip);
725 else if (!nfs_root) {
726 /* We don't want netmasks for SLIP */
727 octet0 = atoi(net_ip);
728 if (!net_mask[0]) {
729 if (0 <= octet0 && octet0 <= 127)
730 strlcpy(net_mask, "0xff000000",
731 sizeof(net_mask));
732 else if (128 <= octet0 && octet0 <= 191)
733 strlcpy(net_mask, "0xffff0000",
734 sizeof(net_mask));
735 else if (192 <= octet0 && octet0 <= 223)
736 strlcpy(net_mask, "0xffffff00",
737 sizeof(net_mask));
739 msg_prompt_add(MSG_net_mask, net_mask, net_mask,
740 sizeof net_mask);
742 msg_prompt_add(MSG_net_defroute, net_defroute, net_defroute,
743 sizeof net_defroute);
746 if (!dhcp_config || net_namesvr[0] == 0)
747 msg_prompt_add(MSG_net_namesrv, net_namesvr, net_namesvr,
748 sizeof net_namesvr);
750 #ifdef INET6
751 /* IPv6 autoconfiguration */
752 if (!is_v6kernel())
753 v6config = 0;
754 else if (v6config) {
755 process_menu(MENU_noyes, deconst(MSG_Perform_IPv6_autoconfiguration));
756 v6config = yesno ? 1 : 0;
757 net_ip6conf |= yesno ? IP6CONF_AUTOHOST : 0;
760 if (v6config) {
761 process_menu(MENU_namesrv6, NULL);
762 if (!yesno)
763 msg_prompt_add(MSG_net_namesrv6, net_namesvr6,
764 net_namesvr6, sizeof net_namesvr6);
766 #endif
768 /* confirm the setting */
769 if (slip)
770 msg_display(MSG_netok_slip, net_domain, net_host, net_dev,
771 *net_ip == '\0' ? "<none>" : net_ip,
772 *net_srv_ip == '\0' ? "<none>" : net_srv_ip,
773 *net_mask == '\0' ? "<none>" : net_mask,
774 *net_namesvr == '\0' ? "<none>" : net_namesvr,
775 *net_defroute == '\0' ? "<none>" : net_defroute,
776 *net_media == '\0' ? "<default>" : net_media);
777 else
778 msg_display(MSG_netok, net_domain, net_host, net_dev,
779 *net_ip == '\0' ? "<none>" : net_ip,
780 *net_mask == '\0' ? "<none>" : net_mask,
781 *net_namesvr == '\0' ? "<none>" : net_namesvr,
782 *net_defroute == '\0' ? "<none>" : net_defroute,
783 *net_media == '\0' ? "<default>" : net_media);
784 #ifdef INET6
785 msg_display_add(MSG_netokv6,
786 !is_v6kernel() ? "<not supported>" :
787 (v6config ? "yes" : "no"),
788 *net_namesvr6 == '\0' ? "<none>" : net_namesvr6);
789 #endif
790 done:
791 process_menu(MENU_yesno, deconst(MSG_netok_ok));
792 if (!yesno)
793 msg_display(MSG_netagain);
794 if (!yesno)
795 goto again;
798 * we may want to perform checks against inconsistent configuration,
799 * like IPv4 DNS server without IPv4 configuration.
802 /* Create /etc/resolv.conf if a nameserver was given */
803 if (net_namesvr[0] != '\0'
804 #ifdef INET6
805 || net_namesvr6[0] != '\0'
806 #endif
808 f = fopen("/etc/resolv.conf", "w");
809 if (f == NULL) {
810 if (logging)
811 (void)fprintf(logfp,
812 "%s", msg_string(MSG_resolv));
813 (void)fprintf(stderr, "%s", msg_string(MSG_resolv));
814 exit(1);
816 scripting_fprintf(NULL, "cat <<EOF >/etc/resolv.conf\n");
817 time(&now);
818 /* NB: ctime() returns a string ending in '\n' */
819 scripting_fprintf(f, ";\n; BIND data file\n; %s %s;\n",
820 "Created by NetBSD sysinst on", ctime(&now));
821 if (net_domain[0] != '\0')
822 scripting_fprintf(f, "search %s\n", net_domain);
823 if (net_namesvr[0] != '\0')
824 scripting_fprintf(f, "nameserver %s\n", net_namesvr);
825 #ifdef INET6
826 if (net_namesvr6[0] != '\0')
827 scripting_fprintf(f, "nameserver %s\n", net_namesvr6);
828 #endif
829 scripting_fprintf(NULL, "EOF\n");
830 fflush(NULL);
831 fclose(f);
834 run_program(0, "/sbin/ifconfig lo0 127.0.0.1");
836 #ifdef INET6
837 if (v6config && !nfs_root) {
838 init_v6kernel(1);
839 run_program(0, "/sbin/ifconfig %s up", net_dev);
840 sleep(get_v6wait() + 1);
841 run_program(RUN_DISPLAY, "/sbin/rtsol -D %s", net_dev);
842 sleep(get_v6wait() + 1);
844 #endif
846 if (net_ip[0] != '\0') {
847 if (slip) {
848 /* XXX: needs 'ifconfig sl0 create' much earlier */
849 /* Set SLIP interface UP */
850 run_program(0, "/sbin/ifconfig %s inet %s %s up",
851 net_dev, net_ip, net_srv_ip);
852 strcpy(sl_flags, "-s 115200 -l /dev/tty00");
853 msg_prompt_win(MSG_slattach, -1, 12, 70, 0,
854 sl_flags, sl_flags, 255);
856 /* XXX: wtf isn't run_program() used here? */
857 pid = fork();
858 if (pid == 0) {
859 strcpy(buffer, "/sbin/slattach ");
860 strcat(buffer, sl_flags);
861 in_buf = buffer;
863 for (ap = slcmd; (*ap = strsep(&in_buf, " ")) != NULL;)
864 if (**ap != '\0')
865 ++ap;
867 execvp(slcmd[0], slcmd);
868 } else
869 wait4(pid, &status, WNOHANG, 0);
870 } else if (!nfs_root) {
871 if (net_mask[0] != '\0') {
872 run_program(0, "/sbin/ifconfig %s inet %s netmask %s",
873 net_dev, net_ip, net_mask);
874 } else {
875 run_program(0, "/sbin/ifconfig %s inet %s",
876 net_dev, net_ip);
881 /* Set host name */
882 if (net_host[0] != '\0')
883 sethostname(net_host, strlen(net_host));
885 /* Set a default route if one was given */
886 if (!nfs_root && net_defroute[0] != '\0') {
887 run_program(RUN_DISPLAY | RUN_PROGRESS,
888 "/sbin/route -n flush -inet");
889 run_program(RUN_DISPLAY | RUN_PROGRESS,
890 "/sbin/route -n add default %s", net_defroute);
894 * wait a couple of seconds for the interface to go live.
896 if (!nfs_root) {
897 msg_display_add(MSG_wait_network);
898 sleep(5);
902 * ping should be verbose, so users can see the cause
903 * of a network failure.
906 #ifdef INET6
907 if (v6config && network_up) {
908 network_up = !run_program(RUN_DISPLAY | RUN_PROGRESS,
909 "/sbin/ping6 -v -c 3 -n -I %s ff02::2", net_dev);
911 if (net_namesvr6[0] != '\0')
912 network_up = !run_program(RUN_DISPLAY | RUN_PROGRESS,
913 "/sbin/ping6 -v -c 3 -n %s", net_namesvr6);
915 #endif
917 if (net_namesvr[0] != '\0' && network_up)
918 network_up = !run_program(RUN_DISPLAY | RUN_PROGRESS,
919 "/sbin/ping -v -c 5 -w 5 -o -n %s", net_namesvr);
921 if (net_defroute[0] != '\0' && network_up)
922 network_up = !run_program(RUN_DISPLAY | RUN_PROGRESS,
923 "/sbin/ping -v -c 5 -w 5 -o -n %s", net_defroute);
924 fflush(NULL);
926 return network_up;
929 static int
930 ftp_fetch(const char *set_name)
932 const char *ftp_opt;
933 char ftp_user_encoded[STRSIZE];
934 char ftp_dir_encoded[STRSIZE];
935 char *cp, *set_dir2;
936 int rval;
939 * Invoke ftp to fetch the file.
941 * ftp.pass is quite likely to contain unsafe characters
942 * that need to be encoded in the URL (for example,
943 * "@", ":" and "/" need quoting). Let's be
944 * paranoid and also encode ftp.user and ftp.dir. (For
945 * example, ftp.dir could easily contain '~', which is
946 * unsafe by a strict reading of RFC 1738).
948 if (strcmp("ftp", ftp.user) == 0 && ftp.pass[0] == 0) {
949 /* do anon ftp */
950 ftp_opt = "-a ";
951 ftp_user_encoded[0] = 0;
952 } else {
953 ftp_opt = "";
954 cp = url_encode(ftp_user_encoded, ftp.user,
955 ftp_user_encoded + sizeof ftp_user_encoded - 1,
956 RFC1738_SAFE_LESS_SHELL, 0);
957 *cp++ = ':';
958 cp = url_encode(cp, ftp.pass,
959 ftp_user_encoded + sizeof ftp_user_encoded - 1,
960 NULL, 0);
961 *cp++ = '@';
962 *cp = 0;
965 cp = url_encode(ftp_dir_encoded, ftp.dir,
966 ftp_dir_encoded + sizeof ftp_dir_encoded - 1,
967 RFC1738_SAFE_LESS_SHELL_PLUS_SLASH, 1);
968 if (cp != ftp_dir_encoded && cp[-1] != '/')
969 *cp++ = '/';
971 set_dir2 = set_dir;
972 while (*set_dir2 == '/')
973 ++set_dir2;
975 url_encode(cp, set_dir2,
976 ftp_dir_encoded + sizeof ftp_dir_encoded,
977 RFC1738_SAFE_LESS_SHELL_PLUS_SLASH, 0);
979 rval = run_program(RUN_DISPLAY | RUN_PROGRESS | RUN_XFER_DIR,
980 "/usr/bin/ftp %s%s://%s%s/%s/%s%s",
981 ftp_opt, ftp.xfer_type, ftp_user_encoded, ftp.host,
982 ftp_dir_encoded, set_name, dist_postfix);
984 return rval ? SET_RETRY : SET_OK;
987 static int
988 do_config_network(void)
990 int ret;
992 while ((ret = config_network()) <= 0) {
993 if (ret < 0)
994 return (-1);
995 msg_display(MSG_netnotup);
996 process_menu(MENU_yesno, NULL);
997 if (!yesno) {
998 msg_display(MSG_netnotup_continueanyway);
999 process_menu(MENU_yesno, NULL);
1000 if (!yesno)
1001 return -1;
1002 network_up = 1;
1003 break;
1006 return 0;
1010 get_via_ftp(const char *xfer_type)
1013 if (do_config_network() != 0)
1014 return SET_RETRY;
1016 process_menu(MENU_ftpsource, deconst(xfer_type));
1018 /* We'll fetch each file just before installing it */
1019 fetch_fn = ftp_fetch;
1020 ftp.xfer_type = xfer_type;
1021 snprintf(ext_dir, sizeof ext_dir, "%s/%s", target_prefix(),
1022 xfer_dir + (*xfer_dir == '/'));
1024 return SET_OK;
1028 get_via_nfs(void)
1030 struct statvfs sb;
1032 if (do_config_network() != 0)
1033 return SET_RETRY;
1035 /* If root is on NFS and we have sets, skip this step. */
1036 if (statvfs(set_dir, &sb) == 0 &&
1037 strcmp(sb.f_fstypename, "nfs") == 0) {
1038 strlcpy(ext_dir, set_dir, sizeof ext_dir);
1039 return SET_OK;
1042 /* Get server and filepath */
1043 process_menu(MENU_nfssource, NULL);
1045 /* Mount it */
1046 if (run_program(0, "/sbin/mount -r -o -2,-i,-r=1024 -t nfs %s:%s /mnt2",
1047 nfs_host, nfs_dir))
1048 return SET_RETRY;
1050 mnt2_mounted = 1;
1052 snprintf(ext_dir, sizeof ext_dir, "/mnt2/%s", set_dir);
1054 /* return location, don't clean... */
1055 return SET_OK;
1059 * write the new contents of /etc/hosts to the specified file
1061 static void
1062 write_etc_hosts(FILE *f)
1064 scripting_fprintf(f, "#\n");
1065 scripting_fprintf(f, "# Added by NetBSD sysinst\n");
1066 scripting_fprintf(f, "#\n");
1068 if (net_domain[0] != '\0')
1069 scripting_fprintf(f, "127.0.0.1 localhost.%s\n", net_domain);
1071 scripting_fprintf(f, "%s\t", net_ip);
1072 if (net_domain[0] != '\0')
1073 scripting_fprintf(f, "%s ", recombine_host_domain());
1074 scripting_fprintf(f, "%s\n", net_host);
1078 * Write the network config info the user entered via menus into the
1079 * config files in the target disk. Be careful not to lose any
1080 * information we don't immediately add back, in case the install
1081 * target is the currently-active root.
1083 void
1084 mnt_net_config(void)
1086 char ifconfig_fn[STRSIZE];
1087 FILE *ifconf = NULL;
1089 if (!network_up)
1090 return;
1091 process_menu(MENU_yesno, deconst(MSG_mntnetconfig));
1092 if (!yesno)
1093 return;
1095 /* Write hostname to /etc/rc.conf */
1096 if ((net_dhcpconf & DHCPCONF_HOST) == 0)
1097 add_rc_conf("hostname=%s\n", recombine_host_domain());
1099 /* If not running in target, copy resolv.conf there. */
1100 if ((net_dhcpconf & DHCPCONF_NAMESVR) == 0) {
1101 #ifndef INET6
1102 if (net_namesvr[0] != '\0')
1103 dup_file_into_target("/etc/resolv.conf");
1104 #else
1106 * not sure if it is a good idea, to allow dhcp config to
1107 * override IPv6 configuration
1109 if (net_namesvr[0] != '\0' || net_namesvr6[0] != '\0')
1110 dup_file_into_target("/etc/resolv.conf");
1111 #endif
1115 * bring the interface up, it will be necessary for IPv6, and
1116 * it won't make trouble with IPv4 case either
1118 snprintf(ifconfig_fn, sizeof ifconfig_fn, "/etc/ifconfig.%s", net_dev);
1119 ifconf = target_fopen(ifconfig_fn, "w");
1120 if (ifconf != NULL) {
1121 scripting_fprintf(NULL, "cat <<EOF >>%s%s\n",
1122 target_prefix(), ifconfig_fn);
1123 scripting_fprintf(ifconf, "up\n");
1124 if (*net_media != '\0')
1125 scripting_fprintf(ifconf, "media %s\n", net_media);
1126 scripting_fprintf(NULL, "EOF\n");
1129 if ((net_dhcpconf & DHCPCONF_IPADDR) == 0) {
1130 FILE *hosts;
1132 /* Write IPaddr and netmask to /etc/ifconfig.if[0-9] */
1133 if (ifconf != NULL) {
1134 scripting_fprintf(NULL, "cat <<EOF >>%s%s\n",
1135 target_prefix(), ifconfig_fn);
1136 if (*net_media != '\0')
1137 scripting_fprintf(ifconf,
1138 "%s netmask %s media %s\n",
1139 net_ip, net_mask, net_media);
1140 else
1141 scripting_fprintf(ifconf, "%s netmask %s\n",
1142 net_ip, net_mask);
1143 scripting_fprintf(NULL, "EOF\n");
1147 * Add IPaddr/hostname to /etc/hosts.
1148 * Be careful not to clobber any existing contents.
1149 * Relies on ordered search of /etc/hosts. XXX YP?
1151 hosts = target_fopen("/etc/hosts", "a");
1152 if (hosts != 0) {
1153 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/hosts\n",
1154 target_prefix());
1155 write_etc_hosts(hosts);
1156 (void)fclose(hosts);
1157 scripting_fprintf(NULL, "EOF\n");
1159 fclose(hosts);
1162 add_rc_conf("defaultroute=\"%s\"\n", net_defroute);
1163 } else {
1164 add_rc_conf("ifconfig_%s=dhcp\n", net_dev);
1167 #ifdef INET6
1168 if ((net_ip6conf & IP6CONF_AUTOHOST) != 0) {
1169 add_rc_conf("ip6mode=autohost\n");
1170 if (ifconf != NULL) {
1171 scripting_fprintf(NULL, "cat <<EOF >>%s%s\n",
1172 target_prefix(), ifconfig_fn);
1173 scripting_fprintf(ifconf, "!rtsol $int\n");
1174 scripting_fprintf(NULL, "EOF\n");
1177 #endif
1179 if (ifconf)
1180 fclose(ifconf);
1182 fflush(NULL);
1186 config_dhcp(char *inter)
1188 int dhcpautoconf;
1191 * Don't bother checking for an existing instance of dhcpcd, just
1192 * ask it to renew the lease. It will fork and daemonize if there
1193 * wasn't already an instance.
1196 if (!file_mode_match(DHCPCD, S_IFREG))
1197 return 0;
1198 process_menu(MENU_yesno, deconst(MSG_Perform_DHCP_autoconfiguration));
1199 if (yesno) {
1200 /* spawn off dhcpcd and wait for parent to exit */
1201 dhcpautoconf = run_program(RUN_DISPLAY | RUN_PROGRESS,
1202 "%s -d -n %s", DHCPCD, inter);
1203 return dhcpautoconf ? 0 : 1;
1205 return 0;
1208 static void
1209 get_dhcp_value(char *targ, size_t l, const char *var)
1211 static const char *lease_data = "/tmp/dhcpcd-lease";
1212 FILE *fp;
1213 char *line;
1214 size_t len, var_len;
1216 if ((fp = fopen(lease_data, "r")) == NULL) {
1217 warn("Could not open %s", lease_data);
1218 *targ = '\0';
1219 return;
1222 var_len = strlen(var);
1224 while ((line = fgetln(fp, &len)) != NULL) {
1225 if (line[len - 1] == '\n')
1226 --len;
1227 if (len <= var_len)
1228 continue;
1229 if (memcmp(line, var, var_len))
1230 continue;
1231 if (line[var_len] != '=')
1232 continue;
1233 line += var_len + 1;
1234 len -= var_len + 1;
1235 strlcpy(targ, line, l > len ? len + 1: l);
1236 break;
1239 fclose(fp);