aboutsummaryrefslogblamecommitdiff
path: root/release/sysinstall/media_strategy.c
blob: d25a7a9d344e3cde8926ba332931f106a8131315 (plain) (tree)
1
2
3
4
5
6
7
8
9
10





                                                                       
                                                             


                                             

                                           


































                                                                             







                      
                     


                       
                  
                








                      
                           
























                                                      
                                                                                                   



















                                                        

                                                                         




























                                                 

                                                                   
                          

                                                                              










                                         

                                                                      





                                     

                                                          




                         
                                                          

         
               




                                            
 

              
                                                                               

            
                                          
                                             
 


                
                            
                         
 
          
















                                                       



                         
                                  
                    

                                                          
 

            
                                            
                                                           




















                                                                                            



                             
     




                                     
 
                                       






                                      
                                       

                                                                              
                  

     










                                                                                                                   





                                                












                                                                                               
 
                                                                 
              

                        
                               
                              
                               


                                       
        
                                                     


















                                                                                         

                                                                                        
                                                        


                               

                                                                      



                                                                   
                           
                                                     


                                                                               

                                                  

                                     

                                                                                                             

                        
            


                                                
                                                           

                        
                      
         


                                                      


                   
 


                                       

                            


                           


                             



                              


                              

                   
                                                                         
     
                                                                                          





                                                                                
                                  


                            
                                                                                                     

                         
                                                                                          


                         
                        


                
   
                                     
 

                                      
                               


                                                                  
                                                        
 
                                                               
     
                                                                                                                                     
                        


                     
                                                                   
 
                                                   

                    


    
                               
 
    
                                        









                                                                                            
                                  
                                                                         
                                   
                         


           

                             


                            

                                  
 















                                                                              


                
   
                                      
 
                                      
                               




                                                                  
                                                        
                                                               
     
                                                                                                                                     
                        

                     

                                              
 
                                                  
                    
                  


    
                                
 





                                                                                           


       
                          



                

                                  
       

                             
          
             
 


                           
                   
                                         
                                






                                                                                          
                                                                




                                                                                                                                            
                                                     
                
                                                                                                                     


                         


                             
                                                                                   


                                            
                              


                
   
                                    
 
              


    
                              



           
    
                                 
 

























                                                                                                                                                                                                                                                         

 

                 
       
                         
 
          
                               
                                 


                                               

                                           








                                                 





                                                                                                  
                                      
                       

                                             
                                
                                            
                                              
                                                
                                                                                    
                                                                                                                                      



                                                     
                                                
 
                                          
                                                                
                                                                                              





                             
                              
                                                        
                           
     
                                        


                

                        
   
                                   
 

                         
                                  

                                   
 

                  
                                                            
                                                                






                                                                                                                                         
   






                                                                                              
 

                                                                            
                                         


                                 
                                                  


                                 
                                                
                 

                 
                                   

                                                                                                                 
                       

                      
                   
     
              

                    
                              
                               
 


                                       

                                                     
                  
            
                                                                                                
                                  


                       
                                                                                                                               

                        
            
                                                   



                                             
                                                                                                     


                            



                        
                                                                   



                   


    
                             
 





                                            







                                                                                                 

                                    


       
                         



                
   
                                   
 
              

 
                                                                           
 

       
                         



                
   
                                   
 
              

 
    
                             

 
/*
 * The new sysinstall program.
 *
 * This is probably the last attempt in the `sysinstall' line, the next
 * generation being slated to essentially a complete rewrite.
 *
 * $Id: media_strategy.c,v 1.27 1995/05/26 19:28:02 jkh Exp $
 *
 * Copyright (c) 1995
 *	Jordan Hubbard.  All rights reserved.
 * Copyright (c) 1995
 * 	Gary J Palmer. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer, 
 *    verbatim and that no modifications are made prior to this 
 *    point in the file.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Jordan Hubbard
 *	for the FreeBSD Project.
 * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <stdio.h>
#include "sysinstall.h"
#include <ctype.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/dkbad.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "ftp.h"

#define MSDOSFS
#define CD9660
#define NFS
#include <sys/mount.h>
#undef MSDOSFS
#undef CD9660
#undef NFS

#define MAX_ATTRIBS	200
#define MAX_NAME	511
#define MAX_VALUE	4095

struct attribs {
    char 		*name;
    char 		*value;
};

static int 		lno;
static int		num_attribs;

static int
attr_parse(struct attribs **attr, char *file)
{
    char hold_n[MAX_NAME+1];
    char hold_v[MAX_VALUE+1];
    int n, v, ch = 0;
    enum { LOOK, COMMENT, NAME, VALUE, COMMIT } state;
    FILE *fp;
    
    num_attribs = n = v = lno = 0;
    state = LOOK;
    
    if ((fp=fopen(file, "r")) == NULL)
    {
	msgConfirm("Cannot open the information file `%s': %s (%d)", file, strerror(errno), errno);
	return 0;
    }

    while (state == COMMIT || (ch = fgetc(fp)) != EOF) {
	/* Count lines */
	if (ch == '\n')
	    ++lno;
	switch(state) {
	case LOOK:
	    if (isspace(ch))
		continue;
	    /* Allow shell or lisp style comments */
	    else if (ch == '#' || ch == ';') {
		state = COMMENT;
		continue;
	    }
	    else if (isalpha(ch)) {
		hold_n[n++] = ch;
		state = NAME;
	    }
	    else
		msgFatal("Invalid character '%c' at line %d\n", ch, lno);
	    break;
	    
	case COMMENT:
	    if (ch == '\n')
		state = LOOK;
	    break;
	    
	case NAME:
	    if (ch == '\n') {
		hold_n[n] = '\0';
		hold_v[v = 0] = '\0';
		state = COMMIT;
	    }
	    else if (isspace(ch))
		continue;
	    else if (ch == '=') {
		hold_n[n] = '\0';
		state = VALUE;
	    }
	    else
		hold_n[n++] = ch;
	    break;
	    
	case VALUE:
	    if (v == 0 && isspace(ch))
		continue;
	    else if (ch == '{') {
		/* multiline value */
		while ((ch = fgetc(fp)) != '}') {
		    if (ch == EOF)
			msgFatal("Unexpected EOF on line %d", lno);
		    else {
		    	if (v == MAX_VALUE)
			    msgFatal("Value length overflow at line %d", lno);
		        hold_v[v++] = ch;
		    }
		}
		hold_v[v] = '\0';
		state = COMMIT;
	    }
	    else if (ch == '\n') {
		hold_v[v] = '\0';
		state = COMMIT;
	    }
	    else {
		if (v == MAX_VALUE)
		    msgFatal("Value length overflow at line %d", lno);
		else
		    hold_v[v++] = ch;
	    }
	    break;
	    
	case COMMIT:
	    (*attr)[num_attribs].name = strdup(hold_n);
	    (*attr)[num_attribs++].value = strdup(hold_v);
	    state = LOOK;
	    v = n = 0;
	    break;

	default:
	    msgFatal("Unknown state at line %d??\n", lno);
	}
    }
    fclose(fp);
    return 1;
}

static const char *
attr_match(struct attribs *attr, char *name)
{
    int n = 0;

    while((strcasecmp(attr[n].name, name)!=0) && (n < num_attribs) && (n < 20))
	n++;

    if (strcasecmp(attr[n].name, name)==0)
	return((const char *) attr[n].value);

    return NULL;
}

static pid_t getDistpid = 0;
static Device *floppyDev;

static int
floppyChoiceHook(char *str)
{
    Device **devs;

    /* Clip garbage off the ends */
    string_prune(str);
    str = string_skipwhite(str);
    if (!*str)
	return 0;
    devs = deviceFind(str, DEVICE_TYPE_FLOPPY);
    if (devs)
	floppyDev = devs[0];
    return devs ? 1 : 0;
}

int
genericGetDist(char *path, void *attrs, Boolean prompt)
{
    int 	fd;
    char 	buf[512];
    struct stat	sb;
    int		pfd[2], numchunks;
    const char *tmp;
    Device *devp;
    struct attribs *dist_attrib = (struct attribs *)attrs;

  top:
    fd = -1;
    /* Floppy is always last-ditch device */
    while (!mediaDevice && (prompt && floppyDev == NULL)) {
	Device **devs;
	int cnt;
		    
	devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
	cnt = deviceCount(devs);
	if (cnt == 1)
	    devp = devs[0];
	else if (cnt > 1) {
	    DMenu *menu;
	    
	    menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyChoiceHook);
	    menu->title = "Please insert the ROOT floppy";
	    dmenuOpenSimple(menu);
	}
	else {
	    msgConfirm("No floppy devices found!  Something is seriously wrong!");
	    return -1;
	}
	if (!floppyDev)
	    continue;
	fd = open(floppyDev->devname, O_RDONLY);
	if (fd != -1)
	    return fd;
	else
	    floppyDev = NULL;
    }
    if (stat(path, &sb) == 0)
    {
	fd = open(path, O_RDONLY, 0);
	return(fd);
    }

    snprintf(buf, 512, "%s.tgz", path);
    if (stat(buf, &sb) == 0)
    {
	fd = open(buf, O_RDONLY, 0);
	return(fd);
    }

    snprintf(buf, 512, "%s.aa", path);
    if (stat(buf, &sb) != 0 && !prompt)
    {
	msgConfirm("Cannot find file(s) for distribution in ``%s''!\n", path);
	return -1;
    }

    if (fd == -1 && prompt) {
	if (mediaDevice->shutdown)
	    (*mediaDevice->shutdown)(mediaDevice);

	if (mediaDevice->init)
	    if (!(*mediaDevice->init)(mediaDevice))
		return -1;
	msgConfirm("Please put distribution files for %s\nin %s and press return", path, mediaDevice->description);
	goto top;
    }

    if (dist_attrib) {
	tmp = attr_match(dist_attrib, "pieces");
	numchunks = atoi(tmp);
    }
    else
	numchunks = 1;

    /* reap the previous child corpse - yuck! */
    if (getDistpid) {
	int i, j;

	i = waitpid(getDistpid, &j, 0);
	if (i < 0 || WEXITSTATUS(j)) {
	    msgNotify("Warning: Previous extraction returned status code %d.", WEXITSTATUS(j));
	    getDistpid = 0;
	    return -1;
	}
	getDistpid = 0;
    }

    msgDebug("Attempting to concatenate %u chunks\n", numchunks);
    pipe(pfd);
    getDistpid = fork();
    if (!getDistpid) {
	caddr_t		memory;
	int		chunk;
	int		retval;

	dup2(pfd[1], 1); close(pfd[1]);
	close(pfd[0]);
	
	for (chunk = 0; chunk < numchunks; chunk++) {
	    int			fd;
	    unsigned long	len, val;
	    
	    retval = stat(buf, &sb);
	    if ((retval != 0) && (prompt != TRUE))
	    {
		msgConfirm("Cannot find file(s) for distribution in ``%s''!\n", path);
		return -1;
	    } else {
		char *tmp = index(buf, '/');
		tmp++;
		    
		while (retval != 0)
		{
		    msgConfirm("Please insert the disk with the `%s' file on it\n", tmp);
		    retval = stat(buf, &sb);
		}
	    }
	    
	    snprintf(buf, 512, "%s.%c%c", path, (chunk / 26) + 'a', (chunk % 26) + 'a');
	    if ((fd = open(buf, O_RDONLY)) == -1)
		msgFatal("Cannot find file `%s'!", buf);
	    
	    if (prompt == TRUE)
	    {
		extern int crc(int, unsigned long *, unsigned long *);

		crc(fd, &val, &len);
		msgDebug("crc for %s is %lu %lu\n", buf, val, len);
	    }
	    
	    fstat(fd, &sb);
	    msgDebug("mmap()ing %s (%d)\n", buf, fd);
	    memory = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, (off_t) 0);
	    if (memory == (caddr_t) -1)
		msgFatal("mmap error: %s\n", strerror(errno));
	
	    retval = write(1, memory, sb.st_size);
	    if (retval != sb.st_size)
	    {
		msgConfirm("write didn't write out the complete file!\n(wrote %d bytes of %d bytes)", retval,
			   sb.st_size);
		exit(1);
	    }
	    
	    retval = munmap(memory, sb.st_size);
	    if (retval != 0)
	    {
		msgConfirm("munmap() returned %d", retval);
		exit(1);
	    }
	    close(fd);
	}
	close(1);
	msgDebug("Extract of %s finished!!!\n", path);
	exit(0);
    }
    close(pfd[1]);
    return(pfd[0]);
}

/* Various media "strategy" routines */

static Boolean cdromMounted;

Boolean
mediaInitCDROM(Device *dev)
{
    struct iso_args	args;
    struct stat		sb;

    if (cdromMounted)
	return TRUE;

    if (Mkdir("/cdrom", NULL))
	return FALSE;

    args.fspec = dev->devname;
    args.flags = 0;

    if (mount(MOUNT_CD9660, "/cdrom", MNT_RDONLY, (caddr_t) &args) == -1)
    {
	msgConfirm("Error mounting %s on /cdrom: %s (%u)\n", dev, strerror(errno), errno);
	return FALSE;
    }

    /* Do a very simple check to see if this looks roughly like a 2.0.5 CDROM
       Unfortunately FreeBSD won't let us read the ``label'' AFAIK, which is one
       sure way of telling the disc version :-( */
    if (stat("/cdrom/dists", &sb))
    {
	if (errno == ENOENT)
	{
	    msgConfirm("Couldn't locate the directory `dists' on the CD.\nIs this a 2.0.5 CDROM?\n");
	    return FALSE;
	} else {
	    msgConfirm("Couldn't stat directory %s: %s", "/cdrom/dists", strerror(errno));
	    return FALSE;
	}
    }
    cdromMounted = TRUE;
    return TRUE;
}

int
mediaGetCDROM(char *dist, char *path)
{
    char		buf[PATH_MAX];
    struct attribs	*dist_attr;
    int			retval;

    dist_attr = safe_malloc(sizeof(struct attribs) * MAX_ATTRIBS);

    snprintf(buf, PATH_MAX, "/stand/info/%s.inf", dist);

    if (!access(buf, R_OK) && attr_parse(&dist_attr, buf) == 0)
    {
	msgConfirm("Cannot load information file for %s distribution!\nPlease verify that your media is valid and try again.", dist);
	free(dist_attr);
	return FALSE;
    }
   
    snprintf(buf, PATH_MAX, "/cdrom/%s%s", path ? path : "", dist);

    retval = genericGetDist(buf, dist_attr, FALSE);
    free(dist_attr);
    return retval;
}

void
mediaShutdownCDROM(Device *dev)
{
    
    msgDebug("In mediaShutdownCDROM\n");
    if (getDistpid) {
	int i, j;

	i = waitpid(getDistpid, &j, 0);
	if (i < 0 || WEXITSTATUS(j)) {
	    msgConfirm("Warning: Last extraction returned status code %d.", WEXITSTATUS(j));
	    getDistpid = 0;
	}
	getDistpid = 0;
    }
    if (unmount("/cdrom", 0) != 0)
	msgConfirm("Could not unmount the CDROM: %s\n", strerror(errno));
    msgDebug("Unmount returned\n");
    cdromMounted = FALSE;
    return;
}

static Boolean floppyMounted;

Boolean
mediaInitFloppy(Device *dev)
{
    struct ufs_args ufsargs;
    char mountpoint[FILENAME_MAX];

    if (floppyMounted)
	return TRUE;
    memset(&ufsargs,0,sizeof ufsargs);

    if (Mkdir("/mnt", NULL)) {
	msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
	return FALSE;
    }
    msgDebug("initFloppy:  mount floppy %s on /mnt\n", dev->devname); 
    ufsargs.fspec = dev->devname;
    if (mount(MOUNT_MSDOS, "/mnt", 0, (caddr_t)&ufsargs) == -1) {
	msgConfirm("Error mounting floppy %s (%s) on /mnt : %s\n", dev->name,
		   dev->devname, mountpoint, strerror(errno));
	return FALSE;
    }
    floppyMounted = TRUE;
    return TRUE;
}

int
mediaGetFloppy(char *dist, char *path)
{
    char		buf[PATH_MAX];
    char		*fname;
    struct attribs	*dist_attr;
    int			retval;

    dist_attr = safe_malloc(sizeof(struct attribs) * MAX_ATTRIBS);

    snprintf(buf, PATH_MAX, "/stand/info/%s.inf", dist);
    if (!access(buf, R_OK) && attr_parse(&dist_attr, buf) == 0)
    {
	msgConfirm("Cannot load information file for %s distribution!\nPlease verify that your media is valid and try again.", dist);
	free(dist_attr);
	return FALSE;
    }
    fname = index(dist, '/') + 1;
    snprintf(buf, PATH_MAX, "/mnt/%s", fname);

    retval = genericGetDist(buf, dist_attr, TRUE);
    free(dist_attr);
    return retval;
}

void
mediaShutdownFloppy(Device *dev)
{
    if (floppyMounted) {
	if (vsystem("umount /mnt") != 0)
	    msgDebug("Umount of floppy on /mnt failed: %s (%d)\n", strerror(errno), errno);
	else
	    floppyMounted = FALSE;
    }
}

Boolean
mediaInitTape(Device *dev)
{
    return TRUE;
}

static Boolean networkInitialized;

Boolean
mediaInitNetwork(Device *dev)
{
    int i;
    char *rp;

    if (networkInitialized)
	return TRUE;

    configResolv();
    if (!strncmp("cuaa", dev->name, 4)) {
	if (!tcpStartPPP(dev)) {
	    msgConfirm("Unable to start PPP!  This installation method\ncannot be used.");
	    return FALSE;
	}
    }
    else {
	char *cp, ifconfig[64];

	snprintf(ifconfig, 64, "%s%s", VAR_IFCONFIG, dev->name);
	cp = getenv(ifconfig);
	if (!cp) {
	    msgConfirm("The %s device is not configured.  You will need to do so\nin the Networking configuration menu before proceeding.");
	    return FALSE;
	}
	i = vsystem("ifconfig %s %s", dev->name, cp);
	if (i) {
	    msgConfirm("Unable to configure the %s interface!\nThis installation method cannot be used.", dev->name);
	    return FALSE;
	}
    }

    rp = getenv(VAR_GATEWAY);
    if (!rp)
	msgConfirm("No gateway has been set. You will not be able to access hosts\n
not on the local network\n");
    else
	vsystem("route add default %s", rp);
    networkInitialized = TRUE;
    return TRUE;
}

int
mediaGetTape(char *dist, char *path)
{
    return -1;
}

void
mediaShutdownTape(Device *dev)
{
    return;
}

void
mediaShutdownNetwork(Device *dev)
{
    char *cp;

    if (!networkInitialized)
	return;

    if (!strncmp("cuaa", dev->name, 4)) {
	msgConfirm("You may now go to the 3rd screen (ALT-F3) and shut down\nyour PPP connection.  It shouldn't be needed any longer\n(unless you wish to create a shell by typing ESC and\nexperiment with it further, in which case go right ahead!)");
	return;
    }
    else {
	int i;
	char ifconfig[64];

	snprintf(ifconfig, 64, "%s%s", VAR_IFCONFIG, dev->name);
	cp = getenv(ifconfig);
	if (!cp)
	    return;
	i = vsystem("ifconfig %s down", dev->name);
	if (i)
	    msgConfirm("Warning: Unable to down the %s interface properly", dev->name);
    }

    cp = getenv(VAR_GATEWAY);
    if (cp)
	vsystem("route delete default");
    networkInitialized = FALSE;
}

static FTP_t ftp;

Boolean
mediaInitFTP(Device *dev)
{
    int i;
    char *url, *hostname, *dir;
    char *my_name, email[BUFSIZ];
    Device *netDevice = (Device *)dev->private;

    if (netDevice->init)
	if (!(*netDevice->init)(netDevice))
	    return FALSE;

    if ((ftp = FtpInit()) == NULL) {
	msgConfirm("FTP initialisation failed!");
	return FALSE;
    }

    url = getenv("ftp");
    if (!url)
	return FALSE;
    my_name = getenv(VAR_HOSTNAME);
    if (strncmp("ftp://", url, 6) != NULL) {
	msgConfirm("Invalid URL (`%s') passed to FTP routines!\n(must start with `ftp://')", url);
	return FALSE;
    }

    msgDebug("Using URL `%s'\n", url);
    hostname = url + 6;
    if ((dir = index(hostname, '/')) != NULL)
	*(dir++) = '\0';
    strcpy(dev->name, hostname);
    msgDebug("hostname = `%s'\n", hostname);
    msgDebug("dir = `%s'\n", dir ? dir : "/");
    msgNotify("Looking up host %s..", hostname);
    if ((gethostbyname(hostname) == NULL) && (inet_addr(hostname) == INADDR_NONE)) {
	msgConfirm("Cannot resolve hostname `%s'!  Are you sure your name server\nand/or gateway values are set properly?", hostname);
	return FALSE;
    }

    snprintf(email, BUFSIZ, "installer@%s", my_name);
    msgDebug("Using fake e-mail `%s'\n", email);

    msgNotify("Logging in as anonymous.");
    if ((i = FtpOpen(ftp, hostname, "anonymous", email)) != 0) {
	msgConfirm("Couldn't open FTP connection to %s: %s (%u)\n", hostname, strerror(i), i);
	return FALSE;
    }

    if (getenv("ftpPassive"))
	FtpPassive(ftp, 1);
    FtpBinary(ftp, 1);
    if (dir && *dir != '\0') {
	msgNotify("CD to distribution in ~ftp/%s", dir);
	FtpChdir(ftp, dir);
    }
    msgDebug("leaving mediaInitFTP!\n");
    return TRUE;
}

static pid_t ftppid = 0;

int
mediaGetFTP(char *dist, char *path)
{
    int 	fd;
    char 	buf[512];
    int		pfd[2], numchunks;
    const char *tmp;
    struct attribs	*dist_attr;

    if (!path)
	path = "";
    msgNotify("Attempting to retreive `%s' over FTP", dist);
    snprintf(buf, PATH_MAX, "/stand/info/%s%s.inf", path, dist);
    if (!access(buf, R_OK)) {
	msgDebug("Parsing attributes file for %s\n", dist);
	dist_attr = safe_malloc(sizeof(struct attribs) * MAX_ATTRIBS);
	if (attr_parse(&dist_attr, buf) == 0) {
	    msgConfirm("Cannot load information file for %s distribution!\nPlease verify that your media is valid and try again.", dist);
	    return -1;
	}
   
	msgDebug("Looking for attribute `pieces'\n");
	tmp = attr_match(dist_attr, "pieces");
	numchunks = atoi(tmp);
    }
    else
	numchunks = 0;
    msgDebug("Attempting to extract distribution from %u files\n", numchunks ? numchunks : 1);

    /* Take the lack of an info file to mean we're a fully qualified name */
    if (!numchunks) {
	sprintf(buf, "%s%s", path, dist);
	return(FtpGet(ftp, buf));
    }
    else if (numchunks == 1) {
	snprintf(buf, 512, "%s%s.aa", path, dist);
	return(FtpGet(ftp, buf));
    }

    /* reap the previous child corpse - yuck! */
    if (ftppid) {
	int i, j;

	i = waitpid(ftppid, &j, 0);
	if (i < 0 || WEXITSTATUS(j)) {
	    msgConfirm("Previous FTP transaction returned status code %d - aborting\ntransfer.", WEXITSTATUS(j));
	    ftppid = 0;
	    return -1;
	}
	ftppid = 0;
    }
    pipe(pfd);
    ftppid = fork();
    if (!ftppid) {
	int		chunk;
	int		retval;

	dup2(pfd[1], 1); close(pfd[1]);
	close(pfd[0]);
	
	for (chunk = 0; chunk < numchunks; chunk++) {
	    char buffer[10240];
	    int n;
	    
	    snprintf(buf, 512, "%s%s.%c%c", path, dist, (chunk / 26) + 'a', (chunk % 26) + 'a');
	    fd = FtpGet(ftp, buf);

	    if (fd < 0)
	    {
		msgConfirm("FtpGet failed to retreive piece `%s' in the %s distribution!\nAborting the transfer", chunk, dist);
		exit(1);
	    }
	    
	    while ((n = read(fd, buffer, 10240))>0)
	    {
		retval = write(1, buffer, n);
		if (retval != n)
		{
		    msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", retval, n);
		    exit(1);
		}
		
	    }
	    FtpEOF(ftp);
	}
	close(1);
	msgDebug("Extract of %s finished with success!!!\n", dist);
	exit(0);
    }
    close(pfd[1]);
    return(pfd[0]);
}

void
mediaShutdownFTP(Device *dev)
{
    Device *netdev = (Device *)dev->private;

    if (ftp != NULL) {
	FtpClose(ftp);
	ftp = NULL;
    }
    if (ftppid) {
	int i, j;

	i = waitpid(ftppid, &j, 0);
	if (i < 0 || WEXITSTATUS(j))
	    msgConfirm("Warning: Last FTP transaction returned status code %d.", WEXITSTATUS(j));
	ftppid = 0;
    }
    if (netdev->shutdown)
	(*netdev->shutdown)(netdev);
}

Boolean
mediaInitUFS(Device *dev)
{
    return TRUE;
}

int
mediaGetUFS(char *dist, char *path)
{
    return -1;
}

/* UFS has no Shutdown routine since this is handled at the device level */


Boolean
mediaInitDOS(Device *dev)
{
    return TRUE;
}

int
mediaGetDOS(char *dist, char *path)
{
    return -1;
}

void
mediaShutdownDOS(Device *dev)
{
}