190539cbmarcel/*-
290539cbmarcel * Copyright (c) 2008-2014, Juniper Networks, Inc.
390539cbmarcel * All rights reserved.
490539cbmarcel *
590539cbmarcel * Redistribution and use in source and binary forms, with or without
690539cbmarcel * modification, are permitted provided that the following conditions
790539cbmarcel * are met:
890539cbmarcel * 1. Redistributions of source code must retain the above copyright
990539cbmarcel *    notice, this list of conditions and the following disclaimer.
1090539cbmarcel * 2. Redistributions in binary form must reproduce the above copyright
1190539cbmarcel *    notice, this list of conditions and the following disclaimer in the
1290539cbmarcel *    documentation and/or other materials provided with the distribution.
1390539cbmarcel *
1490539cbmarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1590539cbmarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1690539cbmarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1790539cbmarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1890539cbmarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1990539cbmarcel * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2090539cbmarcel * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2190539cbmarcel * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2290539cbmarcel * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2390539cbmarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2490539cbmarcel * SUCH DAMAGE.
2590539cbmarcel */
2690539cbmarcel
2790539cbmarcel#include <sys/cdefs.h>
2890539cbmarcel__FBSDID("$FreeBSD$");
2990539cbmarcel
3090539cbmarcel#include <sys/param.h>
3190539cbmarcel#include <sys/socket.h>
3290539cbmarcel#include <net/if.h>
3390539cbmarcel#include <netinet/in.h>
3490539cbmarcel#include <netinet/in_systm.h>
3590539cbmarcel
3690539cbmarcel#include <stand.h>
3790539cbmarcel#include <net.h>
3890539cbmarcel#include <string.h>
3990539cbmarcel
4090539cbmarcel#include "bootstrap.h"
4190539cbmarcel
4290539cbmarcelextern struct in_addr servip;
4390539cbmarcel
4490539cbmarcelextern int pkgfs_init(const char *, struct fs_ops *);
4590539cbmarcelextern void pkgfs_cleanup(void);
4690539cbmarcel
4790539cbmarcelCOMMAND_SET(install, "install", "install software package", command_install);
4890539cbmarcel
4990539cbmarcelstatic char *inst_kernel;
5090539cbmarcelstatic char **inst_modules;
5190539cbmarcelstatic char *inst_rootfs;
520e32a61stevekstatic char *inst_loader_rc;
5390539cbmarcel
5490539cbmarcelstatic int
5590539cbmarcelsetpath(char **what, char *val)
5690539cbmarcel{
5790539cbmarcel	char *path;
5890539cbmarcel	size_t len;
5990539cbmarcel	int rel;
6090539cbmarcel
6190539cbmarcel	len = strlen(val) + 1;
6290539cbmarcel	rel = (val[0] != '/') ? 1 : 0;
6390539cbmarcel	path = malloc(len + rel);
6490539cbmarcel	if (path == NULL)
6590539cbmarcel		return (ENOMEM);
6690539cbmarcel	path[0] = '/';
6790539cbmarcel	strcpy(path + rel, val);
6890539cbmarcel
6990539cbmarcel	*what = path;
7090539cbmarcel	return (0);
7190539cbmarcel}
7290539cbmarcel
7390539cbmarcelstatic int
7490539cbmarcelsetmultipath(char ***what, char *val)
7590539cbmarcel{
7690539cbmarcel	char *s, *v;
7790539cbmarcel	int count, error, idx;
7890539cbmarcel
7990539cbmarcel	count = 0;
8090539cbmarcel	v = val;
8190539cbmarcel	do {
8290539cbmarcel		count++;
8390539cbmarcel		s = strchr(v, ',');
8490539cbmarcel		v = (s == NULL) ? NULL : s + 1;
8590539cbmarcel	} while (v != NULL);
8690539cbmarcel
8790539cbmarcel	*what = calloc(count + 1, sizeof(char *));
8890539cbmarcel	if (*what == NULL)
8990539cbmarcel		return (ENOMEM);
9090539cbmarcel
9190539cbmarcel	for (idx = 0; idx < count; idx++) {
9290539cbmarcel		s = strchr(val, ',');
9390539cbmarcel		if (s != NULL)
9490539cbmarcel			*s++ = '\0';
9590539cbmarcel		error = setpath(*what + idx, val);
9690539cbmarcel		if (error)
9790539cbmarcel			return (error);
9890539cbmarcel		val = s;
9990539cbmarcel	}
10090539cbmarcel
10190539cbmarcel	return (0);
10290539cbmarcel}
10390539cbmarcel
10490539cbmarcelstatic int
10590539cbmarcelread_metatags(int fd)
10690539cbmarcel{
10790539cbmarcel	char buf[1024];
10890539cbmarcel	char *p, *tag, *val;
10990539cbmarcel	ssize_t fsize;
11090539cbmarcel	int error;
11190539cbmarcel
11290539cbmarcel	fsize = read(fd, buf, sizeof(buf));
11390539cbmarcel	if (fsize == -1)
11490539cbmarcel		return (errno);
11590539cbmarcel
11690539cbmarcel	/*
11790539cbmarcel	 * Assume that if we read a whole buffer worth of data, we
11890539cbmarcel	 * haven't read the entire file. In other words, the buffer
11990539cbmarcel	 * size must always be larger than the file size. That way
12090539cbmarcel	 * we can append a '\0' and use standard string operations.
12190539cbmarcel	 * Return an error if this is not possible.
12290539cbmarcel	 */
12390539cbmarcel	if (fsize == sizeof(buf))
12490539cbmarcel		return (ENOMEM);
12590539cbmarcel
12690539cbmarcel	buf[fsize] = '\0';
12790539cbmarcel	error = 0;
12890539cbmarcel	tag = buf;
12990539cbmarcel	while (!error && *tag != '\0') {
13090539cbmarcel		val = strchr(tag, '=');
13190539cbmarcel		if (val == NULL) {
13290539cbmarcel			error = EINVAL;
13390539cbmarcel			break;
13490539cbmarcel		}
13590539cbmarcel		*val++ = '\0';
13690539cbmarcel		p = strchr(val, '\n');
13790539cbmarcel		if (p == NULL) {
13890539cbmarcel			error = EINVAL;
13990539cbmarcel			break;
14090539cbmarcel		}
14190539cbmarcel		*p++ = '\0';
14290539cbmarcel
14390539cbmarcel		if (strcmp(tag, "KERNEL") == 0)
14490539cbmarcel			error = setpath(&inst_kernel, val);
14590539cbmarcel		else if (strcmp(tag, "MODULES") == 0)
14690539cbmarcel			error = setmultipath(&inst_modules, val);
14790539cbmarcel		else if (strcmp(tag, "ROOTFS") == 0)
14890539cbmarcel			error = setpath(&inst_rootfs, val);
1490e32a61stevek		else if (strcmp(tag, "LOADER_RC") == 0)
1500e32a61stevek			error = setpath(&inst_loader_rc, val);
15190539cbmarcel
15290539cbmarcel		tag = p;
15390539cbmarcel	}
15490539cbmarcel
15590539cbmarcel	return (error);
15690539cbmarcel}
15790539cbmarcel
15890539cbmarcelstatic void
15990539cbmarcelcleanup(void)
16090539cbmarcel{
16190539cbmarcel	u_int i;
16290539cbmarcel
16390539cbmarcel	if (inst_kernel != NULL) {
16490539cbmarcel		free(inst_kernel);
16590539cbmarcel		inst_kernel = NULL;
16690539cbmarcel	}
16790539cbmarcel	if (inst_modules != NULL) {
16890539cbmarcel		i = 0;
16990539cbmarcel		while (inst_modules[i] != NULL)
17090539cbmarcel			free(inst_modules[i++]);
17190539cbmarcel		free(inst_modules);
17290539cbmarcel		inst_modules = NULL;
17390539cbmarcel	}
17490539cbmarcel	if (inst_rootfs != NULL) {
17590539cbmarcel		free(inst_rootfs);
17690539cbmarcel		inst_rootfs = NULL;
17790539cbmarcel	}
1780e32a61stevek	if (inst_loader_rc != NULL) {
1790e32a61stevek		free(inst_loader_rc);
1800e32a61stevek		inst_loader_rc = NULL;
1810e32a61stevek	}
18290539cbmarcel	pkgfs_cleanup();
18390539cbmarcel}
18490539cbmarcel
18590539cbmarcel/*
18690539cbmarcel * usage: install URL
18790539cbmarcel * where: URL = (tftp|file)://[host]/<package>
18890539cbmarcel */
18990539cbmarcelstatic int
19090539cbmarcelinstall(char *pkgname)
19190539cbmarcel{
19290539cbmarcel	static char buf[256];
19390539cbmarcel	struct fs_ops *proto;
19490539cbmarcel	struct preloaded_file *fp;
19590539cbmarcel	char *s, *currdev;
19690539cbmarcel	const char *devname;
19790539cbmarcel	int error, fd, i, local;
19890539cbmarcel
19990539cbmarcel	s = strstr(pkgname, "://");
20090539cbmarcel	if (s == NULL)
20190539cbmarcel		goto invalid_url;
20290539cbmarcel
20390539cbmarcel	i = s - pkgname;
20490539cbmarcel	if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
20590539cbmarcel		devname = "net0";
20690539cbmarcel		proto = &tftp_fsops;
20790539cbmarcel		local = 0;
20890539cbmarcel	} else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
20990539cbmarcel		currdev = getenv("currdev");
21090539cbmarcel		if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) {
21190539cbmarcel			devname = "pxe0";
21290539cbmarcel			proto = NULL;
21390539cbmarcel		} else {
21490539cbmarcel			devname = "disk1";
21590539cbmarcel			proto = &dosfs_fsops;
21690539cbmarcel		}
21790539cbmarcel		local = 1;
21890539cbmarcel	} else
21990539cbmarcel		goto invalid_url;
22090539cbmarcel
22190539cbmarcel	s += 3;
22290539cbmarcel	if (*s == '\0')
22390539cbmarcel		goto invalid_url;
22490539cbmarcel
22590539cbmarcel	if (*s != '/' ) {
22690539cbmarcel		if (local)
22790539cbmarcel			goto invalid_url;
22890539cbmarcel
22990539cbmarcel		pkgname = strchr(s, '/');
23090539cbmarcel		if (pkgname == NULL)
23190539cbmarcel			goto invalid_url;
23290539cbmarcel
23390539cbmarcel		*pkgname = '\0';
23490539cbmarcel		servip.s_addr = inet_addr(s);
23590539cbmarcel		if (servip.s_addr == htonl(INADDR_NONE))
23690539cbmarcel			goto invalid_url;
23790539cbmarcel
23890539cbmarcel		setenv("serverip", inet_ntoa(servip), 1);
23990539cbmarcel
24090539cbmarcel		*pkgname = '/';
24190539cbmarcel	} else
24290539cbmarcel		pkgname = s;
24390539cbmarcel
24490539cbmarcel	if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) {
24590539cbmarcel		command_errmsg = "package name too long";
24690539cbmarcel		return (CMD_ERROR);
24790539cbmarcel	}
24890539cbmarcel	sprintf(buf, "%s:%s", devname, pkgname);
24990539cbmarcel	setenv("install_package", buf, 1);
25090539cbmarcel
25190539cbmarcel	error = pkgfs_init(buf, proto);
25290539cbmarcel	if (error) {
25390539cbmarcel		command_errmsg = "cannot open package";
25490539cbmarcel		goto fail;
25590539cbmarcel	}
25690539cbmarcel
25790539cbmarcel	/*
25890539cbmarcel	 * Point of no return: unload anything that may have been
25990539cbmarcel	 * loaded and prune the environment from harmful variables.
26090539cbmarcel	 */
26190539cbmarcel	unload();
26290539cbmarcel	unsetenv("vfs.root.mountfrom");
26390539cbmarcel
26490539cbmarcel	/*
26590539cbmarcel	 * read the metatags file.
26690539cbmarcel	 */
26790539cbmarcel	fd = open("/metatags", O_RDONLY);
26890539cbmarcel	if (fd != -1) {
26990539cbmarcel		error = read_metatags(fd);
27090539cbmarcel		close(fd);
27190539cbmarcel		if (error) {
27290539cbmarcel			command_errmsg = "cannot load metatags";
27390539cbmarcel			goto fail;
27490539cbmarcel		}
27590539cbmarcel	}
27690539cbmarcel
27790539cbmarcel	s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
27890539cbmarcel	error = mod_loadkld(s, 0, NULL);
27990539cbmarcel	if (error) {
28090539cbmarcel		command_errmsg = "cannot load kernel from package";
28190539cbmarcel		goto fail;
28290539cbmarcel	}
28390539cbmarcel
2840e32a61stevek	/* If there is a loader.rc in the package, execute it */
2850e32a61stevek	s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
2860e32a61stevek	fd = open(s, O_RDONLY);
2870e32a61stevek	if (fd != -1) {
2880e32a61stevek		close(fd);
289450f8daimp		error = inter_include(s);
2900e32a61stevek		if (error == CMD_ERROR)
2910e32a61stevek			goto fail;
2920e32a61stevek	}
2930e32a61stevek
29490539cbmarcel	i = 0;
29590539cbmarcel	while (inst_modules != NULL && inst_modules[i] != NULL) {
29690539cbmarcel		error = mod_loadkld(inst_modules[i], 0, NULL);
29790539cbmarcel		if (error) {
29890539cbmarcel			command_errmsg = "cannot load module(s) from package";
29990539cbmarcel			goto fail;
30090539cbmarcel		}
30190539cbmarcel		i++;
30290539cbmarcel	}
30390539cbmarcel
30490539cbmarcel	s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
305e871df6stevek	if (file_loadraw(s, "mfs_root", 1) == NULL) {
30690539cbmarcel		error = errno;
30790539cbmarcel		command_errmsg = "cannot load root file system";
30890539cbmarcel		goto fail;
30990539cbmarcel	}
31090539cbmarcel
31190539cbmarcel	cleanup();
31290539cbmarcel
31390539cbmarcel	fp = file_findfile(NULL, NULL);
31490539cbmarcel	if (fp != NULL)
31590539cbmarcel		file_formats[fp->f_loader]->l_exec(fp);
31690539cbmarcel	error = CMD_ERROR;
31790539cbmarcel	command_errmsg = "unable to start installation";
31890539cbmarcel
31990539cbmarcel fail:
32090539cbmarcel	sprintf(buf, "%s (error %d)", command_errmsg, error);
32190539cbmarcel	cleanup();
32290539cbmarcel	unload();
32390539cbmarcel	exclusive_file_system = NULL;
32490539cbmarcel	command_errmsg = buf;	/* buf is static. */
32590539cbmarcel	return (CMD_ERROR);
32690539cbmarcel
32790539cbmarcel invalid_url:
32890539cbmarcel	command_errmsg = "invalid URL";
32990539cbmarcel	return (CMD_ERROR);
33090539cbmarcel}
33190539cbmarcel
33290539cbmarcelstatic int
33390539cbmarcelcommand_install(int argc, char *argv[])
33490539cbmarcel{
33590539cbmarcel	int argidx;
33690539cbmarcel
33790539cbmarcel	unsetenv("install_format");
33890539cbmarcel
33990539cbmarcel	argidx = 1;
34090539cbmarcel	while (1) {
34190539cbmarcel		if (argc == argidx) {
34290539cbmarcel			command_errmsg =
34390539cbmarcel			    "usage: install [--format] <URL>";
34490539cbmarcel			return (CMD_ERROR);
34590539cbmarcel		}
34690539cbmarcel		if (!strcmp(argv[argidx], "--format")) {
34790539cbmarcel			setenv("install_format", "yes", 1);
34890539cbmarcel			argidx++;
34990539cbmarcel			continue;
35090539cbmarcel		}
35190539cbmarcel		break;
35290539cbmarcel	}
35390539cbmarcel
35490539cbmarcel	return (install(argv[argidx]));
35590539cbmarcel}
356