xref: /freebsd/stand/common/install.c (revision ca987d4641cdcd7f27e153db17c5bf064934faf5)
1*ca987d46SWarner Losh /*-
2*ca987d46SWarner Losh  * Copyright (c) 2008-2014, Juniper Networks, Inc.
3*ca987d46SWarner Losh  * All rights reserved.
4*ca987d46SWarner Losh  *
5*ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
6*ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
7*ca987d46SWarner Losh  * are met:
8*ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
9*ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
10*ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
11*ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
12*ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
13*ca987d46SWarner Losh  *
14*ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15*ca987d46SWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16*ca987d46SWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17*ca987d46SWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18*ca987d46SWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19*ca987d46SWarner Losh  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20*ca987d46SWarner Losh  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21*ca987d46SWarner Losh  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22*ca987d46SWarner Losh  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*ca987d46SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*ca987d46SWarner Losh  * SUCH DAMAGE.
25*ca987d46SWarner Losh  */
26*ca987d46SWarner Losh 
27*ca987d46SWarner Losh #include <sys/cdefs.h>
28*ca987d46SWarner Losh __FBSDID("$FreeBSD$");
29*ca987d46SWarner Losh 
30*ca987d46SWarner Losh #include <sys/param.h>
31*ca987d46SWarner Losh #include <sys/socket.h>
32*ca987d46SWarner Losh #include <net/if.h>
33*ca987d46SWarner Losh #include <netinet/in.h>
34*ca987d46SWarner Losh #include <netinet/in_systm.h>
35*ca987d46SWarner Losh 
36*ca987d46SWarner Losh #include <stand.h>
37*ca987d46SWarner Losh #include <net.h>
38*ca987d46SWarner Losh #include <string.h>
39*ca987d46SWarner Losh 
40*ca987d46SWarner Losh #include "bootstrap.h"
41*ca987d46SWarner Losh 
42*ca987d46SWarner Losh extern struct in_addr servip;
43*ca987d46SWarner Losh 
44*ca987d46SWarner Losh extern int pkgfs_init(const char *, struct fs_ops *);
45*ca987d46SWarner Losh extern void pkgfs_cleanup(void);
46*ca987d46SWarner Losh 
47*ca987d46SWarner Losh COMMAND_SET(install, "install", "install software package", command_install);
48*ca987d46SWarner Losh 
49*ca987d46SWarner Losh static char *inst_kernel;
50*ca987d46SWarner Losh static char **inst_modules;
51*ca987d46SWarner Losh static char *inst_rootfs;
52*ca987d46SWarner Losh static char *inst_loader_rc;
53*ca987d46SWarner Losh 
54*ca987d46SWarner Losh static int
55*ca987d46SWarner Losh setpath(char **what, char *val)
56*ca987d46SWarner Losh {
57*ca987d46SWarner Losh 	char *path;
58*ca987d46SWarner Losh 	size_t len;
59*ca987d46SWarner Losh 	int rel;
60*ca987d46SWarner Losh 
61*ca987d46SWarner Losh 	len = strlen(val) + 1;
62*ca987d46SWarner Losh 	rel = (val[0] != '/') ? 1 : 0;
63*ca987d46SWarner Losh 	path = malloc(len + rel);
64*ca987d46SWarner Losh 	if (path == NULL)
65*ca987d46SWarner Losh 		return (ENOMEM);
66*ca987d46SWarner Losh 	path[0] = '/';
67*ca987d46SWarner Losh 	strcpy(path + rel, val);
68*ca987d46SWarner Losh 
69*ca987d46SWarner Losh 	*what = path;
70*ca987d46SWarner Losh 	return (0);
71*ca987d46SWarner Losh }
72*ca987d46SWarner Losh 
73*ca987d46SWarner Losh static int
74*ca987d46SWarner Losh setmultipath(char ***what, char *val)
75*ca987d46SWarner Losh {
76*ca987d46SWarner Losh 	char *s, *v;
77*ca987d46SWarner Losh 	int count, error, idx;
78*ca987d46SWarner Losh 
79*ca987d46SWarner Losh 	count = 0;
80*ca987d46SWarner Losh 	v = val;
81*ca987d46SWarner Losh 	do {
82*ca987d46SWarner Losh 		count++;
83*ca987d46SWarner Losh 		s = strchr(v, ',');
84*ca987d46SWarner Losh 		v = (s == NULL) ? NULL : s + 1;
85*ca987d46SWarner Losh 	} while (v != NULL);
86*ca987d46SWarner Losh 
87*ca987d46SWarner Losh 	*what = calloc(count + 1, sizeof(char *));
88*ca987d46SWarner Losh 	if (*what == NULL)
89*ca987d46SWarner Losh 		return (ENOMEM);
90*ca987d46SWarner Losh 
91*ca987d46SWarner Losh 	for (idx = 0; idx < count; idx++) {
92*ca987d46SWarner Losh 		s = strchr(val, ',');
93*ca987d46SWarner Losh 		if (s != NULL)
94*ca987d46SWarner Losh 			*s++ = '\0';
95*ca987d46SWarner Losh 		error = setpath(*what + idx, val);
96*ca987d46SWarner Losh 		if (error)
97*ca987d46SWarner Losh 			return (error);
98*ca987d46SWarner Losh 		val = s;
99*ca987d46SWarner Losh 	}
100*ca987d46SWarner Losh 
101*ca987d46SWarner Losh 	return (0);
102*ca987d46SWarner Losh }
103*ca987d46SWarner Losh 
104*ca987d46SWarner Losh static int
105*ca987d46SWarner Losh read_metatags(int fd)
106*ca987d46SWarner Losh {
107*ca987d46SWarner Losh 	char buf[1024];
108*ca987d46SWarner Losh 	char *p, *tag, *val;
109*ca987d46SWarner Losh 	ssize_t fsize;
110*ca987d46SWarner Losh 	int error;
111*ca987d46SWarner Losh 
112*ca987d46SWarner Losh 	fsize = read(fd, buf, sizeof(buf));
113*ca987d46SWarner Losh 	if (fsize == -1)
114*ca987d46SWarner Losh 		return (errno);
115*ca987d46SWarner Losh 
116*ca987d46SWarner Losh 	/*
117*ca987d46SWarner Losh 	 * Assume that if we read a whole buffer worth of data, we
118*ca987d46SWarner Losh 	 * haven't read the entire file. In other words, the buffer
119*ca987d46SWarner Losh 	 * size must always be larger than the file size. That way
120*ca987d46SWarner Losh 	 * we can append a '\0' and use standard string operations.
121*ca987d46SWarner Losh 	 * Return an error if this is not possible.
122*ca987d46SWarner Losh 	 */
123*ca987d46SWarner Losh 	if (fsize == sizeof(buf))
124*ca987d46SWarner Losh 		return (ENOMEM);
125*ca987d46SWarner Losh 
126*ca987d46SWarner Losh 	buf[fsize] = '\0';
127*ca987d46SWarner Losh 	error = 0;
128*ca987d46SWarner Losh 	tag = buf;
129*ca987d46SWarner Losh 	while (!error && *tag != '\0') {
130*ca987d46SWarner Losh 		val = strchr(tag, '=');
131*ca987d46SWarner Losh 		if (val == NULL) {
132*ca987d46SWarner Losh 			error = EINVAL;
133*ca987d46SWarner Losh 			break;
134*ca987d46SWarner Losh 		}
135*ca987d46SWarner Losh 		*val++ = '\0';
136*ca987d46SWarner Losh 		p = strchr(val, '\n');
137*ca987d46SWarner Losh 		if (p == NULL) {
138*ca987d46SWarner Losh 			error = EINVAL;
139*ca987d46SWarner Losh 			break;
140*ca987d46SWarner Losh 		}
141*ca987d46SWarner Losh 		*p++ = '\0';
142*ca987d46SWarner Losh 
143*ca987d46SWarner Losh 		if (strcmp(tag, "KERNEL") == 0)
144*ca987d46SWarner Losh 			error = setpath(&inst_kernel, val);
145*ca987d46SWarner Losh 		else if (strcmp(tag, "MODULES") == 0)
146*ca987d46SWarner Losh 			error = setmultipath(&inst_modules, val);
147*ca987d46SWarner Losh 		else if (strcmp(tag, "ROOTFS") == 0)
148*ca987d46SWarner Losh 			error = setpath(&inst_rootfs, val);
149*ca987d46SWarner Losh 		else if (strcmp(tag, "LOADER_RC") == 0)
150*ca987d46SWarner Losh 			error = setpath(&inst_loader_rc, val);
151*ca987d46SWarner Losh 
152*ca987d46SWarner Losh 		tag = p;
153*ca987d46SWarner Losh 	}
154*ca987d46SWarner Losh 
155*ca987d46SWarner Losh 	return (error);
156*ca987d46SWarner Losh }
157*ca987d46SWarner Losh 
158*ca987d46SWarner Losh static void
159*ca987d46SWarner Losh cleanup(void)
160*ca987d46SWarner Losh {
161*ca987d46SWarner Losh 	u_int i;
162*ca987d46SWarner Losh 
163*ca987d46SWarner Losh 	if (inst_kernel != NULL) {
164*ca987d46SWarner Losh 		free(inst_kernel);
165*ca987d46SWarner Losh 		inst_kernel = NULL;
166*ca987d46SWarner Losh 	}
167*ca987d46SWarner Losh 	if (inst_modules != NULL) {
168*ca987d46SWarner Losh 		i = 0;
169*ca987d46SWarner Losh 		while (inst_modules[i] != NULL)
170*ca987d46SWarner Losh 			free(inst_modules[i++]);
171*ca987d46SWarner Losh 		free(inst_modules);
172*ca987d46SWarner Losh 		inst_modules = NULL;
173*ca987d46SWarner Losh 	}
174*ca987d46SWarner Losh 	if (inst_rootfs != NULL) {
175*ca987d46SWarner Losh 		free(inst_rootfs);
176*ca987d46SWarner Losh 		inst_rootfs = NULL;
177*ca987d46SWarner Losh 	}
178*ca987d46SWarner Losh 	if (inst_loader_rc != NULL) {
179*ca987d46SWarner Losh 		free(inst_loader_rc);
180*ca987d46SWarner Losh 		inst_loader_rc = NULL;
181*ca987d46SWarner Losh 	}
182*ca987d46SWarner Losh 	pkgfs_cleanup();
183*ca987d46SWarner Losh }
184*ca987d46SWarner Losh 
185*ca987d46SWarner Losh /*
186*ca987d46SWarner Losh  * usage: install URL
187*ca987d46SWarner Losh  * where: URL = (tftp|file)://[host]/<package>
188*ca987d46SWarner Losh  */
189*ca987d46SWarner Losh static int
190*ca987d46SWarner Losh install(char *pkgname)
191*ca987d46SWarner Losh {
192*ca987d46SWarner Losh 	static char buf[256];
193*ca987d46SWarner Losh 	struct fs_ops *proto;
194*ca987d46SWarner Losh 	struct preloaded_file *fp;
195*ca987d46SWarner Losh 	char *s, *currdev;
196*ca987d46SWarner Losh 	const char *devname;
197*ca987d46SWarner Losh 	int error, fd, i, local;
198*ca987d46SWarner Losh 
199*ca987d46SWarner Losh 	s = strstr(pkgname, "://");
200*ca987d46SWarner Losh 	if (s == NULL)
201*ca987d46SWarner Losh 		goto invalid_url;
202*ca987d46SWarner Losh 
203*ca987d46SWarner Losh 	i = s - pkgname;
204*ca987d46SWarner Losh 	if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
205*ca987d46SWarner Losh 		devname = "net0";
206*ca987d46SWarner Losh 		proto = &tftp_fsops;
207*ca987d46SWarner Losh 		local = 0;
208*ca987d46SWarner Losh 	} else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
209*ca987d46SWarner Losh 		currdev = getenv("currdev");
210*ca987d46SWarner Losh 		if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) {
211*ca987d46SWarner Losh 			devname = "pxe0";
212*ca987d46SWarner Losh 			proto = NULL;
213*ca987d46SWarner Losh 		} else {
214*ca987d46SWarner Losh 			devname = "disk1";
215*ca987d46SWarner Losh 			proto = &dosfs_fsops;
216*ca987d46SWarner Losh 		}
217*ca987d46SWarner Losh 		local = 1;
218*ca987d46SWarner Losh 	} else
219*ca987d46SWarner Losh 		goto invalid_url;
220*ca987d46SWarner Losh 
221*ca987d46SWarner Losh 	s += 3;
222*ca987d46SWarner Losh 	if (*s == '\0')
223*ca987d46SWarner Losh 		goto invalid_url;
224*ca987d46SWarner Losh 
225*ca987d46SWarner Losh 	if (*s != '/' ) {
226*ca987d46SWarner Losh 		if (local)
227*ca987d46SWarner Losh 			goto invalid_url;
228*ca987d46SWarner Losh 
229*ca987d46SWarner Losh 		pkgname = strchr(s, '/');
230*ca987d46SWarner Losh 		if (pkgname == NULL)
231*ca987d46SWarner Losh 			goto invalid_url;
232*ca987d46SWarner Losh 
233*ca987d46SWarner Losh 		*pkgname = '\0';
234*ca987d46SWarner Losh 		servip.s_addr = inet_addr(s);
235*ca987d46SWarner Losh 		if (servip.s_addr == htonl(INADDR_NONE))
236*ca987d46SWarner Losh 			goto invalid_url;
237*ca987d46SWarner Losh 
238*ca987d46SWarner Losh 		setenv("serverip", inet_ntoa(servip), 1);
239*ca987d46SWarner Losh 
240*ca987d46SWarner Losh 		*pkgname = '/';
241*ca987d46SWarner Losh 	} else
242*ca987d46SWarner Losh 		pkgname = s;
243*ca987d46SWarner Losh 
244*ca987d46SWarner Losh 	if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) {
245*ca987d46SWarner Losh 		command_errmsg = "package name too long";
246*ca987d46SWarner Losh 		return (CMD_ERROR);
247*ca987d46SWarner Losh 	}
248*ca987d46SWarner Losh 	sprintf(buf, "%s:%s", devname, pkgname);
249*ca987d46SWarner Losh 	setenv("install_package", buf, 1);
250*ca987d46SWarner Losh 
251*ca987d46SWarner Losh 	error = pkgfs_init(buf, proto);
252*ca987d46SWarner Losh 	if (error) {
253*ca987d46SWarner Losh 		command_errmsg = "cannot open package";
254*ca987d46SWarner Losh 		goto fail;
255*ca987d46SWarner Losh 	}
256*ca987d46SWarner Losh 
257*ca987d46SWarner Losh 	/*
258*ca987d46SWarner Losh 	 * Point of no return: unload anything that may have been
259*ca987d46SWarner Losh 	 * loaded and prune the environment from harmful variables.
260*ca987d46SWarner Losh 	 */
261*ca987d46SWarner Losh 	unload();
262*ca987d46SWarner Losh 	unsetenv("vfs.root.mountfrom");
263*ca987d46SWarner Losh 
264*ca987d46SWarner Losh 	/*
265*ca987d46SWarner Losh 	 * read the metatags file.
266*ca987d46SWarner Losh 	 */
267*ca987d46SWarner Losh 	fd = open("/metatags", O_RDONLY);
268*ca987d46SWarner Losh 	if (fd != -1) {
269*ca987d46SWarner Losh 		error = read_metatags(fd);
270*ca987d46SWarner Losh 		close(fd);
271*ca987d46SWarner Losh 		if (error) {
272*ca987d46SWarner Losh 			command_errmsg = "cannot load metatags";
273*ca987d46SWarner Losh 			goto fail;
274*ca987d46SWarner Losh 		}
275*ca987d46SWarner Losh 	}
276*ca987d46SWarner Losh 
277*ca987d46SWarner Losh 	s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
278*ca987d46SWarner Losh 	error = mod_loadkld(s, 0, NULL);
279*ca987d46SWarner Losh 	if (error) {
280*ca987d46SWarner Losh 		command_errmsg = "cannot load kernel from package";
281*ca987d46SWarner Losh 		goto fail;
282*ca987d46SWarner Losh 	}
283*ca987d46SWarner Losh 
284*ca987d46SWarner Losh 	/* If there is a loader.rc in the package, execute it */
285*ca987d46SWarner Losh 	s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
286*ca987d46SWarner Losh 	fd = open(s, O_RDONLY);
287*ca987d46SWarner Losh 	if (fd != -1) {
288*ca987d46SWarner Losh 		close(fd);
289*ca987d46SWarner Losh 		error = include(s);
290*ca987d46SWarner Losh 		if (error == CMD_ERROR)
291*ca987d46SWarner Losh 			goto fail;
292*ca987d46SWarner Losh 	}
293*ca987d46SWarner Losh 
294*ca987d46SWarner Losh 	i = 0;
295*ca987d46SWarner Losh 	while (inst_modules != NULL && inst_modules[i] != NULL) {
296*ca987d46SWarner Losh 		error = mod_loadkld(inst_modules[i], 0, NULL);
297*ca987d46SWarner Losh 		if (error) {
298*ca987d46SWarner Losh 			command_errmsg = "cannot load module(s) from package";
299*ca987d46SWarner Losh 			goto fail;
300*ca987d46SWarner Losh 		}
301*ca987d46SWarner Losh 		i++;
302*ca987d46SWarner Losh 	}
303*ca987d46SWarner Losh 
304*ca987d46SWarner Losh 	s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
305*ca987d46SWarner Losh 	if (file_loadraw(s, "mfs_root", 1) == NULL) {
306*ca987d46SWarner Losh 		error = errno;
307*ca987d46SWarner Losh 		command_errmsg = "cannot load root file system";
308*ca987d46SWarner Losh 		goto fail;
309*ca987d46SWarner Losh 	}
310*ca987d46SWarner Losh 
311*ca987d46SWarner Losh 	cleanup();
312*ca987d46SWarner Losh 
313*ca987d46SWarner Losh 	fp = file_findfile(NULL, NULL);
314*ca987d46SWarner Losh 	if (fp != NULL)
315*ca987d46SWarner Losh 		file_formats[fp->f_loader]->l_exec(fp);
316*ca987d46SWarner Losh 	error = CMD_ERROR;
317*ca987d46SWarner Losh 	command_errmsg = "unable to start installation";
318*ca987d46SWarner Losh 
319*ca987d46SWarner Losh  fail:
320*ca987d46SWarner Losh 	sprintf(buf, "%s (error %d)", command_errmsg, error);
321*ca987d46SWarner Losh 	cleanup();
322*ca987d46SWarner Losh 	unload();
323*ca987d46SWarner Losh 	exclusive_file_system = NULL;
324*ca987d46SWarner Losh 	command_errmsg = buf;	/* buf is static. */
325*ca987d46SWarner Losh 	return (CMD_ERROR);
326*ca987d46SWarner Losh 
327*ca987d46SWarner Losh  invalid_url:
328*ca987d46SWarner Losh 	command_errmsg = "invalid URL";
329*ca987d46SWarner Losh 	return (CMD_ERROR);
330*ca987d46SWarner Losh }
331*ca987d46SWarner Losh 
332*ca987d46SWarner Losh static int
333*ca987d46SWarner Losh command_install(int argc, char *argv[])
334*ca987d46SWarner Losh {
335*ca987d46SWarner Losh 	int argidx;
336*ca987d46SWarner Losh 
337*ca987d46SWarner Losh 	unsetenv("install_format");
338*ca987d46SWarner Losh 
339*ca987d46SWarner Losh 	argidx = 1;
340*ca987d46SWarner Losh 	while (1) {
341*ca987d46SWarner Losh 		if (argc == argidx) {
342*ca987d46SWarner Losh 			command_errmsg =
343*ca987d46SWarner Losh 			    "usage: install [--format] <URL>";
344*ca987d46SWarner Losh 			return (CMD_ERROR);
345*ca987d46SWarner Losh 		}
346*ca987d46SWarner Losh 		if (!strcmp(argv[argidx], "--format")) {
347*ca987d46SWarner Losh 			setenv("install_format", "yes", 1);
348*ca987d46SWarner Losh 			argidx++;
349*ca987d46SWarner Losh 			continue;
350*ca987d46SWarner Losh 		}
351*ca987d46SWarner Losh 		break;
352*ca987d46SWarner Losh 	}
353*ca987d46SWarner Losh 
354*ca987d46SWarner Losh 	return (install(argv[argidx]));
355*ca987d46SWarner Losh }
356