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