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 proto = &tftp_fsops;
216 } else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
217 currdev = getenv("currdev");
218 local = 1;
219
220 if (*s == '/') { /* file:/// */
221 if (devname == NULL)
222 devname = currdev;
223 if (devname == NULL)
224 devname = "disk1";
225 } else { /* file://devname[:fstype]/ */
226 devname = s;
227 e = strchr(devname, '/');
228 if (!e)
229 goto invalid_url;
230 devnamelen = e - devname;
231 s = e; /* consume devname */
232 }
233 if ((e = strchr(devname, ':')) != NULL) {
234 /* could be :fstype */
235 devnamelen = e - devname;
236 switch (e[1]) {
237 case '\0': /* just currdev */
238 break;
239 case 'd':
240 proto = &dosfs_fsops;
241 break;
242 #ifdef HOSTPROG
243 case 'h':
244 {
245 extern struct fs_ops host_fsops;
246
247 proto = &host_fsops;
248 }
249 break;
250 #endif
251 case 'u':
252 proto = &ufs_fsops;
253 break;
254 }
255 }
256 if (proto == NULL && strncmp(devname, "disk", 4) == 0) {
257 proto = &dosfs_fsops;
258 }
259 }
260
261 if (devname == NULL)
262 goto invalid_url;
263
264 if (devnamelen == 0) {
265 /* default is currdev which ends with ':' */
266 devnamelen = strlen(devname);
267 if (devname[devnamelen - 1] == ':')
268 devnamelen--;
269 }
270
271 if (*s != '/' ) {
272 if (local)
273 goto invalid_url;
274
275 pkgname = strchr(s, '/');
276 if (pkgname == NULL)
277 goto invalid_url;
278
279 *pkgname = '\0';
280 servip.s_addr = inet_addr(s);
281 if (servip.s_addr == htonl(INADDR_NONE))
282 goto invalid_url;
283
284 setenv("serverip", inet_ntoa(servip), 1);
285
286 *pkgname = '/';
287 } else
288 pkgname = s;
289
290 i = snprintf(buf, sizeof(buf), "%.*s:%s",
291 (int) devnamelen, devname, pkgname);
292 if (i >= (int) sizeof(buf)) {
293 command_errmsg = "package name too long";
294 return (CMD_ERROR);
295 }
296 setenv("install_package", buf, 1);
297
298 error = pkgfs_init(buf, proto);
299 if (error) {
300 command_errmsg = "cannot open package";
301 goto fail;
302 }
303
304 /*
305 * Point of no return: unload anything that may have been
306 * loaded and prune the environment from harmful variables.
307 */
308 unload();
309 unsetenv("vfs.root.mountfrom");
310
311 /*
312 * read the metatags file.
313 */
314 fd = open("/metatags", O_RDONLY);
315 if (fd != -1) {
316 error = read_metatags(fd);
317 close(fd);
318 if (error) {
319 command_errmsg = "cannot load metatags";
320 goto fail;
321 }
322 }
323
324 s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
325 error = mod_loadkld(s, 0, NULL);
326 if (error) {
327 command_errmsg = "cannot load kernel from package";
328 goto fail;
329 }
330
331 /* If there is a loader.rc in the package, execute it */
332 s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
333 fd = open(s, O_RDONLY);
334 if (fd != -1) {
335 close(fd);
336 error = interp_include(s);
337 if (error == CMD_ERROR)
338 goto fail;
339 }
340
341 i = 0;
342 while (inst_modules != NULL && inst_modules[i] != NULL) {
343 error = mod_loadkld(inst_modules[i], 0, NULL);
344 if (error) {
345 command_errmsg = "cannot load module(s) from package";
346 goto fail;
347 }
348 i++;
349 }
350
351 s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
352 if (file_loadraw(s, "mfs_root", 1) == NULL) {
353 error = errno;
354 command_errmsg = "cannot load root file system";
355 goto fail;
356 }
357
358 cleanup();
359
360 fp = file_findfile(NULL, NULL);
361 if (fp != NULL)
362 file_formats[fp->f_loader]->l_exec(fp);
363 error = CMD_ERROR;
364 command_errmsg = "unable to start installation";
365
366 fail:
367 sprintf(buf, "%s (error %d)", command_errmsg, error);
368 cleanup();
369 unload();
370 exclusive_file_system = NULL;
371 command_errmsg = buf; /* buf is static. */
372 return (CMD_ERROR);
373
374 invalid_url:
375 command_errmsg = "invalid URL";
376 return (CMD_ERROR);
377 }
378
379 static int
command_install(int argc,char * argv[])380 command_install(int argc, char *argv[])
381 {
382 int argidx;
383
384 unsetenv("install_format");
385
386 argidx = 1;
387 while (1) {
388 if (argc == argidx) {
389 command_errmsg =
390 "usage: install [--format] <URL>";
391 return (CMD_ERROR);
392 }
393 if (!strcmp(argv[argidx], "--format")) {
394 setenv("install_format", "yes", 1);
395 argidx++;
396 continue;
397 }
398 break;
399 }
400
401 return (install(argv[argidx]));
402 }
403