install.c revision 269621
1254721Semaste/*-
2254721Semaste * Copyright (c) 2008-2014, Juniper Networks, Inc.
3254721Semaste * All rights reserved.
4254721Semaste *
5254721Semaste * Redistribution and use in source and binary forms, with or without
6254721Semaste * modification, are permitted provided that the following conditions
7254721Semaste * are met:
8254721Semaste * 1. Redistributions of source code must retain the above copyright
9254721Semaste *    notice, this list of conditions and the following disclaimer.
10254721Semaste * 2. Redistributions in binary form must reproduce the above copyright
11254721Semaste *    notice, this list of conditions and the following disclaimer in the
12254721Semaste *    documentation and/or other materials provided with the distribution.
13254721Semaste *
14254721Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15254721Semaste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16254721Semaste * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17254721Semaste * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18254721Semaste * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19254721Semaste * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20254721Semaste * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21254721Semaste * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22254721Semaste * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23254721Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24254721Semaste * SUCH DAMAGE.
25254721Semaste */
26254721Semaste
27254721Semaste#include <sys/cdefs.h>
28254721Semaste__FBSDID("$FreeBSD: head/sys/boot/common/install.c 269621 2014-08-06 00:36:04Z marcel $");
29254721Semaste
30254721Semaste#include <sys/param.h>
31254721Semaste#include <sys/socket.h>
32254721Semaste#include <net/if.h>
33254721Semaste#include <netinet/in.h>
34254721Semaste#include <netinet/in_systm.h>
35254721Semaste
36254721Semaste#include <stand.h>
37254721Semaste#include <net.h>
38254721Semaste#include <string.h>
39254721Semaste
40254721Semaste#include "bootstrap.h"
41254721Semaste
42254721Semasteextern struct in_addr rootip;
43254721Semasteextern struct in_addr servip;
44254721Semaste
45254721Semasteextern int pkgfs_init(const char *, struct fs_ops *);
46254721Semasteextern void pkgfs_cleanup(void);
47254721Semaste
48254721SemasteCOMMAND_SET(install, "install", "install software package", command_install);
49254721Semaste
50254721Semastestatic char *inst_kernel;
51254721Semastestatic char **inst_modules;
52254721Semastestatic char *inst_rootfs;
53254721Semaste
54254721Semastestatic int
55254721Semastesetpath(char **what, char *val)
56254721Semaste{
57254721Semaste	char *path;
58254721Semaste	size_t len;
59254721Semaste	int rel;
60254721Semaste
61254721Semaste	len = strlen(val) + 1;
62254721Semaste	rel = (val[0] != '/') ? 1 : 0;
63254721Semaste	path = malloc(len + rel);
64254721Semaste	if (path == NULL)
65254721Semaste		return (ENOMEM);
66254721Semaste	path[0] = '/';
67254721Semaste	strcpy(path + rel, val);
68254721Semaste
69254721Semaste	*what = path;
70254721Semaste	return (0);
71254721Semaste}
72254721Semaste
73254721Semastestatic int
74254721Semastesetmultipath(char ***what, char *val)
75254721Semaste{
76254721Semaste	char *s, *v;
77254721Semaste	int count, error, idx;
78254721Semaste
79254721Semaste	count = 0;
80254721Semaste	v = val;
81254721Semaste	do {
82254721Semaste		count++;
83254721Semaste		s = strchr(v, ',');
84254721Semaste		v = (s == NULL) ? NULL : s + 1;
85254721Semaste	} while (v != NULL);
86254721Semaste
87254721Semaste	*what = calloc(count + 1, sizeof(char *));
88254721Semaste	if (*what == NULL)
89254721Semaste		return (ENOMEM);
90254721Semaste
91254721Semaste	for (idx = 0; idx < count; idx++) {
92254721Semaste		s = strchr(val, ',');
93254721Semaste		if (s != NULL)
94254721Semaste			*s++ = '\0';
95254721Semaste		error = setpath(*what + idx, val);
96254721Semaste		if (error)
97254721Semaste			return (error);
98254721Semaste		val = s;
99254721Semaste	}
100254721Semaste
101254721Semaste	return (0);
102254721Semaste}
103254721Semaste
104254721Semastestatic int
105254721Semasteread_metatags(int fd)
106254721Semaste{
107254721Semaste	char buf[1024];
108254721Semaste	char *p, *tag, *val;
109254721Semaste	ssize_t fsize;
110254721Semaste	int error;
111254721Semaste
112254721Semaste	fsize = read(fd, buf, sizeof(buf));
113254721Semaste	if (fsize == -1)
114254721Semaste		return (errno);
115254721Semaste
116254721Semaste	/*
117254721Semaste	 * Assume that if we read a whole buffer worth of data, we
118254721Semaste	 * haven't read the entire file. In other words, the buffer
119254721Semaste	 * size must always be larger than the file size. That way
120254721Semaste	 * we can append a '\0' and use standard string operations.
121254721Semaste	 * Return an error if this is not possible.
122254721Semaste	 */
123254721Semaste	if (fsize == sizeof(buf))
124254721Semaste		return (ENOMEM);
125254721Semaste
126254721Semaste	buf[fsize] = '\0';
127254721Semaste	error = 0;
128254721Semaste	tag = buf;
129254721Semaste	while (!error && *tag != '\0') {
130254721Semaste		val = strchr(tag, '=');
131254721Semaste		if (val == NULL) {
132254721Semaste			error = EINVAL;
133254721Semaste			break;
134254721Semaste		}
135254721Semaste		*val++ = '\0';
136254721Semaste		p = strchr(val, '\n');
137254721Semaste		if (p == NULL) {
138254721Semaste			error = EINVAL;
139254721Semaste			break;
140254721Semaste		}
141254721Semaste		*p++ = '\0';
142254721Semaste
143254721Semaste		if (strcmp(tag, "KERNEL") == 0)
144254721Semaste			error = setpath(&inst_kernel, val);
145254721Semaste		else if (strcmp(tag, "MODULES") == 0)
146254721Semaste			error = setmultipath(&inst_modules, val);
147254721Semaste		else if (strcmp(tag, "ROOTFS") == 0)
148254721Semaste			error = setpath(&inst_rootfs, val);
149254721Semaste
150254721Semaste		tag = p;
151254721Semaste	}
152254721Semaste
153254721Semaste	return (error);
154254721Semaste}
155254721Semaste
156254721Semastestatic void
157254721Semastecleanup(void)
158254721Semaste{
159254721Semaste	u_int i;
160254721Semaste
161254721Semaste	if (inst_kernel != NULL) {
162254721Semaste		free(inst_kernel);
163254721Semaste		inst_kernel = NULL;
164254721Semaste	}
165254721Semaste	if (inst_modules != NULL) {
166254721Semaste		i = 0;
167254721Semaste		while (inst_modules[i] != NULL)
168254721Semaste			free(inst_modules[i++]);
169254721Semaste		free(inst_modules);
170254721Semaste		inst_modules = NULL;
171254721Semaste	}
172254721Semaste	if (inst_rootfs != NULL) {
173254721Semaste		free(inst_rootfs);
174254721Semaste		inst_rootfs = NULL;
175254721Semaste	}
176254721Semaste	pkgfs_cleanup();
177254721Semaste}
178254721Semaste
179254721Semaste/*
180254721Semaste * usage: install URL
181254721Semaste * where: URL = (tftp|file)://[host]/<package>
182254721Semaste */
183254721Semastestatic int
184254721Semasteinstall(char *pkgname)
185254721Semaste{
186254721Semaste	static char buf[256];
187254721Semaste	struct fs_ops *proto;
188254721Semaste	struct preloaded_file *fp;
189254721Semaste	char *s, *currdev;
190254721Semaste	const char *devname;
191254721Semaste	int error, fd, i, local;
192254721Semaste
193254721Semaste	s = strstr(pkgname, "://");
194254721Semaste	if (s == NULL)
195254721Semaste		goto invalid_url;
196254721Semaste
197254721Semaste	i = s - pkgname;
198254721Semaste	if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
199254721Semaste		devname = "net0";
200254721Semaste		proto = &tftp_fsops;
201254721Semaste		local = 0;
202254721Semaste	} else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
203254721Semaste		currdev = getenv("currdev");
204254721Semaste		if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) {
205254721Semaste			devname = "pxe0";
206254721Semaste			proto = NULL;
207254721Semaste		} else {
208254721Semaste			devname = "disk1";
209254721Semaste			proto = &dosfs_fsops;
210254721Semaste		}
211254721Semaste		local = 1;
212254721Semaste	} else
213254721Semaste		goto invalid_url;
214254721Semaste
215254721Semaste	s += 3;
216254721Semaste	if (*s == '\0')
217254721Semaste		goto invalid_url;
218254721Semaste
219254721Semaste	if (*s != '/' ) {
220254721Semaste		if (local)
221254721Semaste			goto invalid_url;
222254721Semaste
223254721Semaste		pkgname = strchr(s, '/');
224254721Semaste		if (pkgname == NULL)
225254721Semaste			goto invalid_url;
226254721Semaste
227254721Semaste		*pkgname = '\0';
228254721Semaste		servip.s_addr = inet_addr(s);
229254721Semaste		if (servip.s_addr == htonl(INADDR_NONE))
230254721Semaste			goto invalid_url;
231254721Semaste
232254721Semaste		setenv("serverip", inet_ntoa(servip), 1);
233254721Semaste
234254721Semaste		*pkgname = '/';
235254721Semaste	} else
236254721Semaste		pkgname = s;
237254721Semaste
238254721Semaste	if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) {
239254721Semaste		command_errmsg = "package name too long";
240254721Semaste		return (CMD_ERROR);
241254721Semaste	}
242254721Semaste	sprintf(buf, "%s:%s", devname, pkgname);
243254721Semaste	setenv("install_package", buf, 1);
244254721Semaste
245254721Semaste	error = pkgfs_init(buf, proto);
246254721Semaste	if (error) {
247254721Semaste		command_errmsg = "cannot open package";
248254721Semaste		goto fail;
249254721Semaste	}
250254721Semaste
251254721Semaste	/*
252254721Semaste	 * Point of no return: unload anything that may have been
253254721Semaste	 * loaded and prune the environment from harmful variables.
254254721Semaste	 */
255254721Semaste	unload();
256254721Semaste	unsetenv("vfs.root.mountfrom");
257254721Semaste
258254721Semaste	/*
259254721Semaste	 * read the metatags file.
260254721Semaste	 */
261254721Semaste	fd = open("/metatags", O_RDONLY);
262254721Semaste	if (fd != -1) {
263254721Semaste		error = read_metatags(fd);
264254721Semaste		close(fd);
265254721Semaste		if (error) {
266254721Semaste			command_errmsg = "cannot load metatags";
267254721Semaste			goto fail;
268254721Semaste		}
269254721Semaste	}
270254721Semaste
271254721Semaste	s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
272254721Semaste	error = mod_loadkld(s, 0, NULL);
273254721Semaste	if (error) {
274254721Semaste		command_errmsg = "cannot load kernel from package";
275254721Semaste		goto fail;
276254721Semaste	}
277254721Semaste
278254721Semaste	i = 0;
279254721Semaste	while (inst_modules != NULL && inst_modules[i] != NULL) {
280254721Semaste		error = mod_loadkld(inst_modules[i], 0, NULL);
281254721Semaste		if (error) {
282254721Semaste			command_errmsg = "cannot load module(s) from package";
283254721Semaste			goto fail;
284254721Semaste		}
285254721Semaste		i++;
286254721Semaste	}
287254721Semaste
288254721Semaste	s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
289254721Semaste	if (file_loadraw("mfs_root", s) == NULL) {
290254721Semaste		error = errno;
291254721Semaste		command_errmsg = "cannot load root file system";
292254721Semaste		goto fail;
293254721Semaste	}
294254721Semaste
295254721Semaste	cleanup();
296254721Semaste
297254721Semaste	fp = file_findfile(NULL, NULL);
298254721Semaste	if (fp != NULL)
299254721Semaste		file_formats[fp->f_loader]->l_exec(fp);
300254721Semaste	error = CMD_ERROR;
301254721Semaste	command_errmsg = "unable to start installation";
302254721Semaste
303254721Semaste fail:
304254721Semaste	sprintf(buf, "%s (error %d)", command_errmsg, error);
305254721Semaste	cleanup();
306254721Semaste	unload();
307254721Semaste	exclusive_file_system = NULL;
308254721Semaste	command_errmsg = buf;	/* buf is static. */
309254721Semaste	return (CMD_ERROR);
310254721Semaste
311254721Semaste invalid_url:
312254721Semaste	command_errmsg = "invalid URL";
313254721Semaste	return (CMD_ERROR);
314254721Semaste}
315254721Semaste
316254721Semastestatic int
317254721Semastecommand_install(int argc, char *argv[])
318254721Semaste{
319254721Semaste	int argidx;
320254721Semaste
321254721Semaste	unsetenv("install_format");
322254721Semaste
323254721Semaste	argidx = 1;
324254721Semaste	while (1) {
325254721Semaste		if (argc == argidx) {
326254721Semaste			command_errmsg =
327254721Semaste			    "usage: install [--format] <URL>";
328254721Semaste			return (CMD_ERROR);
329254721Semaste		}
330254721Semaste		if (!strcmp(argv[argidx], "--format")) {
331254721Semaste			setenv("install_format", "yes", 1);
332254721Semaste			argidx++;
333254721Semaste			continue;
334254721Semaste		}
335254721Semaste		break;
336254721Semaste	}
337254721Semaste
338254721Semaste	return (install(argv[argidx]));
339254721Semaste}
340254721Semaste