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