1
0
Files
2022-09-29 17:59:04 +03:00

586 lines
16 KiB
C

#ident "sash/main.c: $Revision: 1.114 $"
/*
* main.c -- main line sash code
*/
#include <arcs/errno.h>
#include <arcs/io.h>
#include <saioctl.h>
#include <stringlist.h>
#include <parser.h>
#include <setjmp.h>
#include <gfxgui.h>
#include <string.h>
#include <arcs/restart.h>
#include <arcs/signal.h>
#include <arcs/io.h>
#include <arcs/pvector.h>
#include <arcs/spb.h>
#include <libsc.h>
extern jmp_buf restart_buf;
#define PROMPT "sash: "
static char *fixuppath(char *);
static int version(int, char **, char **, struct cmd_table *);
static void main_intr (void);
static void sash_quit(char *, char *, LONG);
static LONG sash_autoboot(int, char **, char **);
static char *kernel_name(char *, char *);
static void adjustmrargs(int, char **, int, int *);
int setup_specialswap_vh(void);
extern void set_vhchksum(struct volume_header *);
extern void dump_vh(struct volume_header *, char *);
/*
* commands
*/
#ifdef DEBUG
extern int mcopy(), mcmp(), mfind(), memlist();
extern int dev_test(), checksum();
#endif
extern int modem_init (int argc, char **argv);
/*
* cmd_table -- interface between parser and command execution routines
* Add new commands by making entry here.
*/
static struct cmd_table cmd_table[] = {
{ "boot", boot, "boot:\t\tboot [-f FILE] [-n] [ARGS]" },
{ "cat", cat, "cat:\t\tcat FILE_LIST" },
{ "cp", copy, "copy:\t\tcp [-b BLOCKSIZE] [-c BCOUNT] SRC_FILE DST_FILE" },
{ "go", go_cmd, "go:\t\tgo [INITIAL_PC]" },
{ "help", help, "help:\t\thelp [COMMAND]" },
{ "?", help, "help:\t\t? [COMMAND]" },
{ "hinv", hinv, "inventory:\thinv [-v] [-t [-p]]" },
{ "install", mrboot, "install:\tinstall" },
{ "ls", ls, "ls:\t\tls DIRECTORY|DEVICE" },
{ "printenv", printenv_cmd, "printenv:\tprintenv [ENV_VAR_LIST]" },
{ "setenv", setenv_cmd, "setenv:\t\tsetenv ENV_VAR STRING" },
{ "unsetenv", unsetenv_cmd, "unsetenv:\tunsetenv ENV_VAR" },
{ "version", version, "version:\tversion" },
#ifdef DEBUG
{ "checksum", checksum, "checksum:\tchecksum (RANGE | -f FILE)" },
{ "dev", dev_test, "device test:\t\tdev [-d pattern] [-w write addr] [-r read addr] -b blkcnt TEST_DEVICE" },
{ "mcmp", mcmp, "mcmp:\t\tmcmp [-(b|h|w)] FROMRANGE TO" },
{ "mcopy", mcopy, "mcopy:\t\tmcopy [-(b|h|w)] FROMRANGE TO" },
{ "mfind", mfind, "mfind:\t\tmfind [-(b|h|w)] [-n] RANGE VALUE" },
{ "mlist", memlist, "mlist:\t\tmlist" },
#endif
{ 0, 0, "" }
};
extern void sdvh_install(void);
extern void mr_setinst_state(int);
extern int mr_interrupted_inst(int, int);
extern int mr_iscustom(int, char**);
/*
* main -- called from csu after data area has been cleared
*/
int
main(int argc, char **argv, char **envp)
{
size_t s;
LONG rc;
int modemstat, domrb, mrspecial=0, mrok=0, dovhsetup=0, i;
char *osopts;
updatetv(); /* check for backlevel spb tv */
rbclrbs(BS_BSTARTED); /* clear boot started flag (for ARC) */
if(getenv("DEBUG")) { /* mostly for partitioning debugging */
atob(getenv("DEBUG"),&Debug);
if(!Debug)
Debug = 1;
}
_init_malloc(); /* initialize malloc for efs_install */
efs_install(); /* install EFS */
xfs_install(); /* install XFS */
sdvh_install(); /* install volhdr filesystem with rewrite */
/* We add modem initialization / remote diagnostics capability here.
* The exit status from this call tells what to do next: if "0",
* either there was no modem, or there was a modem but boot
* is to proceed normally. If "1", then there was a modem and
* sash is to exit back to the interactive prompt. If "2"
* (or greater) then sash is to ignore the boot file and continue
* to its command interpreter.
*/
modemstat = modem_init (argc, argv); /* add modem hook here */
initGfxGui(0); /* we use gui for install */
ioctl(StandardIn,TIOCSETXOFF,0);
/*
* Deal with autoboots, remote debugging boots, and two-level
* boots. fix up path first, so that one can just say
* 'boot unix' from the PROM or 'unix' from sash and have it work.
*/
*argv = fixuppath(*argv);
ioctl (0, TIOCINTRCHAR, (long)"\003\033"); /* ^C and ESC */
/* see if we're in a special miniroot mode */
mrspecial = mr_iscustom(argc-1, argv+1);
if(mrspecial)
for (i=1; i<argc; ++i)
if (!strcmp(argv[i], "disksetup=true")) {
dovhsetup = 1;
break;
}
/* avoid doing this check in two places */
domrb = argc>1 && (!strcmp(argv[1], "OSLoadOptions=mini") ||
!strcmp(argv[1], "-m") || mrspecial);
/* see if we either had an interrupted miniroot install (INST*),
* or completed a miniroot inst normally (inst*) and do the right
* thing in either case. See mrboot_cmd.c for guts of it */
if(osopts=getenv("OSLoadOptions")) {
if(!strncmp("INST", osopts, 4)) {
if(!domrb) switch(mr_interrupted_inst(0,mrspecial)) {
case 0: goto alldone;
case -1: mrok=1; /* so autoboot can continue after fixup */
break;
}
}
else if(!strncmp("inst", osopts, 4)) {
mr_setinst_state(1); /* reset inststate back to normal */
mrok = 1; /* done because prom will not set OSLoadOptions
if already set, so this lets us fake it... worse yet,
it doesn't put the OSLoadOptions=xxx in the same position
if the prom thinks it was set explictly... */
}
}
/* Hook for sash to be installed on the volume header as
* ide to load /stand/ide for machines that do not know
* how to do a two level boot for ide.
*/
if (argv[0] && (s = strlen(argv[0])) &&
(strcmp(argv[0]+s-3,"ide") == 0)) {
char *s = *argv;
char *p;
*argv = kernel_name(s,"/usr/stand/ide");
rc = sash_autoboot(argc, argv, envp);
/* get here iff sash_autoboot failed
* if the partition ends in a 0, i.d. dksc(0,1,0)
* then check for dksc(0,1,6), i.e. change the
* last 0 into a 6 and see if that is bootable
*/
s = *argv = kernel_name(s,"/stand/ide");
for(p = s + strlen(s) - 1; p != s; p--) {
if(*p == '0') {
*p = '6';
break;
}
}
rc = sash_autoboot(argc, argv, envp);
sash_quit("IDE boot failed",argv[0],rc);
}
if (argc >= 2) {
if(modemstat == 0) { /* normal */
if (argv[0] && (s = strlen(argv[0])) &&
strcmp(argv[0]+s-4, "unix") == 0) {
/* diskless boot */
*argv = kernel_name(*argv, "unix.auto");
rc = sash_autoboot(argc, argv, envp);
sash_quit("Autoboot failed",*argv,rc);
} else if (!strcmp(argv[1], "OSLoadOptions=auto") ||
!strcmp(argv[1], "-a") ||
((osopts=getenv("OSLoadOptions"))
&& !strcmp(osopts,"auto")) ) {
/* check vh for custom boot file for auto mr */
if (do_custom_boot(argc-2,&argv[2]) != 0)
sash_quit("Custom boot failed",0,0);
else {
autoboot: /* autoboot */
*argv = kernel_name(*argv,0);
rc = sash_autoboot(argc, argv, envp);
sash_quit("Autoboot failed",*argv,rc);
}
} else if (domrb) {
int argoffset;
if(dovhsetup && setup_specialswap_vh())
sash_quit(0,0,0);
adjustmrargs(argc,argv,mrspecial,&argoffset);
/* miniroot boot */
mrboot(argc-argoffset, &argv[argoffset], argv, 0);
/* mrboot prints error message */
sash_quit(0,0,0);
} else if ((strcmp(argv[1], "OSLoadOptions=remote") == 0) ||
(strcmp(argv[1], "-r") == 0)) {
/* remote debugging */
setenv("dbgmon", "1");
setenv("rdebug", "1");
*argv = kernel_name(*argv,0);
rc = sash_autoboot(argc, argv, envp);
sash_quit("Remote debug boot failed",*argv,rc);
} else if ((argv[1][0] != '-') && !index(argv[1],'=')) {
/* boot passed filename */
rc = sash_autoboot(argc-1, &argv[1], envp);
sash_quit("Autoboot failed",argv[1],rc);
}
else if(mrok)
goto autoboot; /* reboot after mr isntall */
}
else if(modemstat == 1) {
/* since we are aborting a normal boot here, we need to
* make sure that this path can not be taken indefinitely.
* We do this by limiting this exit to the case that sash
* was invoked by autoboot or with a boot file name.
* This will allow the direct invocation of sash from the
* interactive menu, which can then be used to boot UNIX.
*/
if ((strcmp(argv[1], "OSLoadOptions=auto") == 0) ||
(strcmp(argv[1], "-a") == 0) ||
((osopts=getenv("OSLoadOptions"))
&& !strcmp(osopts,"auto")) ) {
/* autoboot */
sash_quit (NULL, NULL, 0);
}
else if ((argv[1][0] != '-') && !index(argv[1],'=')) {
/* boot passed filename */
sash_quit (NULL, NULL, 0);
}
}
/* else we will fall through to sash command mode */
}
/* NO flags and no osloadfilename set in argv - go to cmd mode */
ioctl (0, TIOCINTRCHAR, (long)"\003"); /* ^C only */
if ( getenv("VERBOSE") )
atob(getenv("VERBOSE"),&Verbose);
else
Verbose = 1;
version(argc, argv, argv, 0); /* these args are all dummy */
if (setjmp(restart_buf)) {
putchar('\n');
close_noncons(); /* close leftover fd's */
}
Signal (SIGINT, main_intr);
command_parser(cmd_table, PROMPT, 1, 0);
alldone:
putchar('\n');
fs_unregister("efs");
fs_unregister("xfs");
return(0);
}
/*
* where sash goes on a sigint
*/
static void
main_intr (void)
{
if (Verbose)
longjmp (restart_buf, 1);
else {
fs_unregister("efs");
EnterInteractiveMode();
}
}
/*
* prepend a complete pathname onto a path such as "sash"
*/
static char *
fixuppath(char *path)
{
static char newpath[2*64+1];
char *p, *cp;
/* if we didn't find a '(', then prepend the default path to path */
if (!(cp=rindex(path, '('))) {
/* get a copy of the current default boot device path */
cp = path;
if (!(p = getenv("SystemPartition")))
return (path);
strcpy (newpath, p);
strcat (newpath, cp);
return (newpath);
} else
return (path);
}
static void
sash_quit(char *string, char *file, LONG errno)
{
static int sq_buttons[] = {DIALOGCONTINUE,DIALOGPREVDEF,-1};
char buf[240];
char fbuf[160]; /* kernel_name assumes file can be 128 */
if (string) {
if (file)
sprintf(fbuf,"\n%s: %s",file,arcs_strerror(errno));
else
*fbuf = '\0';
if (isGuiMode()) {
setGuiMode(1,0);
sprintf(buf,"%s.%s",string,fbuf);
}
else
sprintf(buf,"\n\n%s.%s.\n\nHit Enter to continue.",
string,fbuf);
popupDialog(buf,sq_buttons,DIALOGWARNING,DIALOGCENTER);
}
fs_unregister("efs");
EnterInteractiveMode();
}
static LONG
sash_autoboot(int argc, char **argv, char **envp)
{
setenv ("kernname", *argv);
return(Execute(*argv, (LONG)argc, argv, envp));
}
/*ARGSUSED*/
static int
version(int a, char **b, char **c, struct cmd_table *d)
{
extern char *getversion(void);
#if _MIPS_SIM == _ABI64
char *size = "64";
#else
char *size = "32";
#endif
printf("Standalone Shell %s (%s Bit)\n", getversion(), size);
return 0;
}
/*
* kernel_name - construct a rational path name for the kernel given the file
* that sash was loaded from. The sash path name should have been "fixed up"
* by calling fixuppath before passing it here.
*/
static char *
kernel_name(char *oboot, char *name)
{
char *cp, *bp;
int cnt = 0;
static char bootfile[128];
size_t s;
bp = bootfile;
/*
* If sash was booted off of the net, replace "sash" in the
* boot string with "unix" and try to autoboot unix off of
* the net.
*
* For diskless boots, we may have been invoked as "...unix".
*/
if (!strncmp("bootp", oboot, 5) || !strncmp("net", oboot, 3)) {
cp = oboot;
cp = &cp[strlen(cp)];
if (name) {
while (cp > oboot && *cp != ')' && *cp != ':' &&
*cp != '/') {
cnt++; cp--;
}
} else {
while (cp > oboot && *cp != ')' && *cp != ':') {
cnt++; cp--;
}
}
cnt--;
if ((cnt = ((int)strlen(oboot)-cnt)) <= 0) {
printf("bad sash filename format, can't load kernel\n");
return NULL;
}
strncpy (bp, oboot, cnt);
if (name)
strcat (bp, name);
else
strcat (bp, getenv("OSLoadFilename"));
/*
* If sash was booted off of the disk, get the unix partition and
* filename out of the volume header.
*/
} else {
strcpy (bp, getenv("OSLoadPartition"));
if (name)
strcat (bp, name);
else
strcat (bp, getenv("OSLoadFilename"));
}
return(bootfile);
}
/* adjust argc/argv arguments to be used when booting the
miniroot kernel. In the special miniroot modes, we need to pass
the mrmode nvram variable to the kernel. Also, if we are in
a special miniroot mode, try to derive tapedevice if it is not
already set since we want the install to be non interactive */
static void
adjustmrargs(int argc, char **argv, int special, int *argoffset)
{
*argoffset = 2;
if (special) {
/* don't require user to have to set tapedevice in prom */
if (argc > 0 && getenv("tapedevice") == NULL) {
char taped[256], *ptr, *bp;
*taped = '\0';
/* user accessed sash from the sa so just remove (sashxxxx) */
if (argv[0][strlen(argv[0])-1] == ')' &&
(ptr=rindex(argv[0],'('))) {
strncpy(taped,argv[0],ptr-argv[0]);
taped[ptr-argv[0]] = '\0';
} else if (bp = strstr(argv[0],"bootp()")) {
/* sash is separate program; replace sash with sa */
if ((ptr=rindex(argv[0],'/')) == NULL)
if ((ptr = rindex(argv[0],':')) == NULL)
ptr=bp+6; /* to the ')' */
if (ptr != NULL) {
strncpy(taped,argv[0],ptr-argv[0]+1);
strcpy(taped+(ptr-argv[0]+1),"sa");
}
}
if (*taped)
setenv("tapedevice",taped);
}
(*argoffset)--; /* need to pass mrmode to the kernel */
}
/* just in case mrmode was previously set in env */
setenv("mrmode","");
}
#ifdef DEBUG /* so ASSERT's can be used */
int doass = 1;
void
assfail(char *a, char *f, char *l)
{
panic("assertion failed: %s, file: %s, line: %d", a, f, l);
}
#endif
/* open the SystemPartition device, and if swap doesn't immediately
* follow the volhdr, create a "standard" irix 6.5 root filesystem.
*/
#include <sys/dvh.h>
#include <sys/dkio.h>
#define VH_AMT 4096 /* read size in bytes for vhdr; allows for multiple
* block sizes of devices; avoids problems with cdrom, etc. */
dopart(char *part, ULONG fd)
{
struct volume_header *vh = calloc(VH_AMT, 1);
LONG rval;
ULONG cc, cc2;
LARGE offset;
struct partition_table *pt = vh->vh_pt;
if(!vh) {
printf("unable to malloc space for volume header\n");
return 1;
}
if((rval = Read (fd, vh, VH_AMT, &cc)) || cc<sizeof(*vh) || !is_vh(vh)) {
unsigned drivecap = 0;
printf("%s: invalid partition table, initializing disk\n",
part);
cc = VH_AMT; /* for write below */
bzero(vh, cc);
pt[8].pt_nblks = PTYPE_VOLHDR_DFLTSZ; /* same as fx; from dvh.h */
pt[8].pt_firstlbn = 0;
pt[8].pt_type = PTYPE_VOLHDR;
pt[0].pt_nblks = pt[1].pt_nblks = 0; /* force setup below */
pt[10].pt_firstlbn = 0;
pt[10].pt_type = PTYPE_VOLUME;
/* if this fails, we are sort of hosed... */
(void)ioctl(fd, (long)DIOCREADCAPACITY, (LONG)&drivecap);
vh->vh_dp.dp_drivecap = pt[10].pt_nblks = drivecap;
vh->vh_swappt = 1;
vh->vh_dp.dp_secbytes = 512; /* should get from driver at some point */
strcpy(vh->vh_bootfile, "/unix");
}
/* if swap (part 1) doesn't immediately follow vh, or
* part 1 is < 40MB, then force a partitioning. Note that
* we pay no attention to swapdev or anything else. When
* this option is invoked, it's based entirely on
* systempartition, which is OK, since there is a special
* option that invokes this behavior, and it's normally only
* going to be invoked by roboinst when requested.
*/
if(pt[1].pt_firstlbn != pt[8].pt_nblks ||
pt[1].pt_nblks < 102400) {
pt[8].pt_nblks = PTYPE_VOLHDR_DFLTSZ; /* same as fx; from dvh.h */
pt[8].pt_firstlbn = 0;
pt[8].pt_type = PTYPE_VOLHDR;
pt[1].pt_firstlbn = pt[8].pt_nblks;
pt[1].pt_nblks = 102400;
pt[1].pt_type = PTYPE_RAW;
pt[0].pt_firstlbn = pt[1].pt_firstlbn + pt[1].pt_nblks;
pt[0].pt_nblks = pt[10].pt_nblks - pt[0].pt_firstlbn;
pt[0].pt_type = PTYPE_XFS;
set_vhchksum(vh);
offset.hi = 0; offset.lo = 0;
if(rval=ioctl(fd, (long)DIOCSETVH, (LONG)vh))
printf("failed to set new partition table in driver: error %ld\n", rval);
if(Seek(fd, &offset, SeekAbsolute) ||
(rval = Write(fd, vh, cc, &cc2))!=ESUCCESS)
printf("failed to write new partition table to disk: error %ld\n", rval);
if(Debug)
dump_vh(vh, "new partition table for miniroot");
}
else if(Debug)
printf("partitions already OK, not changed\n");
(void)free(vh);
(void)Close(fd);
return rval;
}
setup_specialswap_vh()
{
ULONG fd;
LONG rval;
char *part, *s = "SystemPartition";
part = getenv(s);
if(!part) {
printf("%s not set, can't repartition for miniroot\n", s);
return 1;
}
rval = Open (part, OpenReadWrite, &fd);
if (rval != ESUCCESS) {
printf ("Unable to open %s %s to repartition: error %ld\n", s, part, rval);
return 1;
}
return dopart(part, fd);
}