1269621Smarcel/*-
2269621Smarcel * Copyright (c) 2008-2014, Juniper Networks, Inc.
3269621Smarcel * All rights reserved.
4269621Smarcel *
5269621Smarcel * Redistribution and use in source and binary forms, with or without
6269621Smarcel * modification, are permitted provided that the following conditions
7269621Smarcel * are met:
8269621Smarcel * 1. Redistributions of source code must retain the above copyright
9269621Smarcel *    notice, this list of conditions and the following disclaimer.
10269621Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11269621Smarcel *    notice, this list of conditions and the following disclaimer in the
12269621Smarcel *    documentation and/or other materials provided with the distribution.
13269621Smarcel *
14269621Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15269621Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16269621Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17269621Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18269621Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19269621Smarcel * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20269621Smarcel * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21269621Smarcel * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22269621Smarcel * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23269621Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24269621Smarcel * SUCH DAMAGE.
25269621Smarcel */
26269621Smarcel
27269621Smarcel#include <sys/cdefs.h>
28269621Smarcel__FBSDID("$FreeBSD: stable/11/stand/common/install.c 329183 2018-02-12 20:51:28Z kevans $");
29269621Smarcel
30269621Smarcel#include <sys/param.h>
31269621Smarcel#include <sys/socket.h>
32269621Smarcel#include <net/if.h>
33269621Smarcel#include <netinet/in.h>
34269621Smarcel#include <netinet/in_systm.h>
35269621Smarcel
36269621Smarcel#include <stand.h>
37269621Smarcel#include <net.h>
38269621Smarcel#include <string.h>
39269621Smarcel
40269621Smarcel#include "bootstrap.h"
41269621Smarcel
42269621Smarcelextern struct in_addr servip;
43269621Smarcel
44269621Smarcelextern int pkgfs_init(const char *, struct fs_ops *);
45269621Smarcelextern void pkgfs_cleanup(void);
46269621Smarcel
47269621SmarcelCOMMAND_SET(install, "install", "install software package", command_install);
48269621Smarcel
49269621Smarcelstatic char *inst_kernel;
50269621Smarcelstatic char **inst_modules;
51269621Smarcelstatic char *inst_rootfs;
52329011Skevansstatic char *inst_loader_rc;
53269621Smarcel
54269621Smarcelstatic int
55269621Smarcelsetpath(char **what, char *val)
56269621Smarcel{
57269621Smarcel	char *path;
58269621Smarcel	size_t len;
59269621Smarcel	int rel;
60269621Smarcel
61269621Smarcel	len = strlen(val) + 1;
62269621Smarcel	rel = (val[0] != '/') ? 1 : 0;
63269621Smarcel	path = malloc(len + rel);
64269621Smarcel	if (path == NULL)
65269621Smarcel		return (ENOMEM);
66269621Smarcel	path[0] = '/';
67269621Smarcel	strcpy(path + rel, val);
68269621Smarcel
69269621Smarcel	*what = path;
70269621Smarcel	return (0);
71269621Smarcel}
72269621Smarcel
73269621Smarcelstatic int
74269621Smarcelsetmultipath(char ***what, char *val)
75269621Smarcel{
76269621Smarcel	char *s, *v;
77269621Smarcel	int count, error, idx;
78269621Smarcel
79269621Smarcel	count = 0;
80269621Smarcel	v = val;
81269621Smarcel	do {
82269621Smarcel		count++;
83269621Smarcel		s = strchr(v, ',');
84269621Smarcel		v = (s == NULL) ? NULL : s + 1;
85269621Smarcel	} while (v != NULL);
86269621Smarcel
87269621Smarcel	*what = calloc(count + 1, sizeof(char *));
88269621Smarcel	if (*what == NULL)
89269621Smarcel		return (ENOMEM);
90269621Smarcel
91269621Smarcel	for (idx = 0; idx < count; idx++) {
92269621Smarcel		s = strchr(val, ',');
93269621Smarcel		if (s != NULL)
94269621Smarcel			*s++ = '\0';
95269621Smarcel		error = setpath(*what + idx, val);
96269621Smarcel		if (error)
97269621Smarcel			return (error);
98269621Smarcel		val = s;
99269621Smarcel	}
100269621Smarcel
101269621Smarcel	return (0);
102269621Smarcel}
103269621Smarcel
104269621Smarcelstatic int
105269621Smarcelread_metatags(int fd)
106269621Smarcel{
107269621Smarcel	char buf[1024];
108269621Smarcel	char *p, *tag, *val;
109269621Smarcel	ssize_t fsize;
110269621Smarcel	int error;
111269621Smarcel
112269621Smarcel	fsize = read(fd, buf, sizeof(buf));
113269621Smarcel	if (fsize == -1)
114269621Smarcel		return (errno);
115269621Smarcel
116269621Smarcel	/*
117269621Smarcel	 * Assume that if we read a whole buffer worth of data, we
118269621Smarcel	 * haven't read the entire file. In other words, the buffer
119269621Smarcel	 * size must always be larger than the file size. That way
120269621Smarcel	 * we can append a '\0' and use standard string operations.
121269621Smarcel	 * Return an error if this is not possible.
122269621Smarcel	 */
123269621Smarcel	if (fsize == sizeof(buf))
124269621Smarcel		return (ENOMEM);
125269621Smarcel
126269621Smarcel	buf[fsize] = '\0';
127269621Smarcel	error = 0;
128269621Smarcel	tag = buf;
129269621Smarcel	while (!error && *tag != '\0') {
130269621Smarcel		val = strchr(tag, '=');
131269621Smarcel		if (val == NULL) {
132269621Smarcel			error = EINVAL;
133269621Smarcel			break;
134269621Smarcel		}
135269621Smarcel		*val++ = '\0';
136269621Smarcel		p = strchr(val, '\n');
137269621Smarcel		if (p == NULL) {
138269621Smarcel			error = EINVAL;
139269621Smarcel			break;
140269621Smarcel		}
141269621Smarcel		*p++ = '\0';
142269621Smarcel
143269621Smarcel		if (strcmp(tag, "KERNEL") == 0)
144269621Smarcel			error = setpath(&inst_kernel, val);
145269621Smarcel		else if (strcmp(tag, "MODULES") == 0)
146269621Smarcel			error = setmultipath(&inst_modules, val);
147269621Smarcel		else if (strcmp(tag, "ROOTFS") == 0)
148269621Smarcel			error = setpath(&inst_rootfs, val);
149329011Skevans		else if (strcmp(tag, "LOADER_RC") == 0)
150329011Skevans			error = setpath(&inst_loader_rc, val);
151269621Smarcel
152269621Smarcel		tag = p;
153269621Smarcel	}
154269621Smarcel
155269621Smarcel	return (error);
156269621Smarcel}
157269621Smarcel
158269621Smarcelstatic void
159269621Smarcelcleanup(void)
160269621Smarcel{
161269621Smarcel	u_int i;
162269621Smarcel
163269621Smarcel	if (inst_kernel != NULL) {
164269621Smarcel		free(inst_kernel);
165269621Smarcel		inst_kernel = NULL;
166269621Smarcel	}
167269621Smarcel	if (inst_modules != NULL) {
168269621Smarcel		i = 0;
169269621Smarcel		while (inst_modules[i] != NULL)
170269621Smarcel			free(inst_modules[i++]);
171269621Smarcel		free(inst_modules);
172269621Smarcel		inst_modules = NULL;
173269621Smarcel	}
174269621Smarcel	if (inst_rootfs != NULL) {
175269621Smarcel		free(inst_rootfs);
176269621Smarcel		inst_rootfs = NULL;
177269621Smarcel	}
178329011Skevans	if (inst_loader_rc != NULL) {
179329011Skevans		free(inst_loader_rc);
180329011Skevans		inst_loader_rc = NULL;
181329011Skevans	}
182269621Smarcel	pkgfs_cleanup();
183269621Smarcel}
184269621Smarcel
185269621Smarcel/*
186269621Smarcel * usage: install URL
187269621Smarcel * where: URL = (tftp|file)://[host]/<package>
188269621Smarcel */
189269621Smarcelstatic int
190269621Smarcelinstall(char *pkgname)
191269621Smarcel{
192269621Smarcel	static char buf[256];
193269621Smarcel	struct fs_ops *proto;
194269621Smarcel	struct preloaded_file *fp;
195269621Smarcel	char *s, *currdev;
196269621Smarcel	const char *devname;
197269621Smarcel	int error, fd, i, local;
198269621Smarcel
199269621Smarcel	s = strstr(pkgname, "://");
200269621Smarcel	if (s == NULL)
201269621Smarcel		goto invalid_url;
202269621Smarcel
203269621Smarcel	i = s - pkgname;
204269621Smarcel	if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
205269621Smarcel		devname = "net0";
206269621Smarcel		proto = &tftp_fsops;
207269621Smarcel		local = 0;
208269621Smarcel	} else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
209269621Smarcel		currdev = getenv("currdev");
210269621Smarcel		if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) {
211269621Smarcel			devname = "pxe0";
212269621Smarcel			proto = NULL;
213269621Smarcel		} else {
214269621Smarcel			devname = "disk1";
215269621Smarcel			proto = &dosfs_fsops;
216269621Smarcel		}
217269621Smarcel		local = 1;
218269621Smarcel	} else
219269621Smarcel		goto invalid_url;
220269621Smarcel
221269621Smarcel	s += 3;
222269621Smarcel	if (*s == '\0')
223269621Smarcel		goto invalid_url;
224269621Smarcel
225269621Smarcel	if (*s != '/' ) {
226269621Smarcel		if (local)
227269621Smarcel			goto invalid_url;
228269621Smarcel
229269621Smarcel		pkgname = strchr(s, '/');
230269621Smarcel		if (pkgname == NULL)
231269621Smarcel			goto invalid_url;
232269621Smarcel
233269621Smarcel		*pkgname = '\0';
234269621Smarcel		servip.s_addr = inet_addr(s);
235269621Smarcel		if (servip.s_addr == htonl(INADDR_NONE))
236269621Smarcel			goto invalid_url;
237269621Smarcel
238269621Smarcel		setenv("serverip", inet_ntoa(servip), 1);
239269621Smarcel
240269621Smarcel		*pkgname = '/';
241269621Smarcel	} else
242269621Smarcel		pkgname = s;
243269621Smarcel
244269621Smarcel	if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) {
245269621Smarcel		command_errmsg = "package name too long";
246269621Smarcel		return (CMD_ERROR);
247269621Smarcel	}
248269621Smarcel	sprintf(buf, "%s:%s", devname, pkgname);
249269621Smarcel	setenv("install_package", buf, 1);
250269621Smarcel
251269621Smarcel	error = pkgfs_init(buf, proto);
252269621Smarcel	if (error) {
253269621Smarcel		command_errmsg = "cannot open package";
254269621Smarcel		goto fail;
255269621Smarcel	}
256269621Smarcel
257269621Smarcel	/*
258269621Smarcel	 * Point of no return: unload anything that may have been
259269621Smarcel	 * loaded and prune the environment from harmful variables.
260269621Smarcel	 */
261269621Smarcel	unload();
262269621Smarcel	unsetenv("vfs.root.mountfrom");
263269621Smarcel
264269621Smarcel	/*
265269621Smarcel	 * read the metatags file.
266269621Smarcel	 */
267269621Smarcel	fd = open("/metatags", O_RDONLY);
268269621Smarcel	if (fd != -1) {
269269621Smarcel		error = read_metatags(fd);
270269621Smarcel		close(fd);
271269621Smarcel		if (error) {
272269621Smarcel			command_errmsg = "cannot load metatags";
273269621Smarcel			goto fail;
274269621Smarcel		}
275269621Smarcel	}
276269621Smarcel
277269621Smarcel	s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
278269621Smarcel	error = mod_loadkld(s, 0, NULL);
279269621Smarcel	if (error) {
280269621Smarcel		command_errmsg = "cannot load kernel from package";
281269621Smarcel		goto fail;
282269621Smarcel	}
283269621Smarcel
284329011Skevans	/* If there is a loader.rc in the package, execute it */
285329011Skevans	s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
286329011Skevans	fd = open(s, O_RDONLY);
287329011Skevans	if (fd != -1) {
288329011Skevans		close(fd);
289329183Skevans		error = inter_include(s);
290329011Skevans		if (error == CMD_ERROR)
291329011Skevans			goto fail;
292329011Skevans	}
293329011Skevans
294269621Smarcel	i = 0;
295269621Smarcel	while (inst_modules != NULL && inst_modules[i] != NULL) {
296269621Smarcel		error = mod_loadkld(inst_modules[i], 0, NULL);
297269621Smarcel		if (error) {
298269621Smarcel			command_errmsg = "cannot load module(s) from package";
299269621Smarcel			goto fail;
300269621Smarcel		}
301269621Smarcel		i++;
302269621Smarcel	}
303269621Smarcel
304269621Smarcel	s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
305329011Skevans	if (file_loadraw(s, "mfs_root", 1) == NULL) {
306269621Smarcel		error = errno;
307269621Smarcel		command_errmsg = "cannot load root file system";
308269621Smarcel		goto fail;
309269621Smarcel	}
310269621Smarcel
311269621Smarcel	cleanup();
312269621Smarcel
313269621Smarcel	fp = file_findfile(NULL, NULL);
314269621Smarcel	if (fp != NULL)
315269621Smarcel		file_formats[fp->f_loader]->l_exec(fp);
316269621Smarcel	error = CMD_ERROR;
317269621Smarcel	command_errmsg = "unable to start installation";
318269621Smarcel
319269621Smarcel fail:
320269621Smarcel	sprintf(buf, "%s (error %d)", command_errmsg, error);
321269621Smarcel	cleanup();
322269621Smarcel	unload();
323269621Smarcel	exclusive_file_system = NULL;
324269621Smarcel	command_errmsg = buf;	/* buf is static. */
325269621Smarcel	return (CMD_ERROR);
326269621Smarcel
327269621Smarcel invalid_url:
328269621Smarcel	command_errmsg = "invalid URL";
329269621Smarcel	return (CMD_ERROR);
330269621Smarcel}
331269621Smarcel
332269621Smarcelstatic int
333269621Smarcelcommand_install(int argc, char *argv[])
334269621Smarcel{
335269621Smarcel	int argidx;
336269621Smarcel
337269621Smarcel	unsetenv("install_format");
338269621Smarcel
339269621Smarcel	argidx = 1;
340269621Smarcel	while (1) {
341269621Smarcel		if (argc == argidx) {
342269621Smarcel			command_errmsg =
343269621Smarcel			    "usage: install [--format] <URL>";
344269621Smarcel			return (CMD_ERROR);
345269621Smarcel		}
346269621Smarcel		if (!strcmp(argv[argidx], "--format")) {
347269621Smarcel			setenv("install_format", "yes", 1);
348269621Smarcel			argidx++;
349269621Smarcel			continue;
350269621Smarcel		}
351269621Smarcel		break;
352269621Smarcel	}
353269621Smarcel
354269621Smarcel	return (install(argv[argidx]));
355269621Smarcel}
356