1/* 2 * The new sysinstall program. 3 * 4 * This is probably the last attempt in the `sysinstall' line, the next 5 * generation being slated to essentially a complete rewrite. 6 * 7 * $FreeBSD$ 8 * 9 * Copyright (c) 1995 10 * Jordan Hubbard. All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer, 17 * verbatim and that no modifications are made prior to this 18 * point in the file. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37#include "sysinstall.h" 38#include <sys/socket.h> 39#include <netinet/in.h> 40#include <arpa/inet.h> 41#include <sys/param.h> 42#include <sys/wait.h> 43#include <netdb.h> 44#include <pwd.h> 45#include <ftpio.h> 46 47Boolean ftpInitted = FALSE; 48static FILE *OpenConn; 49int FtpPort; 50 51/* List of sub directories to look for under a given FTP server. */ 52const char *ftp_dirs[] = { ".", "releases/"MACHINE, "snapshots/"MACHINE, 53 "releases/"MACHINE"/"MACHINE_ARCH, "snapshots/"MACHINE"/"MACHINE_ARCH, 54 "pub/FreeBSD", "pub/FreeBSD/releases/"MACHINE, 55 "pub/FreeBSD/snapshots/"MACHINE, 56 "pub/FreeBSD/releases/"MACHINE"/"MACHINE_ARCH, 57 "pub/FreeBSD/snapshots/"MACHINE"/"MACHINE_ARCH, 58 NULL }; 59 60/* Brings up attached network device, if any - takes FTP device as arg */ 61static Boolean 62netUp(Device *dev) 63{ 64 Device *netdev = (Device *)dev->private; 65 66 if (netdev) 67 return DEVICE_INIT(netdev); 68 else 69 return TRUE; /* No net == happy net */ 70} 71 72/* Brings down attached network device, if any - takes FTP device as arg */ 73static void 74netDown(Device *dev) 75{ 76 Device *netdev = (Device *)dev->private; 77 78 if (netdev) 79 DEVICE_SHUTDOWN(netdev); 80} 81 82Boolean 83mediaInitFTP(Device *dev) 84{ 85 int i, code, af, fdir; 86 char *cp, *rel, *hostname, *dir; 87 char *user, *login_name, password[80]; 88 89 if (ftpInitted) 90 return TRUE; 91 92 if (OpenConn) { 93 fclose(OpenConn); 94 OpenConn = NULL; 95 } 96 97 /* If we can't initialize the network, bag it! */ 98 if (!netUp(dev)) 99 return FALSE; 100 101try: 102 cp = variable_get(VAR_FTP_PATH); 103 if (!cp) { 104 if (DITEM_STATUS(mediaSetFTP(NULL)) == DITEM_FAILURE || (cp = variable_get(VAR_FTP_PATH)) == NULL) { 105 msgConfirm("Unable to get proper FTP path. FTP media not initialized."); 106 netDown(dev); 107 return FALSE; 108 } 109 } 110 111 hostname = variable_get(VAR_FTP_HOST); 112 dir = variable_get(VAR_FTP_DIR); 113 if (!hostname || !dir) { 114 msgConfirm("Missing FTP host or directory specification. FTP media not initialized."); 115 netDown(dev); 116 return FALSE; 117 } 118 user = variable_get(VAR_FTP_USER); 119 login_name = (!user || !*user) ? "anonymous" : user; 120 121 if (variable_get(VAR_FTP_PASS)) 122 SAFE_STRCPY(password, variable_get(VAR_FTP_PASS)); 123 else if (RunningAsInit) 124 sprintf(password, "installer@%s", variable_get(VAR_HOSTNAME)); 125 else { 126 struct passwd *pw; 127 char *user; 128 129 pw = getpwuid(getuid()); 130 user = pw ? pw->pw_name : "ftp"; 131 sprintf(password, "%s@%s", user, variable_get(VAR_HOSTNAME)); 132 } 133 af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC; 134 msgNotify("Logging in to %s@%s..", login_name, hostname); 135 if ((OpenConn = ftpLoginAf(hostname, af, login_name, password, FtpPort, isDebug(), &code)) == NULL) { 136 msgConfirm("Couldn't open FTP connection to %s:\n %s.", hostname, ftpErrString(code)); 137 goto punt; 138 } 139 140 ftpPassive(OpenConn, !strcmp(variable_get(VAR_FTP_STATE), "passive")); 141 ftpBinary(OpenConn); 142 if (dir && *dir != '\0') { 143 if ((i = ftpChdir(OpenConn, dir)) != 0) { 144 if (i == 550) 145 msgConfirm("No such directory ftp://%s/%s\n" 146 "please check your URL and try again.", hostname, dir); 147 else 148 msgConfirm("FTP chdir to ftp://%s/%s returned error status:\n %s.", hostname, dir, ftpErrString(i)); 149 goto punt; 150 } 151 } 152 153 /* 154 * Now that we've verified that the path we're given is ok, let's try to 155 * be a bit intelligent in locating the release we are looking for. First 156 * off, if the release is specified as "__RELEASE" or "any", then just 157 * assume that the current directory is the one we want and give up. 158 */ 159 rel = variable_get(VAR_RELNAME); 160 if (strcmp(rel, "__RELEASE") && strcmp(rel, "any")) { 161 /* 162 * Ok, since we have a release variable, let's walk through the list 163 * of directories looking for a release directory. The first one to 164 * match wins. For each case, we chdir to ftp_dirs[fdir] first. If 165 * that fails, we skip to the next one. Otherwise, we try to chdir to 166 * rel. If it succeeds we break out. If it fails, then we go back to 167 * the base directory and try again. Lots of chdirs, but oh well. :) 168 */ 169 for (fdir = 0; ftp_dirs[fdir]; fdir++) { 170 /* Avoid sending CWD . commands which confuse some ftp servers */ 171 if (strcmp(ftp_dirs[fdir], ".") && 172 (ftpChdir(OpenConn, (char *)ftp_dirs[fdir]) != 0)) 173 continue; 174 if (ftpChdir(OpenConn, rel) == 0) { 175 ftpInitted = TRUE; 176 return TRUE; 177 } 178 else /* reset to "root" dir for a fresh try */ 179 ftpChdir(OpenConn, "/"); 180 } 181 182 /* 183 * If we get here, then all of the directories we tried failed, so 184 * print out the error message and ask the user if they want to try 185 * again. 186 */ 187 if (!msgYesNo("Warning: Can't find the `%s' distribution on this\n" 188 "FTP server. You may need to visit a different server for\n" 189 "the release you are trying to fetch or go to the Options\n" 190 "menu and to set the release name to explicitly match what's\n" 191 "available on %s (or set to \"any\").\n\n" 192 "Would you like to select another FTP server?", 193 rel, hostname)) { 194 variable_unset(VAR_FTP_PATH); 195 if (DITEM_STATUS(mediaSetFTP(NULL)) != DITEM_FAILURE) 196 goto try; 197 } 198 } else { 199 ftpInitted = TRUE; 200 return TRUE; 201 } 202 203punt: 204 ftpInitted = FALSE; 205 if (OpenConn != NULL) { 206 fclose(OpenConn); 207 OpenConn = NULL; 208 } 209 netDown(dev); 210 variable_unset(VAR_FTP_PATH); 211 return FALSE; 212} 213 214FILE * 215mediaGetFTP(Device *dev, char *file, Boolean probe) 216{ 217 int nretries = 1; 218 FILE *fp; 219 char *try, buf[PATH_MAX]; 220 221 if (!OpenConn) { 222 msgDebug("No FTP connection open, can't get file %s\n", file); 223 return NULL; 224 } 225 226 try = file; 227 while ((fp = ftpGet(OpenConn, try, 0)) == NULL) { 228 int ftperr = ftpErrno(OpenConn); 229 230 /* If a hard fail, try to "bounce" the ftp server to clear it */ 231 if (ftperr != 550) { 232 if (ftperr != 421) /* Timeout? */ 233 variable_unset(VAR_FTP_PATH); 234 /* If we can't re-initialize, just forget it */ 235 DEVICE_SHUTDOWN(dev); 236 if (!DEVICE_INIT(dev)) { 237 netDown(dev); 238 if (OpenConn) { 239 fclose(OpenConn); 240 OpenConn = NULL; 241 } 242 variable_unset(VAR_FTP_PATH); 243 return NULL; 244 } 245 } 246 else if (probe) 247 return NULL; 248 else { 249 /* Try some alternatives */ 250 switch (nretries++) { 251 case 1: 252 sprintf(buf, "releases/%s", file); 253 try = buf; 254 break; 255 256 case 2: 257 sprintf(buf, "%s/%s", variable_get(VAR_RELNAME), file); 258 try = buf; 259 break; 260 261 case 3: 262 sprintf(buf, "%s/releases/%s", variable_get(VAR_RELNAME), file); 263 try = buf; 264 break; 265 266 case 4: 267 try = file; 268 break; 269 } 270 } 271 } 272 return fp; 273} 274 275void 276mediaShutdownFTP(Device *dev) 277{ 278 if (!ftpInitted) 279 return; 280 281 if (OpenConn != NULL) { 282 fclose(OpenConn); 283 OpenConn = NULL; 284 } 285 ftpInitted = FALSE; 286} 287