install.c revision 329011
112869Sdfuchs/*-
212869Sdfuchs * Copyright (c) 2008-2014, Juniper Networks, Inc.
312869Sdfuchs * All rights reserved.
412869Sdfuchs *
512869Sdfuchs * Redistribution and use in source and binary forms, with or without
612869Sdfuchs * modification, are permitted provided that the following conditions
712869Sdfuchs * are met:
812869Sdfuchs * 1. Redistributions of source code must retain the above copyright
912869Sdfuchs *    notice, this list of conditions and the following disclaimer.
1012869Sdfuchs * 2. Redistributions in binary form must reproduce the above copyright
1112869Sdfuchs *    notice, this list of conditions and the following disclaimer in the
1212869Sdfuchs *    documentation and/or other materials provided with the distribution.
1312869Sdfuchs *
1412869Sdfuchs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1512869Sdfuchs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1612869Sdfuchs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1712869Sdfuchs * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1812869Sdfuchs * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1912869Sdfuchs * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2012869Sdfuchs * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2112869Sdfuchs * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2212869Sdfuchs * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2312869Sdfuchs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2412869Sdfuchs * SUCH DAMAGE.
2512869Sdfuchs */
2612869Sdfuchs
2712869Sdfuchs#include <sys/cdefs.h>
2812869Sdfuchs__FBSDID("$FreeBSD: stable/11/sys/boot/common/install.c 329011 2018-02-08 02:50:47Z kevans $");
2912869Sdfuchs
3012869Sdfuchs#include <sys/param.h>
3112869Sdfuchs#include <sys/socket.h>
3212869Sdfuchs#include <net/if.h>
3312869Sdfuchs#include <netinet/in.h>
3412869Sdfuchs#include <netinet/in_systm.h>
3512869Sdfuchs
3612869Sdfuchs#include <stand.h>
3712869Sdfuchs#include <net.h>
3812869Sdfuchs#include <string.h>
3912869Sdfuchs
4012869Sdfuchs#include "bootstrap.h"
4112869Sdfuchs
4212869Sdfuchsextern struct in_addr servip;
4312869Sdfuchs
4412869Sdfuchsextern int pkgfs_init(const char *, struct fs_ops *);
4512869Sdfuchsextern void pkgfs_cleanup(void);
4612869Sdfuchs
4712869SdfuchsCOMMAND_SET(install, "install", "install software package", command_install);
4812869Sdfuchs
4912869Sdfuchsstatic char *inst_kernel;
5012869Sdfuchsstatic char **inst_modules;
5112869Sdfuchsstatic char *inst_rootfs;
5212869Sdfuchsstatic char *inst_loader_rc;
5312869Sdfuchs
5412869Sdfuchsstatic int
5512869Sdfuchssetpath(char **what, char *val)
5612869Sdfuchs{
5712869Sdfuchs	char *path;
5812869Sdfuchs	size_t len;
5912869Sdfuchs	int rel;
6012869Sdfuchs
6112869Sdfuchs	len = strlen(val) + 1;
6212869Sdfuchs	rel = (val[0] != '/') ? 1 : 0;
6312869Sdfuchs	path = malloc(len + rel);
6412869Sdfuchs	if (path == NULL)
6512869Sdfuchs		return (ENOMEM);
6612869Sdfuchs	path[0] = '/';
6712869Sdfuchs	strcpy(path + rel, val);
6812869Sdfuchs
6912869Sdfuchs	*what = path;
7012869Sdfuchs	return (0);
7112869Sdfuchs}
7212869Sdfuchs
7312869Sdfuchsstatic int
7412869Sdfuchssetmultipath(char ***what, char *val)
7512869Sdfuchs{
7612869Sdfuchs	char *s, *v;
7712869Sdfuchs	int count, error, idx;
7812869Sdfuchs
7912869Sdfuchs	count = 0;
8012869Sdfuchs	v = val;
8112869Sdfuchs	do {
8212869Sdfuchs		count++;
8312869Sdfuchs		s = strchr(v, ',');
8412869Sdfuchs		v = (s == NULL) ? NULL : s + 1;
8512869Sdfuchs	} while (v != NULL);
8612869Sdfuchs
8712869Sdfuchs	*what = calloc(count + 1, sizeof(char *));
8812869Sdfuchs	if (*what == NULL)
8912869Sdfuchs		return (ENOMEM);
9012869Sdfuchs
9112869Sdfuchs	for (idx = 0; idx < count; idx++) {
9212869Sdfuchs		s = strchr(val, ',');
9312869Sdfuchs		if (s != NULL)
9412869Sdfuchs			*s++ = '\0';
9512869Sdfuchs		error = setpath(*what + idx, val);
9612869Sdfuchs		if (error)
9712869Sdfuchs			return (error);
9812869Sdfuchs		val = s;
9912869Sdfuchs	}
10012869Sdfuchs
10112869Sdfuchs	return (0);
10212869Sdfuchs}
10312869Sdfuchs
10412869Sdfuchsstatic int
10512869Sdfuchsread_metatags(int fd)
10612869Sdfuchs{
10712869Sdfuchs	char buf[1024];
10812869Sdfuchs	char *p, *tag, *val;
10912869Sdfuchs	ssize_t fsize;
11012869Sdfuchs	int error;
11112869Sdfuchs
11212869Sdfuchs	fsize = read(fd, buf, sizeof(buf));
11312869Sdfuchs	if (fsize == -1)
11412869Sdfuchs		return (errno);
11512869Sdfuchs
11612869Sdfuchs	/*
11712869Sdfuchs	 * Assume that if we read a whole buffer worth of data, we
11812869Sdfuchs	 * haven't read the entire file. In other words, the buffer
11912869Sdfuchs	 * size must always be larger than the file size. That way
12012869Sdfuchs	 * we can append a '\0' and use standard string operations.
12112869Sdfuchs	 * Return an error if this is not possible.
12212869Sdfuchs	 */
12312869Sdfuchs	if (fsize == sizeof(buf))
12412869Sdfuchs		return (ENOMEM);
12512869Sdfuchs
12612869Sdfuchs	buf[fsize] = '\0';
12712869Sdfuchs	error = 0;
12812869Sdfuchs	tag = buf;
12912869Sdfuchs	while (!error && *tag != '\0') {
13012869Sdfuchs		val = strchr(tag, '=');
13112869Sdfuchs		if (val == NULL) {
13212869Sdfuchs			error = EINVAL;
13312869Sdfuchs			break;
13412869Sdfuchs		}
13512869Sdfuchs		*val++ = '\0';
13612869Sdfuchs		p = strchr(val, '\n');
13712869Sdfuchs		if (p == NULL) {
13812869Sdfuchs			error = EINVAL;
13912869Sdfuchs			break;
14012869Sdfuchs		}
14112869Sdfuchs		*p++ = '\0';
14212869Sdfuchs
14312869Sdfuchs		if (strcmp(tag, "KERNEL") == 0)
14412869Sdfuchs			error = setpath(&inst_kernel, val);
14512869Sdfuchs		else if (strcmp(tag, "MODULES") == 0)
14612869Sdfuchs			error = setmultipath(&inst_modules, val);
14712869Sdfuchs		else if (strcmp(tag, "ROOTFS") == 0)
14812869Sdfuchs			error = setpath(&inst_rootfs, val);
14912869Sdfuchs		else if (strcmp(tag, "LOADER_RC") == 0)
15012869Sdfuchs			error = setpath(&inst_loader_rc, val);
15112869Sdfuchs
15212869Sdfuchs		tag = p;
15312869Sdfuchs	}
15412869Sdfuchs
15512869Sdfuchs	return (error);
15612869Sdfuchs}
15712869Sdfuchs
15812869Sdfuchsstatic void
15912869Sdfuchscleanup(void)
16012869Sdfuchs{
16112869Sdfuchs	u_int i;
16212869Sdfuchs
16312869Sdfuchs	if (inst_kernel != NULL) {
16412869Sdfuchs		free(inst_kernel);
16512869Sdfuchs		inst_kernel = NULL;
16612869Sdfuchs	}
16712869Sdfuchs	if (inst_modules != NULL) {
16812869Sdfuchs		i = 0;
16912869Sdfuchs		while (inst_modules[i] != NULL)
17012869Sdfuchs			free(inst_modules[i++]);
17112869Sdfuchs		free(inst_modules);
17212869Sdfuchs		inst_modules = NULL;
17312869Sdfuchs	}
17412869Sdfuchs	if (inst_rootfs != NULL) {
17512869Sdfuchs		free(inst_rootfs);
17612869Sdfuchs		inst_rootfs = NULL;
17712869Sdfuchs	}
17812869Sdfuchs	if (inst_loader_rc != NULL) {
17912869Sdfuchs		free(inst_loader_rc);
18012869Sdfuchs		inst_loader_rc = NULL;
18112869Sdfuchs	}
18212869Sdfuchs	pkgfs_cleanup();
18312869Sdfuchs}
18412869Sdfuchs
18512869Sdfuchs/*
18612869Sdfuchs * usage: install URL
18712869Sdfuchs * where: URL = (tftp|file)://[host]/<package>
18812869Sdfuchs */
18912869Sdfuchsstatic int
19012869Sdfuchsinstall(char *pkgname)
19112869Sdfuchs{
19212869Sdfuchs	static char buf[256];
19312869Sdfuchs	struct fs_ops *proto;
19412869Sdfuchs	struct preloaded_file *fp;
19512869Sdfuchs	char *s, *currdev;
19612869Sdfuchs	const char *devname;
19712869Sdfuchs	int error, fd, i, local;
19812869Sdfuchs
19912869Sdfuchs	s = strstr(pkgname, "://");
20012869Sdfuchs	if (s == NULL)
20112869Sdfuchs		goto invalid_url;
20212869Sdfuchs
20312869Sdfuchs	i = s - pkgname;
20412869Sdfuchs	if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
20512869Sdfuchs		devname = "net0";
20612869Sdfuchs		proto = &tftp_fsops;
20712869Sdfuchs		local = 0;
20812869Sdfuchs	} else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
20912869Sdfuchs		currdev = getenv("currdev");
21012869Sdfuchs		if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) {
21112869Sdfuchs			devname = "pxe0";
21212869Sdfuchs			proto = NULL;
21312869Sdfuchs		} else {
21412869Sdfuchs			devname = "disk1";
21512869Sdfuchs			proto = &dosfs_fsops;
21612869Sdfuchs		}
21712869Sdfuchs		local = 1;
21812869Sdfuchs	} else
21912869Sdfuchs		goto invalid_url;
22012869Sdfuchs
22112869Sdfuchs	s += 3;
22212869Sdfuchs	if (*s == '\0')
22312869Sdfuchs		goto invalid_url;
22412869Sdfuchs
22512869Sdfuchs	if (*s != '/' ) {
22612869Sdfuchs		if (local)
22712869Sdfuchs			goto invalid_url;
22812869Sdfuchs
22912869Sdfuchs		pkgname = strchr(s, '/');
23012869Sdfuchs		if (pkgname == NULL)
23112869Sdfuchs			goto invalid_url;
23212869Sdfuchs
23312869Sdfuchs		*pkgname = '\0';
23412869Sdfuchs		servip.s_addr = inet_addr(s);
23512869Sdfuchs		if (servip.s_addr == htonl(INADDR_NONE))
23612869Sdfuchs			goto invalid_url;
23712869Sdfuchs
23812869Sdfuchs		setenv("serverip", inet_ntoa(servip), 1);
23912869Sdfuchs
24012869Sdfuchs		*pkgname = '/';
24112869Sdfuchs	} else
24212869Sdfuchs		pkgname = s;
24312869Sdfuchs
24412869Sdfuchs	if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) {
24512869Sdfuchs		command_errmsg = "package name too long";
24612869Sdfuchs		return (CMD_ERROR);
24712869Sdfuchs	}
24812869Sdfuchs	sprintf(buf, "%s:%s", devname, pkgname);
24912869Sdfuchs	setenv("install_package", buf, 1);
25012869Sdfuchs
25112869Sdfuchs	error = pkgfs_init(buf, proto);
25212869Sdfuchs	if (error) {
25312869Sdfuchs		command_errmsg = "cannot open package";
25412869Sdfuchs		goto fail;
25512869Sdfuchs	}
25612869Sdfuchs
25712869Sdfuchs	/*
25812869Sdfuchs	 * Point of no return: unload anything that may have been
25912869Sdfuchs	 * loaded and prune the environment from harmful variables.
26012869Sdfuchs	 */
26112869Sdfuchs	unload();
26212869Sdfuchs	unsetenv("vfs.root.mountfrom");
26312869Sdfuchs
26412869Sdfuchs	/*
26512869Sdfuchs	 * read the metatags file.
26612869Sdfuchs	 */
26712869Sdfuchs	fd = open("/metatags", O_RDONLY);
26812869Sdfuchs	if (fd != -1) {
26912869Sdfuchs		error = read_metatags(fd);
27012869Sdfuchs		close(fd);
27112869Sdfuchs		if (error) {
27212869Sdfuchs			command_errmsg = "cannot load metatags";
27312869Sdfuchs			goto fail;
27412869Sdfuchs		}
27512869Sdfuchs	}
27612869Sdfuchs
27712869Sdfuchs	s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
27812869Sdfuchs	error = mod_loadkld(s, 0, NULL);
27912869Sdfuchs	if (error) {
28012869Sdfuchs		command_errmsg = "cannot load kernel from package";
28112869Sdfuchs		goto fail;
28212869Sdfuchs	}
28312869Sdfuchs
28412869Sdfuchs	/* If there is a loader.rc in the package, execute it */
28512869Sdfuchs	s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
28612869Sdfuchs	fd = open(s, O_RDONLY);
28712869Sdfuchs	if (fd != -1) {
28812869Sdfuchs		close(fd);
28912869Sdfuchs		error = include(s);
29012869Sdfuchs		if (error == CMD_ERROR)
29112869Sdfuchs			goto fail;
29212869Sdfuchs	}
29312869Sdfuchs
29412869Sdfuchs	i = 0;
29512869Sdfuchs	while (inst_modules != NULL && inst_modules[i] != NULL) {
29612869Sdfuchs		error = mod_loadkld(inst_modules[i], 0, NULL);
29712869Sdfuchs		if (error) {
29812869Sdfuchs			command_errmsg = "cannot load module(s) from package";
29912869Sdfuchs			goto fail;
30012869Sdfuchs		}
30112869Sdfuchs		i++;
30212869Sdfuchs	}
30312869Sdfuchs
30412869Sdfuchs	s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
30512869Sdfuchs	if (file_loadraw(s, "mfs_root", 1) == NULL) {
30612869Sdfuchs		error = errno;
30712869Sdfuchs		command_errmsg = "cannot load root file system";
30812869Sdfuchs		goto fail;
30912869Sdfuchs	}
31012869Sdfuchs
31112869Sdfuchs	cleanup();
31212869Sdfuchs
31312869Sdfuchs	fp = file_findfile(NULL, NULL);
31412869Sdfuchs	if (fp != NULL)
31512869Sdfuchs		file_formats[fp->f_loader]->l_exec(fp);
31612869Sdfuchs	error = CMD_ERROR;
31712869Sdfuchs	command_errmsg = "unable to start installation";
31812869Sdfuchs
31912869Sdfuchs fail:
32012869Sdfuchs	sprintf(buf, "%s (error %d)", command_errmsg, error);
32112869Sdfuchs	cleanup();
32212869Sdfuchs	unload();
32312869Sdfuchs	exclusive_file_system = NULL;
32412869Sdfuchs	command_errmsg = buf;	/* buf is static. */
32512869Sdfuchs	return (CMD_ERROR);
32612869Sdfuchs
32712869Sdfuchs invalid_url:
32812869Sdfuchs	command_errmsg = "invalid URL";
32912869Sdfuchs	return (CMD_ERROR);
33012869Sdfuchs}
33112869Sdfuchs
33212869Sdfuchsstatic int
33312869Sdfuchscommand_install(int argc, char *argv[])
33412869Sdfuchs{
33512869Sdfuchs	int argidx;
33612869Sdfuchs
33712869Sdfuchs	unsetenv("install_format");
33812869Sdfuchs
33912869Sdfuchs	argidx = 1;
34012869Sdfuchs	while (1) {
34112869Sdfuchs		if (argc == argidx) {
34212869Sdfuchs			command_errmsg =
34312869Sdfuchs			    "usage: install [--format] <URL>";
34412869Sdfuchs			return (CMD_ERROR);
34512869Sdfuchs		}
34612869Sdfuchs		if (!strcmp(argv[argidx], "--format")) {
34712869Sdfuchs			setenv("install_format", "yes", 1);
34812869Sdfuchs			argidx++;
34912869Sdfuchs			continue;
35012869Sdfuchs		}
35112869Sdfuchs		break;
35212869Sdfuchs	}
35312869Sdfuchs
35412869Sdfuchs	return (install(argv[argidx]));
35512869Sdfuchs}
35612869Sdfuchs