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 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 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 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 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 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 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