69df29fff8
use them yet, but it's close (we're working on the last wrinkles in the CD install for now). 2. Complete the CDROM installation strategy code. 3. Simplify the distribtuion loading code. 4. General error message cleanup. 5. Write the /etc/fstab file now and split those routines into config.c 6. Clean up the menus a little more.
521 lines
11 KiB
C
521 lines
11 KiB
C
/*
|
|
* 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.11 1995/05/22 14:10:23 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 <netdb.h>
|
|
#include "libftp.h"
|
|
|
|
#define MSDOSFS
|
|
#define CD9660
|
|
#define NFS
|
|
#include <sys/mount.h>
|
|
#undef MSDOSFS
|
|
#undef CD9660
|
|
#undef NFS
|
|
|
|
#define MAX_ATTRIBS 20
|
|
#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((strcmp(attr[n].name, name)!=0) && (n < num_attribs) && (n < 20))
|
|
n++;
|
|
|
|
if (strcmp(attr[n].name, name)==0)
|
|
return((const char *) attr[n].value);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
genericGetDist(char *path, struct attribs *dist_attrib)
|
|
{
|
|
int fd;
|
|
char buf[512];
|
|
struct stat sb;
|
|
int pfd[2], pid, numchunks;
|
|
const char *tmp;
|
|
|
|
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)
|
|
{
|
|
msgConfirm("Cannot find file(s) for distribution in ``%s''!\n", path);
|
|
return -1;
|
|
}
|
|
|
|
tmp = attr_match(dist_attrib, "pieces");
|
|
numchunks = atoi(tmp);
|
|
msgDebug("Attempting to extract distribution from %u files\n", numchunks);
|
|
pipe(pfd);
|
|
pid = fork();
|
|
if (!pid)
|
|
{
|
|
caddr_t memory;
|
|
int chunk = 0;
|
|
int retval;
|
|
|
|
dup2(pfd[1], 1); close(pfd[1]);
|
|
close(pfd[0]);
|
|
|
|
while (chunk < numchunks)
|
|
{
|
|
int fd;
|
|
|
|
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'!\n", buf);
|
|
|
|
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)\n", retval, sb.st_size);
|
|
exit(1);
|
|
}
|
|
|
|
retval = munmap(memory, sb.st_size);
|
|
if (retval != 0)
|
|
{
|
|
msgConfirm("munmap() returned %d\n", retval);
|
|
exit(1);
|
|
}
|
|
close(fd);
|
|
++chunk;
|
|
}
|
|
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 cdrom\n\
|
|
Is 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 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 (attr_parse(&dist_attr, buf) == 0)
|
|
{
|
|
msgConfirm("Cannot load information file for distribution\n");
|
|
return FALSE;
|
|
}
|
|
|
|
snprintf(buf, PATH_MAX, "/cdrom/dists/%s", dist);
|
|
|
|
retval = genericGetDist(buf, dist_attr);
|
|
free(dist_attr);
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
mediaCloseCDROM(Device *dev)
|
|
{
|
|
msgDebug("In mediaCloseCDROM\n");
|
|
if (unmount("/cdrom", 0) != 0)
|
|
msgConfirm("Could not unmount the CDROM: %s\n", strerror(errno));
|
|
msgDebug("Unmount returned\n");
|
|
cdromMounted = FALSE;
|
|
return;
|
|
}
|
|
|
|
Boolean
|
|
mediaInitFloppy(Device *dev)
|
|
{
|
|
if (Mkdir("/mnt", NULL))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
mediaGetFloppy(char *dist)
|
|
{
|
|
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 (attr_parse(&dist_attr, buf) == 0)
|
|
{
|
|
msgConfirm("Cannot load information file for distribution\n");
|
|
return FALSE;
|
|
}
|
|
|
|
snprintf(buf, PATH_MAX, "/mnt/%s", dist);
|
|
|
|
retval = genericGetDist(buf, dist_attr);
|
|
free(dist_attr);
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
mediaCloseFloppy(Device *dev)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Boolean
|
|
mediaInitTape(Device *dev)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
Boolean
|
|
mediaInitNetwork(Device *dev)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
mediaGetTape(char *dist)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
mediaCloseTape(Device *dev)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void
|
|
mediaCloseNetwork(Device *dev)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static FTP_t ftp;
|
|
|
|
Boolean
|
|
mediaInitFTP(Device *dev)
|
|
{
|
|
int i;
|
|
char *url, *hostname, *dir, *dir_p;
|
|
char *my_name, email[BUFSIZ];
|
|
|
|
if ((ftp = FtpInit()) == NULL) {
|
|
msgConfirm("FTP initialisation failed!");
|
|
return FALSE;
|
|
}
|
|
|
|
url = getenv("ftp");
|
|
if (!url)
|
|
return FALSE;
|
|
if (!strcmp(url, "other")) {
|
|
url = msgGetInput(NULL, "Please specify the URL of a FreeBSD distribution on a\nremote ftp site. This site must accept anonymous 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;
|
|
}
|
|
|
|
hostname = url + 6;
|
|
dir = index(hostname, '/');
|
|
*(dir++) = '\0';
|
|
if (gethostbyname(hostname) == NULL) {
|
|
msgConfirm("Cannot resolve hostname `%s'!\n", hostname);
|
|
return FALSE;
|
|
}
|
|
|
|
snprintf(email, BUFSIZ, "installer@%s", my_name);
|
|
|
|
if ((i = FtpOpen(ftp, hostname, "anonymous", email)) != 0) {
|
|
msgConfirm("Couldn't open FTP connection to %s (%u)\n", strerror(i), i);
|
|
return FALSE;
|
|
}
|
|
|
|
if (getenv("ftpPassive"))
|
|
FtpPassive(ftp, 1);
|
|
FtpBinary(ftp, 1);
|
|
FtpChdir(ftp, "/");
|
|
while ((dir_p = index(dir, '/')) != NULL) {
|
|
*dir_p = '\0';
|
|
FtpChdir(ftp, dir);
|
|
dir = ++dir_p;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
mediaGetFTP(char *dist)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
mediaCloseFTP(Device *dev)
|
|
{
|
|
}
|
|
|
|
Boolean
|
|
mediaInitUFS(Device *dev)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
mediaGetUFS(char *dist)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/* UFS has no close routine since this is handled at the device level */
|
|
|
|
|
|
Boolean
|
|
mediaInitDOS(Device *dev)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
mediaGetDOS(char *dist)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
mediaCloseDOS(Device *dev)
|
|
{
|
|
}
|