]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/sysinstall/ftp.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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     "pub/FreeBSD", "pub/FreeBSD/releases/"MACHINE,
54     "pub/FreeBSD/snapshots/"MACHINE, NULL };
55
56 /* Brings up attached network device, if any - takes FTP device as arg */
57 static Boolean
58 netUp(Device *dev)
59 {
60     Device *netdev = (Device *)dev->private;
61
62     if (netdev)
63         return DEVICE_INIT(netdev);
64     else
65         return TRUE;    /* No net == happy net */
66 }
67
68 /* Brings down attached network device, if any - takes FTP device as arg */
69 static void
70 netDown(Device *dev)
71 {
72     Device *netdev = (Device *)dev->private;
73
74     if (netdev)
75         DEVICE_SHUTDOWN(netdev);
76 }
77
78 Boolean
79 mediaInitFTP(Device *dev)
80 {
81     int i, code, af, fdir;
82     char *cp, *rel, *hostname, *dir;
83     char *user, *login_name, password[80];
84
85     if (ftpInitted)
86         return TRUE;
87
88     if (OpenConn) {
89         fclose(OpenConn);
90         OpenConn = NULL;
91     }
92
93     /* If we can't initialize the network, bag it! */
94     if (!netUp(dev))
95         return FALSE;
96
97 try:
98     cp = variable_get(VAR_FTP_PATH);
99     if (!cp) {
100         if (DITEM_STATUS(mediaSetFTP(NULL)) == DITEM_FAILURE || (cp = variable_get(VAR_FTP_PATH)) == NULL) {
101             msgConfirm("Unable to get proper FTP path.  FTP media not initialized.");
102             netDown(dev);
103             return FALSE;
104         }
105     }
106
107     hostname = variable_get(VAR_FTP_HOST);
108     dir = variable_get(VAR_FTP_DIR);
109     if (!hostname || !dir) {
110         msgConfirm("Missing FTP host or directory specification.  FTP media not initialized,");
111         netDown(dev);
112         return FALSE;
113     }
114     user = variable_get(VAR_FTP_USER);
115     login_name = (!user || !*user) ? "anonymous" : user;
116
117     if (variable_get(VAR_FTP_PASS))
118         SAFE_STRCPY(password, variable_get(VAR_FTP_PASS));
119     else if (RunningAsInit)
120         sprintf(password, "installer@%s", variable_get(VAR_HOSTNAME));
121     else {
122         struct passwd *pw;
123         char *user;
124
125         pw = getpwuid(getuid());
126         user = pw ? pw->pw_name : "ftp";
127         sprintf(password, "%s@%s", user, variable_get(VAR_HOSTNAME));
128     }
129     af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
130     msgNotify("Logging in to %s@%s..", login_name, hostname);
131     if ((OpenConn = ftpLoginAf(hostname, af, login_name, password, FtpPort, isDebug(), &code)) == NULL) {
132         msgConfirm("Couldn't open FTP connection to %s:\n  %s.", hostname, ftpErrString(code));
133         goto punt;
134     }
135
136     ftpPassive(OpenConn, !strcmp(variable_get(VAR_FTP_STATE), "passive"));
137     ftpBinary(OpenConn);
138     if (dir && *dir != '\0') {
139         if ((i = ftpChdir(OpenConn, dir)) != 0) {
140             if (i == 550)
141                 msgConfirm("No such directory ftp://%s/%s\n"
142                            "please check your URL and try again.", hostname, dir);
143             else
144                 msgConfirm("FTP chdir to ftp://%s/%s returned error status:\n  %s.", hostname, dir, ftpErrString(i));
145             goto punt;
146         }
147     }
148
149     /*
150      * Now that we've verified that the path we're given is ok, let's try to
151      * be a bit intelligent in locating the release we are looking for.  First
152      * off, if the release is specified as "__RELEASE" or "any", then just
153      * assume that the current directory is the one we want and give up.
154      */
155     rel = variable_get(VAR_RELNAME);
156     if (strcmp(rel, "__RELEASE") && strcmp(rel, "any")) {
157         /*
158          * Ok, since we have a release variable, let's walk through the list
159          * of directories looking for a release directory.  The first one to
160          * match wins.  For each case, we chdir to ftp_dirs[fdir] first.  If
161          * that fails, we skip to the next one.  Otherwise, we try to chdir to
162          * rel.  If it succeeds we break out.  If it fails, then we go back to
163          * the base directory and try again.  Lots of chdirs, but oh well. :)
164          */
165         for (fdir = 0; ftp_dirs[fdir]; fdir++) {
166             /* Avoid sending CWD . commands which confuse some ftp servers */
167             if (strcmp(ftp_dirs[fdir], ".") &&
168                 (ftpChdir(OpenConn, (char *)ftp_dirs[fdir]) != 0))
169                 continue;
170             if (ftpChdir(OpenConn, rel) == 0) {
171                 ftpInitted = TRUE;
172                 return TRUE;
173             }
174             else        /* reset to "root" dir for a fresh try */
175                 ftpChdir(OpenConn, "/");
176         }
177
178         /*
179          * If we get here, then all of the directories we tried failed, so
180          * print out the error message and ask the user if they want to try
181          * again.
182          */
183         if (!msgYesNo("Warning:  Can't find the `%s' distribution on this\n"
184                       "FTP server.  You may need to visit a different server for\n"
185                       "the release you are trying to fetch or go to the Options\n"
186                       "menu and to set the release name to explicitly match what's\n"
187                       "available on %s (or set to \"any\").\n\n"
188                       "Would you like to select another FTP server?",
189                       rel, hostname)) {
190             variable_unset(VAR_FTP_PATH);
191             if (DITEM_STATUS(mediaSetFTP(NULL)) != DITEM_FAILURE)
192                 goto try;
193         }
194     } else {
195         ftpInitted = TRUE;
196         return TRUE;
197     }
198
199 punt:
200     ftpInitted = FALSE;
201     if (OpenConn != NULL) {
202         fclose(OpenConn);
203         OpenConn = NULL;
204     }
205     netDown(dev);
206     variable_unset(VAR_FTP_PATH);
207     return FALSE;
208 }
209
210 FILE *
211 mediaGetFTP(Device *dev, char *file, Boolean probe)
212 {
213     int nretries = 1;
214     FILE *fp;
215     char *try, buf[PATH_MAX];
216
217     if (!OpenConn) {
218         msgDebug("No FTP connection open, can't get file %s\n", file);
219         return NULL;
220     }
221
222     try = file;
223     while ((fp = ftpGet(OpenConn, try, 0)) == NULL) {
224         int ftperr = ftpErrno(OpenConn);
225
226         /* If a hard fail, try to "bounce" the ftp server to clear it */
227         if (ftperr != 550) {
228             if (ftperr != 421)  /* Timeout? */
229                 variable_unset(VAR_FTP_PATH);
230             /* If we can't re-initialize, just forget it */
231             DEVICE_SHUTDOWN(dev);
232             if (!DEVICE_INIT(dev)) {
233                 netDown(dev);
234                 if (OpenConn) {
235                     fclose(OpenConn);
236                     OpenConn = NULL;
237                 }
238                 variable_unset(VAR_FTP_PATH);
239                 return NULL;
240             }
241         }
242         else if (probe)
243             return NULL;
244         else {
245             /* Try some alternatives */
246             switch (nretries++) {
247             case 1:
248                 sprintf(buf, "releases/%s", file);
249                 try = buf;
250                 break;
251
252             case 2:
253                 sprintf(buf, "%s/%s", variable_get(VAR_RELNAME), file);
254                 try = buf;
255                 break;
256
257             case 3:
258                 sprintf(buf, "%s/releases/%s", variable_get(VAR_RELNAME), file);
259                 try = buf;
260                 break;
261
262             case 4:
263                 try = file;
264                 break;
265             }
266         }
267     }
268     return fp;
269 }
270
271 void
272 mediaShutdownFTP(Device *dev)
273 {
274     if (!ftpInitted)
275         return;
276
277     if (OpenConn != NULL) {
278         fclose(OpenConn);
279         OpenConn = NULL;
280     }
281     ftpInitted = FALSE;
282 }