install.c revision 329011
112869Sdfuchs/*- 212869Sdfuchs * Copyright (c) 2008-2014, Juniper Networks, Inc. 312869Sdfuchs * All rights reserved. 412869Sdfuchs * 512869Sdfuchs * Redistribution and use in source and binary forms, with or without 612869Sdfuchs * modification, are permitted provided that the following conditions 712869Sdfuchs * are met: 812869Sdfuchs * 1. Redistributions of source code must retain the above copyright 912869Sdfuchs * notice, this list of conditions and the following disclaimer. 1012869Sdfuchs * 2. Redistributions in binary form must reproduce the above copyright 1112869Sdfuchs * notice, this list of conditions and the following disclaimer in the 1212869Sdfuchs * documentation and/or other materials provided with the distribution. 1312869Sdfuchs * 1412869Sdfuchs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1512869Sdfuchs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1612869Sdfuchs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1712869Sdfuchs * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1812869Sdfuchs * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1912869Sdfuchs * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2012869Sdfuchs * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2112869Sdfuchs * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2212869Sdfuchs * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2312869Sdfuchs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2412869Sdfuchs * SUCH DAMAGE. 2512869Sdfuchs */ 2612869Sdfuchs 2712869Sdfuchs#include <sys/cdefs.h> 2812869Sdfuchs__FBSDID("$FreeBSD: stable/11/sys/boot/common/install.c 329011 2018-02-08 02:50:47Z kevans $"); 2912869Sdfuchs 3012869Sdfuchs#include <sys/param.h> 3112869Sdfuchs#include <sys/socket.h> 3212869Sdfuchs#include <net/if.h> 3312869Sdfuchs#include <netinet/in.h> 3412869Sdfuchs#include <netinet/in_systm.h> 3512869Sdfuchs 3612869Sdfuchs#include <stand.h> 3712869Sdfuchs#include <net.h> 3812869Sdfuchs#include <string.h> 3912869Sdfuchs 4012869Sdfuchs#include "bootstrap.h" 4112869Sdfuchs 4212869Sdfuchsextern struct in_addr servip; 4312869Sdfuchs 4412869Sdfuchsextern int pkgfs_init(const char *, struct fs_ops *); 4512869Sdfuchsextern void pkgfs_cleanup(void); 4612869Sdfuchs 4712869SdfuchsCOMMAND_SET(install, "install", "install software package", command_install); 4812869Sdfuchs 4912869Sdfuchsstatic char *inst_kernel; 5012869Sdfuchsstatic char **inst_modules; 5112869Sdfuchsstatic char *inst_rootfs; 5212869Sdfuchsstatic char *inst_loader_rc; 5312869Sdfuchs 5412869Sdfuchsstatic int 5512869Sdfuchssetpath(char **what, char *val) 5612869Sdfuchs{ 5712869Sdfuchs char *path; 5812869Sdfuchs size_t len; 5912869Sdfuchs int rel; 6012869Sdfuchs 6112869Sdfuchs len = strlen(val) + 1; 6212869Sdfuchs rel = (val[0] != '/') ? 1 : 0; 6312869Sdfuchs path = malloc(len + rel); 6412869Sdfuchs if (path == NULL) 6512869Sdfuchs return (ENOMEM); 6612869Sdfuchs path[0] = '/'; 6712869Sdfuchs strcpy(path + rel, val); 6812869Sdfuchs 6912869Sdfuchs *what = path; 7012869Sdfuchs return (0); 7112869Sdfuchs} 7212869Sdfuchs 7312869Sdfuchsstatic int 7412869Sdfuchssetmultipath(char ***what, char *val) 7512869Sdfuchs{ 7612869Sdfuchs char *s, *v; 7712869Sdfuchs int count, error, idx; 7812869Sdfuchs 7912869Sdfuchs count = 0; 8012869Sdfuchs v = val; 8112869Sdfuchs do { 8212869Sdfuchs count++; 8312869Sdfuchs s = strchr(v, ','); 8412869Sdfuchs v = (s == NULL) ? NULL : s + 1; 8512869Sdfuchs } while (v != NULL); 8612869Sdfuchs 8712869Sdfuchs *what = calloc(count + 1, sizeof(char *)); 8812869Sdfuchs if (*what == NULL) 8912869Sdfuchs return (ENOMEM); 9012869Sdfuchs 9112869Sdfuchs for (idx = 0; idx < count; idx++) { 9212869Sdfuchs s = strchr(val, ','); 9312869Sdfuchs if (s != NULL) 9412869Sdfuchs *s++ = '\0'; 9512869Sdfuchs error = setpath(*what + idx, val); 9612869Sdfuchs if (error) 9712869Sdfuchs return (error); 9812869Sdfuchs val = s; 9912869Sdfuchs } 10012869Sdfuchs 10112869Sdfuchs return (0); 10212869Sdfuchs} 10312869Sdfuchs 10412869Sdfuchsstatic int 10512869Sdfuchsread_metatags(int fd) 10612869Sdfuchs{ 10712869Sdfuchs char buf[1024]; 10812869Sdfuchs char *p, *tag, *val; 10912869Sdfuchs ssize_t fsize; 11012869Sdfuchs int error; 11112869Sdfuchs 11212869Sdfuchs fsize = read(fd, buf, sizeof(buf)); 11312869Sdfuchs if (fsize == -1) 11412869Sdfuchs return (errno); 11512869Sdfuchs 11612869Sdfuchs /* 11712869Sdfuchs * Assume that if we read a whole buffer worth of data, we 11812869Sdfuchs * haven't read the entire file. In other words, the buffer 11912869Sdfuchs * size must always be larger than the file size. That way 12012869Sdfuchs * we can append a '\0' and use standard string operations. 12112869Sdfuchs * Return an error if this is not possible. 12212869Sdfuchs */ 12312869Sdfuchs if (fsize == sizeof(buf)) 12412869Sdfuchs return (ENOMEM); 12512869Sdfuchs 12612869Sdfuchs buf[fsize] = '\0'; 12712869Sdfuchs error = 0; 12812869Sdfuchs tag = buf; 12912869Sdfuchs while (!error && *tag != '\0') { 13012869Sdfuchs val = strchr(tag, '='); 13112869Sdfuchs if (val == NULL) { 13212869Sdfuchs error = EINVAL; 13312869Sdfuchs break; 13412869Sdfuchs } 13512869Sdfuchs *val++ = '\0'; 13612869Sdfuchs p = strchr(val, '\n'); 13712869Sdfuchs if (p == NULL) { 13812869Sdfuchs error = EINVAL; 13912869Sdfuchs break; 14012869Sdfuchs } 14112869Sdfuchs *p++ = '\0'; 14212869Sdfuchs 14312869Sdfuchs if (strcmp(tag, "KERNEL") == 0) 14412869Sdfuchs error = setpath(&inst_kernel, val); 14512869Sdfuchs else if (strcmp(tag, "MODULES") == 0) 14612869Sdfuchs error = setmultipath(&inst_modules, val); 14712869Sdfuchs else if (strcmp(tag, "ROOTFS") == 0) 14812869Sdfuchs error = setpath(&inst_rootfs, val); 14912869Sdfuchs else if (strcmp(tag, "LOADER_RC") == 0) 15012869Sdfuchs error = setpath(&inst_loader_rc, val); 15112869Sdfuchs 15212869Sdfuchs tag = p; 15312869Sdfuchs } 15412869Sdfuchs 15512869Sdfuchs return (error); 15612869Sdfuchs} 15712869Sdfuchs 15812869Sdfuchsstatic void 15912869Sdfuchscleanup(void) 16012869Sdfuchs{ 16112869Sdfuchs u_int i; 16212869Sdfuchs 16312869Sdfuchs if (inst_kernel != NULL) { 16412869Sdfuchs free(inst_kernel); 16512869Sdfuchs inst_kernel = NULL; 16612869Sdfuchs } 16712869Sdfuchs if (inst_modules != NULL) { 16812869Sdfuchs i = 0; 16912869Sdfuchs while (inst_modules[i] != NULL) 17012869Sdfuchs free(inst_modules[i++]); 17112869Sdfuchs free(inst_modules); 17212869Sdfuchs inst_modules = NULL; 17312869Sdfuchs } 17412869Sdfuchs if (inst_rootfs != NULL) { 17512869Sdfuchs free(inst_rootfs); 17612869Sdfuchs inst_rootfs = NULL; 17712869Sdfuchs } 17812869Sdfuchs if (inst_loader_rc != NULL) { 17912869Sdfuchs free(inst_loader_rc); 18012869Sdfuchs inst_loader_rc = NULL; 18112869Sdfuchs } 18212869Sdfuchs pkgfs_cleanup(); 18312869Sdfuchs} 18412869Sdfuchs 18512869Sdfuchs/* 18612869Sdfuchs * usage: install URL 18712869Sdfuchs * where: URL = (tftp|file)://[host]/<package> 18812869Sdfuchs */ 18912869Sdfuchsstatic int 19012869Sdfuchsinstall(char *pkgname) 19112869Sdfuchs{ 19212869Sdfuchs static char buf[256]; 19312869Sdfuchs struct fs_ops *proto; 19412869Sdfuchs struct preloaded_file *fp; 19512869Sdfuchs char *s, *currdev; 19612869Sdfuchs const char *devname; 19712869Sdfuchs int error, fd, i, local; 19812869Sdfuchs 19912869Sdfuchs s = strstr(pkgname, "://"); 20012869Sdfuchs if (s == NULL) 20112869Sdfuchs goto invalid_url; 20212869Sdfuchs 20312869Sdfuchs i = s - pkgname; 20412869Sdfuchs if (i == 4 && !strncasecmp(pkgname, "tftp", i)) { 20512869Sdfuchs devname = "net0"; 20612869Sdfuchs proto = &tftp_fsops; 20712869Sdfuchs local = 0; 20812869Sdfuchs } else if (i == 4 && !strncasecmp(pkgname, "file", i)) { 20912869Sdfuchs currdev = getenv("currdev"); 21012869Sdfuchs if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) { 21112869Sdfuchs devname = "pxe0"; 21212869Sdfuchs proto = NULL; 21312869Sdfuchs } else { 21412869Sdfuchs devname = "disk1"; 21512869Sdfuchs proto = &dosfs_fsops; 21612869Sdfuchs } 21712869Sdfuchs local = 1; 21812869Sdfuchs } else 21912869Sdfuchs goto invalid_url; 22012869Sdfuchs 22112869Sdfuchs s += 3; 22212869Sdfuchs if (*s == '\0') 22312869Sdfuchs goto invalid_url; 22412869Sdfuchs 22512869Sdfuchs if (*s != '/' ) { 22612869Sdfuchs if (local) 22712869Sdfuchs goto invalid_url; 22812869Sdfuchs 22912869Sdfuchs pkgname = strchr(s, '/'); 23012869Sdfuchs if (pkgname == NULL) 23112869Sdfuchs goto invalid_url; 23212869Sdfuchs 23312869Sdfuchs *pkgname = '\0'; 23412869Sdfuchs servip.s_addr = inet_addr(s); 23512869Sdfuchs if (servip.s_addr == htonl(INADDR_NONE)) 23612869Sdfuchs goto invalid_url; 23712869Sdfuchs 23812869Sdfuchs setenv("serverip", inet_ntoa(servip), 1); 23912869Sdfuchs 24012869Sdfuchs *pkgname = '/'; 24112869Sdfuchs } else 24212869Sdfuchs pkgname = s; 24312869Sdfuchs 24412869Sdfuchs if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) { 24512869Sdfuchs command_errmsg = "package name too long"; 24612869Sdfuchs return (CMD_ERROR); 24712869Sdfuchs } 24812869Sdfuchs sprintf(buf, "%s:%s", devname, pkgname); 24912869Sdfuchs setenv("install_package", buf, 1); 25012869Sdfuchs 25112869Sdfuchs error = pkgfs_init(buf, proto); 25212869Sdfuchs if (error) { 25312869Sdfuchs command_errmsg = "cannot open package"; 25412869Sdfuchs goto fail; 25512869Sdfuchs } 25612869Sdfuchs 25712869Sdfuchs /* 25812869Sdfuchs * Point of no return: unload anything that may have been 25912869Sdfuchs * loaded and prune the environment from harmful variables. 26012869Sdfuchs */ 26112869Sdfuchs unload(); 26212869Sdfuchs unsetenv("vfs.root.mountfrom"); 26312869Sdfuchs 26412869Sdfuchs /* 26512869Sdfuchs * read the metatags file. 26612869Sdfuchs */ 26712869Sdfuchs fd = open("/metatags", O_RDONLY); 26812869Sdfuchs if (fd != -1) { 26912869Sdfuchs error = read_metatags(fd); 27012869Sdfuchs close(fd); 27112869Sdfuchs if (error) { 27212869Sdfuchs command_errmsg = "cannot load metatags"; 27312869Sdfuchs goto fail; 27412869Sdfuchs } 27512869Sdfuchs } 27612869Sdfuchs 27712869Sdfuchs s = (inst_kernel == NULL) ? "/kernel" : inst_kernel; 27812869Sdfuchs error = mod_loadkld(s, 0, NULL); 27912869Sdfuchs if (error) { 28012869Sdfuchs command_errmsg = "cannot load kernel from package"; 28112869Sdfuchs goto fail; 28212869Sdfuchs } 28312869Sdfuchs 28412869Sdfuchs /* If there is a loader.rc in the package, execute it */ 28512869Sdfuchs s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc; 28612869Sdfuchs fd = open(s, O_RDONLY); 28712869Sdfuchs if (fd != -1) { 28812869Sdfuchs close(fd); 28912869Sdfuchs error = include(s); 29012869Sdfuchs if (error == CMD_ERROR) 29112869Sdfuchs goto fail; 29212869Sdfuchs } 29312869Sdfuchs 29412869Sdfuchs i = 0; 29512869Sdfuchs while (inst_modules != NULL && inst_modules[i] != NULL) { 29612869Sdfuchs error = mod_loadkld(inst_modules[i], 0, NULL); 29712869Sdfuchs if (error) { 29812869Sdfuchs command_errmsg = "cannot load module(s) from package"; 29912869Sdfuchs goto fail; 30012869Sdfuchs } 30112869Sdfuchs i++; 30212869Sdfuchs } 30312869Sdfuchs 30412869Sdfuchs s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs; 30512869Sdfuchs if (file_loadraw(s, "mfs_root", 1) == NULL) { 30612869Sdfuchs error = errno; 30712869Sdfuchs command_errmsg = "cannot load root file system"; 30812869Sdfuchs goto fail; 30912869Sdfuchs } 31012869Sdfuchs 31112869Sdfuchs cleanup(); 31212869Sdfuchs 31312869Sdfuchs fp = file_findfile(NULL, NULL); 31412869Sdfuchs if (fp != NULL) 31512869Sdfuchs file_formats[fp->f_loader]->l_exec(fp); 31612869Sdfuchs error = CMD_ERROR; 31712869Sdfuchs command_errmsg = "unable to start installation"; 31812869Sdfuchs 31912869Sdfuchs fail: 32012869Sdfuchs sprintf(buf, "%s (error %d)", command_errmsg, error); 32112869Sdfuchs cleanup(); 32212869Sdfuchs unload(); 32312869Sdfuchs exclusive_file_system = NULL; 32412869Sdfuchs command_errmsg = buf; /* buf is static. */ 32512869Sdfuchs return (CMD_ERROR); 32612869Sdfuchs 32712869Sdfuchs invalid_url: 32812869Sdfuchs command_errmsg = "invalid URL"; 32912869Sdfuchs return (CMD_ERROR); 33012869Sdfuchs} 33112869Sdfuchs 33212869Sdfuchsstatic int 33312869Sdfuchscommand_install(int argc, char *argv[]) 33412869Sdfuchs{ 33512869Sdfuchs int argidx; 33612869Sdfuchs 33712869Sdfuchs unsetenv("install_format"); 33812869Sdfuchs 33912869Sdfuchs argidx = 1; 34012869Sdfuchs while (1) { 34112869Sdfuchs if (argc == argidx) { 34212869Sdfuchs command_errmsg = 34312869Sdfuchs "usage: install [--format] <URL>"; 34412869Sdfuchs return (CMD_ERROR); 34512869Sdfuchs } 34612869Sdfuchs if (!strcmp(argv[argidx], "--format")) { 34712869Sdfuchs setenv("install_format", "yes", 1); 34812869Sdfuchs argidx++; 34912869Sdfuchs continue; 35012869Sdfuchs } 35112869Sdfuchs break; 35212869Sdfuchs } 35312869Sdfuchs 35412869Sdfuchs return (install(argv[argidx])); 35512869Sdfuchs} 35612869Sdfuchs