]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/sysinstall/ftp.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / sysinstall / ftp.c
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
47 Boolean ftpInitted = FALSE;
48 static FILE *OpenConn;
49 int FtpPort;
50
51 /* List of sub directories to look for under a given FTP server. */
52 const 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 */
61 static Boolean
62 netUp(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 */
73 static void
74 netDown(Device *dev)
75 {
76     Device *netdev = (Device *)dev->private;
77
78     if (netdev)
79         DEVICE_SHUTDOWN(netdev);
80 }
81
82 Boolean
83 mediaInitFTP(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
101 try:
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
203 punt:
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
214 FILE *
215 mediaGetFTP(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
275 void
276 mediaShutdownFTP(Device *dev)
277 {
278     if (!ftpInitted)
279         return;
280
281     if (OpenConn != NULL) {
282         fclose(OpenConn);
283         OpenConn = NULL;
284     }
285     ftpInitted = FALSE;
286 }