summaryrefslogtreecommitdiffstats
path: root/loader/urlinstall.c
diff options
context:
space:
mode:
Diffstat (limited to 'loader/urlinstall.c')
-rw-r--r--loader/urlinstall.c452
1 files changed, 452 insertions, 0 deletions
diff --git a/loader/urlinstall.c b/loader/urlinstall.c
new file mode 100644
index 000000000..177716612
--- /dev/null
+++ b/loader/urlinstall.c
@@ -0,0 +1,452 @@
+/*
+ * urlinstall.c - code to set up url (ftp/http) installs
+ *
+ * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Erik Troan <ewt@redhat.com>
+ * Matt Wilson <msw@redhat.com>
+ * Michael Fulbright <msf@redhat.com>
+ * Jeremy Katz <katzj@redhat.com>
+ */
+
+#include <newt.h>
+#include <popt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "../isys/iface.h"
+
+#include "copy.h"
+#include "kickstart.h"
+#include "loader.h"
+#include "loadermisc.h"
+#include "lang.h"
+#include "log.h"
+#include "method.h"
+#include "net.h"
+#include "method.h"
+#include "urlinstall.h"
+#include "cdinstall.h"
+#include "urls.h"
+#include "windows.h"
+
+/* boot flags */
+extern uint64_t flags;
+
+static int loadSingleUrlImage(struct iurlinfo * ui, char *path,
+ char * dest, char * mntpoint, char * device,
+ int silentErrors) {
+ int fd;
+ int rc = 0;
+ char *ehdrs = NULL;
+
+ if (ui->protocol == URL_METHOD_HTTP) {
+ char *arch = getProductArch();
+ char *name = getProductName();
+
+ if (asprintf(&ehdrs, "User-Agent: anaconda/%s\r\n"
+ "X-Anaconda-Architecture: %s\r\n"
+ "X-Anaconda-System-Release: %s\r\n",
+ VERSION, arch, name) == -1) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+ }
+
+ fd = urlinstStartTransfer(ui, path, ehdrs);
+
+ if (fd == -2) {
+ if (ehdrs) free (ehdrs);
+ return 2;
+ }
+ else if (fd < 0) {
+ if (!silentErrors) {
+ newtWinMessage(_("Error"), _("OK"),
+ _("Unable to retrieve %s://%s%s."),
+ (ui->protocol == URL_METHOD_FTP ? "ftp" : "http"),
+ ui->address, path);
+ }
+
+ if (ehdrs) free (ehdrs);
+ return 2;
+ }
+
+ if (dest != NULL) {
+ rc = copyFileAndLoopbackMount(fd, dest, device, mntpoint);
+ }
+
+ urlinstFinishTransfer(ui, fd);
+ return rc;
+}
+
+static void copyWarnFn (char *msg) {
+ logMessage(WARNING, msg);
+}
+
+static void copyErrorFn (char *msg) {
+ newtWinMessage(_("Error"), _("OK"), _(msg));
+}
+
+static int loadUrlImages(struct iurlinfo * ui) {
+ char *buf, *path, *dest, *slash;
+ int rc;
+
+ /* Figure out the path where updates.img and product.img files are
+ * kept. Since ui->prefix points to a stage2 image file, we just need
+ * to trim off the file name and look in the same directory.
+ */
+ if ((slash = strrchr(ui->prefix, '/')) == NULL)
+ return 0;
+
+ if ((path = strndup(ui->prefix, slash - ui->prefix)) == NULL)
+ path = ui->prefix;
+
+ /* grab the updates.img before install.img so that we minimize our
+ * ramdisk usage */
+ if (asprintf(&buf, "%s/%s", path, "updates.img") == -1) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ if (!loadSingleUrlImage(ui, buf,
+ "/tmp/updates-disk.img", "/tmp/update-disk",
+ "/dev/loop7", 1)) {
+ copyDirectory("/tmp/update-disk", "/tmp/updates", copyWarnFn,
+ copyErrorFn);
+ umountLoopback("/tmp/update-disk", "/dev/loop7");
+ unlink("/tmp/updates-disk.img");
+ unlink("/tmp/update-disk");
+ } else if (!access("/tmp/updates-disk.img", R_OK)) {
+ unpackCpioBall("/tmp/updates-disk.img", "/tmp/updates");
+ unlink("/tmp/updates-disk.img");
+ }
+
+ free(buf);
+
+ /* grab the product.img before install.img so that we minimize our
+ * ramdisk usage */
+ if (asprintf(&buf, "%s/%s", path, "product.img") == -1) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ if (!loadSingleUrlImage(ui, buf,
+ "/tmp/product-disk.img", "/tmp/product-disk",
+ "/dev/loop7", 1)) {
+ copyDirectory("/tmp/product-disk", "/tmp/product", copyWarnFn,
+ copyErrorFn);
+ umountLoopback("/tmp/product-disk", "/dev/loop7");
+ unlink("/tmp/product-disk.img");
+ unlink("/tmp/product-disk");
+ }
+
+ free(buf);
+
+ if (asprintf(&dest, "/tmp/install.img") == -1) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ rc = loadSingleUrlImage(ui, ui->prefix, dest, "/mnt/runtime", "/dev/loop0", 0);
+ free(dest);
+
+ if (rc) {
+ if (rc != 2)
+ newtWinMessage(_("Error"), _("OK"),
+ _("Unable to retrieve the install image."));
+ return 1;
+ }
+
+ return 0;
+}
+
+char *mountUrlImage(struct installMethod *method, char *location,
+ struct loaderData_s *loaderData) {
+ struct iurlinfo ui;
+ char *url = NULL;
+
+ enum { URL_STAGE_MAIN, URL_STAGE_FETCH,
+ URL_STAGE_DONE } stage = URL_STAGE_MAIN;
+
+ memset(&ui, 0, sizeof(ui));
+
+ while (stage != URL_STAGE_DONE) {
+ switch(stage) {
+ case URL_STAGE_MAIN: {
+ /* If the stage2= parameter was given (or inferred from repo=)
+ * then use that configuration info to fetch the image. This
+ * could also have come from kickstart. Else, we need to show
+ * the UI.
+ */
+ if (loaderData->method == METHOD_URL && loaderData->stage2Data) {
+ url = ((struct urlInstallData *) loaderData->stage2Data)->url;
+ logMessage(INFO, "URL_STAGE_MAIN: url is %s", url);
+
+ if (!url) {
+ logMessage(ERROR, "missing URL specification");
+ loaderData->method = -1;
+ free(loaderData->stage2Data);
+ loaderData->stage2Data = NULL;
+
+ if (loaderData->inferredStage2)
+ loaderData->invalidRepoParam = 1;
+
+ break;
+ }
+
+ /* explode url into ui struct */
+ convertURLToUI(url, &ui);
+
+ /* ks info was adequate, lets skip to fetching image */
+ stage = URL_STAGE_FETCH;
+ break;
+ } else {
+ char *substr;
+
+ if (urlMainSetupPanel(&ui))
+ return NULL;
+
+ /* If the user-provided URL points at a repo instead of
+ * a stage2 image, fix it up now.
+ */
+ substr = strstr(ui.prefix, ".img");
+ if (!substr || (substr && *(substr+4) != '\0')) {
+ if (asprintf(&ui.prefix, "%s/images/install.img",
+ ui.prefix) == -1) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__,
+ __LINE__);
+ abort();
+ }
+ }
+
+ loaderData->invalidRepoParam = 1;
+ }
+
+ stage = URL_STAGE_FETCH;
+ break;
+ }
+
+ case URL_STAGE_FETCH: {
+ if (FL_TESTING(flags)) {
+ stage = URL_STAGE_DONE;
+ break;
+ }
+
+ if (loadUrlImages(&ui)) {
+ stage = URL_STAGE_MAIN;
+
+ if (loaderData->method >= 0)
+ loaderData->method = -1;
+
+ if (loaderData->inferredStage2)
+ loaderData->invalidRepoParam = 1;
+ } else {
+ stage = URL_STAGE_DONE;
+ }
+
+ break;
+ }
+
+ case URL_STAGE_DONE:
+ break;
+ }
+ }
+
+ url = convertUIToURL(&ui);
+ return url;
+}
+
+int getFileFromUrl(char * url, char * dest,
+ struct loaderData_s * loaderData) {
+ int retval = 0;
+ struct iurlinfo ui;
+ enum urlprotocol_t proto =
+ !strncmp(url, "ftp://", 6) ? URL_METHOD_FTP : URL_METHOD_HTTP;
+ char * host = NULL, * file = NULL, * chptr = NULL, *login = NULL, *password = NULL;
+ int fd, rc;
+ iface_t iface;
+ char *ehdrs = NULL, *ip = NULL;
+
+ iface_init_iface_t(&iface);
+
+ if (kickstartNetworkUp(loaderData, &iface)) {
+ logMessage(ERROR, "unable to bring up network");
+ return 1;
+ }
+
+ memset(&ui, 0, sizeof(ui));
+ ui.protocol = proto;
+
+ if ((ip = iface_ip2str(loaderData->netDev)) == NULL) {
+ logMessage(ERROR, "getFileFromUrl: no client IP information");
+ return 1;
+ }
+
+ getHostPathandLogin((proto == URL_METHOD_FTP ? url + 6 : url + 7),
+ &host, &file, &login, &password, ip);
+
+ logMessage(INFO, "file location: %s://%s%s",
+ (proto == URL_METHOD_FTP ? "ftp" : "http"), host, file);
+
+ chptr = strchr(host, '/');
+ if (chptr == NULL) {
+ ui.address = strdup(host);
+ ui.prefix = strdup("/");
+ } else {
+ *chptr = '\0';
+ ui.address = strdup(host);
+ host = chptr;
+ *host = '/';
+ ui.prefix = strdup(host);
+ }
+
+ if (password[0] != '\0')
+ ui.password = strdup (password);
+ if (login[0] != '\0')
+ ui.login = strdup (login);
+
+ if (proto == URL_METHOD_HTTP) {
+ char *arch = getProductArch();
+ char *name = getProductName();
+
+ if (asprintf(&ehdrs, "User-Agent: anaconda/%s\r\n"
+ "X-Anaconda-Architecture: %s\r\n"
+ "X-Anaconda-System-Release: %s\r\n",
+ VERSION, arch, name) == -1) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+ }
+
+ if (proto == URL_METHOD_HTTP && FL_KICKSTART_SEND_MAC(flags)) {
+ /* find all ethernet devices and make a header entry for each one */
+ int i;
+ char *dev, *mac, *tmpstr;
+ struct device **devices;
+
+ devices = getDevices(DEVICE_NETWORK);
+ for (i = 0; devices && devices[i]; i++) {
+ dev = devices[i]->device;
+ mac = iface_mac2str(dev);
+
+ if (mac) {
+ if (asprintf(&tmpstr, "X-RHN-Provisioning-MAC-%d: %s %s\r\n",
+ i, dev, mac) == -1) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ if (!ehdrs) {
+ ehdrs = strdup(tmpstr);
+ } else {
+ ehdrs = (char *) realloc(ehdrs, strlen(ehdrs)+strlen(tmpstr)+1);
+ strcat(ehdrs, tmpstr);
+ }
+
+ free(mac);
+ free(tmpstr);
+ }
+ }
+ }
+
+ fd = urlinstStartTransfer(&ui, file, ehdrs);
+ if (fd < 0) {
+ logMessage(ERROR, "failed to retrieve http://%s/%s%s", ui.address, ui.prefix, file);
+ retval = 1;
+ goto err;
+ }
+
+ rc = copyFileFd(fd, dest);
+ if (rc) {
+ unlink (dest);
+ logMessage(ERROR, "failed to copy file to %s", dest);
+ retval = 1;
+ goto err;
+ }
+
+ urlinstFinishTransfer(&ui, fd);
+
+err:
+ if (file) free(file);
+ if (ehdrs) free(ehdrs);
+ if (host) free(host);
+ if (login) free(login);
+ if (password) free(password);
+
+ return retval;
+}
+
+/* pull kickstart configuration file via http */
+int kickstartFromUrl(char * url, struct loaderData_s * loaderData) {
+ return getFileFromUrl(url, "/tmp/ks.cfg", loaderData);
+}
+
+void setKickstartUrl(struct loaderData_s * loaderData, int argc,
+ char ** argv) {
+
+ char *url = NULL, *substr = NULL;
+ poptContext optCon;
+ int rc;
+ struct poptOption ksUrlOptions[] = {
+ { "url", '\0', POPT_ARG_STRING, &url, 0, NULL, NULL },
+ { 0, 0, 0, 0, 0, 0, 0 }
+ };
+
+ logMessage(INFO, "kickstartFromUrl");
+ optCon = poptGetContext(NULL, argc, (const char **) argv, ksUrlOptions, 0);
+ if ((rc = poptGetNextOpt(optCon)) < -1) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Bad argument to Url kickstart method "
+ "command %s: %s"),
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ poptStrerror(rc));
+ return;
+ }
+
+ if (!url) {
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Must supply a --url argument to Url kickstart method."));
+ return;
+ }
+
+ /* determine install type */
+ if (strstr(url, "http://") || strstr(url, "ftp://"))
+ loaderData->method = METHOD_URL;
+ else {
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Unknown Url method %s"), url);
+ return;
+ }
+
+ substr = strstr(url, ".img");
+ if (!substr || (substr && *(substr+4) != '\0')) {
+ loaderData->instRepo = strdup(url);
+ } else {
+ if ((loaderData->stage2Data = calloc(sizeof(struct urlInstallData *), 1)) == NULL)
+ return;
+
+ ((struct urlInstallData *)loaderData->stage2Data)->url = url;
+ }
+
+ logMessage(INFO, "results of url ks, url %s", url);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4: */