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 (strcmp(tag, "KERNEL") == 0)
141 error = setpath(&inst_kernel, val);
142 else if (strcmp(tag, "MODULES") == 0)
143 error = setmultipath(&inst_modules, val);
144 else if (strcmp(tag, "ROOTFS") == 0)
145 error = setpath(&inst_rootfs, val);
146 else if (strcmp(tag, "LOADER_RC") == 0)
147 error = setpath(&inst_loader_rc, val);
148
149 tag = p;
150 }
151
152 return (error);
153 }
154
155 static void
cleanup(void)156 cleanup(void)
157 {
158 u_int i;
159
160 if (inst_kernel != NULL) {
161 free(inst_kernel);
162 inst_kernel = NULL;
163 }
164 if (inst_modules != NULL) {
165 i = 0;
166 while (inst_modules[i] != NULL)
167 free(inst_modules[i++]);
168 free(inst_modules);
169 inst_modules = NULL;
170 }
171 if (inst_rootfs != NULL) {
172 free(inst_rootfs);
173 inst_rootfs = NULL;
174 }
175 if (inst_loader_rc != NULL) {
176 free(inst_loader_rc);
177 inst_loader_rc = NULL;
178 }
179 pkgfs_cleanup();
180 }
181
182 /*
183 * usage: install URL
184 * where: URL = tftp://[host]/<package>
185 * or file://[devname[:fstype]]/<package>
186 */
187 static int
install(char * pkgname)188 install(char *pkgname)
189 {
190 static char buf[256];
191 struct fs_ops *proto;
192 struct preloaded_file *fp;
193 char *e, *s, *currdev;
194 char *devname;
195 size_t devnamelen;
196 int error, fd, i, local;
197
198 s = strstr(pkgname, "://");
199 if (s == NULL)
200 goto invalid_url;
201
202 i = s - pkgname;
203 s += 3;
204 if (*s == '\0')
205 goto invalid_url;
206
207 devname = NULL;
208 devnamelen = 0;
209 proto = NULL;
210 local = 0;
211
212 if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
213 devname = "net0";
214 devnamelen = 4;
215 netproto = NET_TFTP;
216 proto = &tftp_fsops;
217 } else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
218 currdev = getenv("currdev");
219 local = 1;
220
221 if (*s == '/') { /* file:/// */
222 if (devname == NULL)
223 devname = currdev;
224 if (devname == NULL)
225 devname = "disk1";
226 } else { /* file://devname[:fstype]/ */
227 devname = s;
228 e = strchr(devname, '/');
229 if (!e)
230 goto invalid_url;
231 devnamelen = e - devname;
232 s = e; /* consume devname */
233 }
234 if ((e = strchr(devname, ':')) != NULL) {
235 /* could be :fstype */
236 devnamelen = e - devname;
237 switch (e[1]) {
238 case '\0': /* just currdev */
239 break;
240 case 'd':
241 proto = &dosfs_fsops;
242 break;
243 #ifdef HOSTPROG
244 case 'h':
245 {
246 extern struct fs_ops host_fsops;
247
248 proto = &host_fsops;
249 }
250 break;
251 #endif
252 case 'u':
253 proto = &ufs_fsops;
254 break;
255 }
256 }
257 if (proto == NULL && strncmp(devname, "disk", 4) == 0) {
258 proto = &dosfs_fsops;
259 }
260 }
261
262 if (devname == NULL)
263 goto invalid_url;
264
265 if (devnamelen == 0) {
266 /* default is currdev which ends with ':' */
267 devnamelen = strlen(devname);
268 if (devname[devnamelen - 1] == ':')
269 devnamelen--;
270 }
271
272 if (*s != '/' ) {
273 if (local)
274 goto invalid_url;
275
276 pkgname = strchr(s, '/');
277 if (pkgname == NULL)
278 goto invalid_url;
279
280 *pkgname = '\0';
281 servip.s_addr = inet_addr(s);
282 if (servip.s_addr == htonl(INADDR_NONE))
283 goto invalid_url;
284
285 setenv("serverip", inet_ntoa(servip), 1);
286
287 *pkgname = '/';
288 } else
289 pkgname = s;
290
291 i = snprintf(buf, sizeof(buf), "%.*s:%s",
292 (int) devnamelen, devname, pkgname);
293 if (i >= (int) sizeof(buf)) {
294 command_errmsg = "package name too long";
295 return (CMD_ERROR);
296 }
297 setenv("install_package", buf, 1);
298
299 error = pkgfs_init(buf, proto);
300 if (error) {
301 command_errmsg = "cannot open package";
302 goto fail;
303 }
304
305 /*
306 * Point of no return: unload anything that may have been
307 * loaded and prune the environment from harmful variables.
308 */
309 unload();
310 unsetenv("vfs.root.mountfrom");
311
312 /*
313 * read the metatags file.
314 */
315 fd = open("/metatags", O_RDONLY);
316 if (fd != -1) {
317 error = read_metatags(fd);
318 close(fd);
319 if (error) {
320 command_errmsg = "cannot load metatags";
321 goto fail;
322 }
323 }
324
325 s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
326 error = mod_loadkld(s, 0, NULL);
327 if (error) {
328 command_errmsg = "cannot load kernel from package";
329 goto fail;
330 }
331
332 /* If there is a loader.rc in the package, execute it */
333 s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
334 fd = open(s, O_RDONLY);
335 if (fd != -1) {
336 close(fd);
337 error = interp_include(s);
338 if (error == CMD_ERROR)
339 goto fail;
340 }
341
342 i = 0;
343 while (inst_modules != NULL && inst_modules[i] != NULL) {
344 error = mod_loadkld(inst_modules[i], 0, NULL);
345 if (error) {
346 command_errmsg = "cannot load module(s) from package";
347 goto fail;
348 }
349 i++;
350 }
351
352 s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
353 if (file_loadraw(s, "mfs_root", 1) == NULL) {
354 error = errno;
355 command_errmsg = "cannot load root file system";
356 goto fail;
357 }
358
359 cleanup();
360
361 fp = file_findfile(NULL, NULL);
362 if (fp != NULL)
363 file_formats[fp->f_loader]->l_exec(fp);
364 error = CMD_ERROR;
365 command_errmsg = "unable to start installation";
366
367 fail:
368 sprintf(buf, "%s (error %d)", command_errmsg, error);
369 cleanup();
370 unload();
371 exclusive_file_system = NULL;
372 command_errmsg = buf; /* buf is static. */
373 return (CMD_ERROR);
374
375 invalid_url:
376 command_errmsg = "invalid URL";
377 return (CMD_ERROR);
378 }
379
380 static int
command_install(int argc,char * argv[])381 command_install(int argc, char *argv[])
382 {
383 int argidx;
384
385 unsetenv("install_format");
386
387 argidx = 1;
388 while (1) {
389 if (argc == argidx) {
390 command_errmsg =
391 "usage: install [--format] <URL>";
392 return (CMD_ERROR);
393 }
394 if (!strcmp(argv[argidx], "--format")) {
395 setenv("install_format", "yes", 1);
396 argidx++;
397 continue;
398 }
399 break;
400 }
401
402 return (install(argv[argidx]));
403 }
404