freebsd-skq/usr.bin/doscmd/doscmd.c
John Dyson 90c38ed200 Add our doscmd to the tree. This is a result of work from BSDI, and
a group of dos emulator developers.
Submitted by:	Jonathan Lemon <jlemon@americantv.com>
Obtained from:	BSDI
1997-08-09 01:43:15 +00:00

893 lines
18 KiB
C

/*
* Copyright (c) 1992, 1993, 1996
* Berkeley Software Design, Inc. 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.
* 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 Berkeley Software
* Design, Inc.
*
* THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``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 Berkeley Software Design, Inc. 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, 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.
*
* BSDI doscmd.c,v 2.3 1996/04/08 19:32:30 bostic Exp
*
* $Id: doscmd.c,v 1.10 1997/03/18 02:36:55 msmith Exp $
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <errno.h>
#include <limits.h>
#include <pwd.h>
#include <signal.h>
#include <unistd.h>
#include <machine/param.h>
#include <machine/vmparam.h>
#include <sys/proc.h>
#include <machine/sysarch.h>
#include <machine/vm86.h>
#include "doscmd.h"
/* exports */
int capture_fd = -1;
int dead = 0;
int xmode = 0;
int booting = 0;
int raw_kbd = 0;
int timer_disable = 0;
struct timeval boot_time;
unsigned long *ivec = (unsigned long *)0;
u_long pending[256]; /* pending interrupts */
int n_pending;
#ifndef USE_VM86
#define PRB_V86_FORMAT 0x4242
struct vconnect_area vconnect_area = {
0, /* Interrupt state */
PRB_V86_FORMAT, /* Magic number */
{ 0, }, /* Pass through ints */
{ 0x00000000, 0x00000000 } /* Magic iret location */
};
#endif
/* local prototypes */
static void setup_boot(regcontext_t *REGS);
static void setup_command(int argc, char *argv[], regcontext_t *REGS);
static FILE *find_doscmdrc(void);
static int do_args(int argc, char *argv[]);
static void usage(void);
static int open_name(char *name, char *ext);
static void init_iomap(void);
/* Local option flags &c. */
static int zflag = 0;
/* DOS environment emulation */
static int ecnt = 0;
static char *envs[256];
/* Search path and command name */
static char *dos_path = 0;
char cmdname[256]; /* referenced from dos.c */
/* high memory mapfile */
static char *memfile = "/tmp/doscmd.XXXXXX";
static struct i386_vm86_args vm86;
static struct vm86_init_args kargs;
/* lobotomise */
int
main(int argc, char **argv)
{
#ifndef USE_VM86
struct sigcontext sc;
#else
struct vm86_struct vm86s;
#define sc vm86s.substr.regs.vmsc
#endif
regcontext_t *REGS = (regcontext_t *)&sc;
int fd;
int i;
char buffer[4096];
int mfd;
FILE *fp;
/* XXX should only be for tty mode */
fd = open ("/dev/null", O_RDWR);
if (fd != 3)
dup2 (fd, 3); /* stdaux */
if (fd != 4)
dup2 (fd, 4); /* stdprt */
if (fd != 3 && fd != 4)
close (fd);
fd = -1;
debug_set(0); /* debug any D_TRAPS without intnum */
/* perform option argument processing */
optind = do_args(argc, argv);
argc -= optind;
argv += optind;
if (vflag && debugf == stderr) {
debugf = stdout;
setbuf (stdout, NULL);
}
mfd = mkstemp(memfile);
if (mfd < 0) {
fprintf(stderr, "memfile: %s\n", strerror(errno));
fprintf(stderr, "High memory will not be mapped\n");
} else {
caddr_t add;
unlink(memfile);
mfd = squirrel_fd(mfd);
lseek(mfd, 64 * 1024 - 1, 0);
write(mfd, "", 1);
add = mmap((caddr_t)0x000000, 64 * 1024,
PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED,
mfd, 0);
add = mmap((caddr_t)0x100000, 64 * 1024,
PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED,
mfd, 0);
}
/* This needs to happen before the executable is loaded */
mem_init();
#ifdef USE_VM86
memset(&vm86s, 0, sizeof(vm86s));
#endif
/*
* With no other arguments we will assume we must boot DOS
*/
if (argc <= 0)
booting = 1;
#if 1
/*
* Nominate interrupts to handle here when the kernel is
* performing interrupt handling.
*
* I would like to let INT 2F pass through as well, but I
* need to get my hands on INT 2F:11 to do file redirection.
*/
for (i = 0; i <= 0xff; ++i) {
switch (i) {
case 0x2f:
case 0xff:
#if 1
kargs.int_map[i >> 3] |= (1 << (i & 7));
#ifndef USE_VM86
vconnect_area.passthru[i >> 5] &= ~(1 << (i & 0x1f));
#else
vm86s.int_byuser[i >> 3] |= (1 << (i & 0x07));
#endif
#endif
break;
default:
#if 1
kargs.int_map[i >> 3] &= ~(1 << (i & 7));
#ifndef USE_VM86
vconnect_area.passthru[i >> 5] |= (1 << (i & 0x1f));
#else
vm86s.int_byuser[i >> 3] |= (1 << (i & 0x07));
#endif
#endif
break;
}
}
#endif
for (i = 0; i < 256; i++)
pending[i] = 0;
n_pending = 0;
if (booting) { /* are we booting? */
setup_boot(REGS);
} else { /* no, load a command */
setup_command(argc, argv, REGS);
}
/* install signal handlers */
setsignal (SIGFPE, sigfpe); /* */
setsignal (SIGALRM, sigalrm); /* */
setsignal (SIGILL, sigill); /* */
setsignal (SIGTRAP, sigtrap); /* */
setsignal (SIGUSR2, sigtrace); /* */
setsignal (SIGINFO, sigtrace); /* */
#ifdef USE_VM86
setsignal (SIGURG, sigurg); /* entry from NetBSD vm86 */
#else
setsignal (SIGBUS, sigbus); /* entry from FreeBSD, BSD/OS vm86 */
#endif
/* Call init functions */
if (raw_kbd)
console_init();
init_io_port_handlers();
bios_init();
cpu_init();
video_init();
if (xmode)
mouse_init();
video_bios_init();
disk_bios_init();
cmos_init();
xms_init();
dos_init();
net_init();
speaker_init();
timer_init();
/* iomap_init(); */
gettimeofday(&boot_time, 0);
if (zflag) for (;;) pause(); /* spin if requested */
if (raw_kbd) {
/*
* If we have a raw keyboard, and hence, video,
* sneak in a call to the video BIOS to reinit the
* the video display.
*/
u_long video_vector;
static u_char video_trampoline[] = {
0x60, /* pusha */
0xB8, 0x03, 0x00, /* mov ax,00003h */
0xCD, 0x10, /* int 010h */
0x61, /* popa */
0xCF, /* iret */
};
video_vector = insert_generic_trampoline(
sizeof(video_trampoline), video_trampoline);
N_PUSH(R_FLAGS, REGS);
N_PUSH(R_CS, REGS);
N_PUSH(R_IP, REGS);
N_PUTVEC(R_CS, R_IP, video_vector);
}
sc.sc_mask = 0;
sc.sc_onstack = 0;
if (tmode)
tracetrap(REGS);
#ifndef USE_VM86
R_EAX = (booting || raw_kbd) ? (int)&vconnect_area : -1;
R_EFLAGS |= PSL_VM | PSL_VIF; /* request VM86 mode */
vm86.sub_op = VM86_INIT;
vm86.sub_args = (char *)&kargs;
i = sysarch(I386_VM86, &vm86);
printf("Init: %d\n", i);
sigreturn(&sc);
debug(D_ALWAYS,"sigreturn failed : %s\n", strerror(errno));
#else
vm86s.cpu_type = VCPU_586;
i386_vm86(&vm86s);
#endif
/* shouldn't get here */
if (vflag) dump_regs((regcontext_t *)&sc);
fatal ("vm86 returned (no kernel support?)\n");
#undef sc
}
/*
** setup_boot
**
** Setup to boot DOS
*/
static void
setup_boot(regcontext_t *REGS)
{
FILE *fp; /* doscmdrc handle */
int fd; /* don't close this! */
fp = find_doscmdrc(); /* get doscmdrc */
if (!fp) {
fprintf(stderr, "You must have a doscmdrc to boot\n");
quit(1);
}
booting = read_config(fp); /* where to boot from? */
fclose(fp);
if (booting < 0) { /* not specified */
if ((fd = disk_fd(booting = 0)) < 0) /* try A: */
fd = disk_fd(booting = 2); /* try C: */
} else {
fd = disk_fd(booting); /* do like the man says */
}
if (fd < 0) { /* can we boot it? */
fprintf(stderr, "Cannot boot from %c: (can't open)\n",
booting + 'A');
quit(1);
}
/* read bootblock */
if (read(fd, (char *)0x7c00, 512) != 512) {
fprintf(stderr, "Short read on boot block from %c:\n",
booting + 'A');
quit(1);
}
/* initialise registers for entry to bootblock */
R_EFLAGS = 0x20202;
R_CS = 0x0000;
R_IP = 0x7c00;
R_SS = 0x9800;
R_SP = 0x8000 - 2;
R_DS = 0x0000;
R_ES = 0x0000;
R_AX = R_BX = R_CX = R_DX = R_SI = R_DI = R_BP = 0;
#if defined(__FreeBSD__) || defined(__NetBSD__)
/*
** init a few other context registers
*/
R_FS = 0x0000;
R_GS = 0x0000;
#endif
}
/*
** setup_command
**
** Setup to run a single command and emulate DOS
*/
static void
setup_command(int argc, char *argv[], regcontext_t *REGS)
{
FILE *fp;
u_short param[7] = {0, 0, 0, 0, 0, 0, 0};
char *p;
char prog[1024];
char buffer[PATH_MAX];
int i;
int fd;
fp = find_doscmdrc(); /* dig up a doscmdrc */
if (fp) {
read_config(fp); /* load config for non-boot mode */
fclose(fp);
}
if (argc <= 0) /* need some arguments */
usage();
/* look for a working directory XXX ??? */
if (dos_getcwd('C' - 'A') == NULL) {
/* try to get our current directory, use '/' if desperate */
p = getcwd(buffer, sizeof(buffer));
if (!p || !*p) p = getenv("PWD");
if (!p || !*p) p = "/";
init_path('C' - 'A', (u_char *)"/", (u_char *)p);
/* look for PATH= already set, learn from it if possible */
for (i = 0; i < ecnt; ++i) {
if (!strncmp(envs[i], "PATH=", 5)) {
dos_path = envs[i] + 5;
break;
}
}
/* no PATH in DOS environment? put current directory there*/
if (i >= ecnt) {
static char path[256];
sprintf(path, "PATH=C:%s",
dos_getcwd('C' - 'A'));
put_dosenv(path);
dos_path = envs[ecnt-1] + 5;
}
}
/* add a COMSPEC if required */
for (i = 0; i < ecnt; ++i) {
if (!strncmp(envs[i], "COMSPEC=", 8))
break;
}
if (i >= ecnt)
put_dosenv("COMSPEC=C:\\COMMAND.COM");
/* look for PATH already set, learn from it if possible */
for (i = 0; i < ecnt; ++i) {
if (!strncmp(envs[i], "PATH=", 5)) {
dos_path = envs[i] + 5;
break;
}
}
/* No PATH, default to c:\ */
if (i >= ecnt) {
dos_path = envs[ecnt-1] + 5;
put_dosenv("PATH=C:\\");
}
/* if no PROMPT, default to 'DOS>' */
for (i = 0; i < ecnt; ++i) {
if (!strncmp(envs[i], "PROMPT=", 7))
break;
}
if (i >= ecnt)
put_dosenv("PROMPT=DOS> ");
/* terminate environment */
envs[ecnt] = 0;
/* XXX ??? */
if (dos_getcwd('R' - 'A') == NULL)
init_path('R' - 'A', (u_char *)"/", 0);
/* get program name */
strncpy(prog, *argv++, sizeof(prog) -1);
prog[sizeof(prog) -1] = '\0';
/* try to open program */
if ((fd = open_prog(prog)) < 0) {
fprintf (stderr, "%s: command not found\n", prog);
quit(1);
}
/* load program */
load_command(REGS, 1, fd, cmdname, param, argv, envs);
close(fd);
}
/*
** find_doscmdrc
**
** Try to find a doscmdrc file
*/
static FILE *
find_doscmdrc(void)
{
FILE *fp;
char buffer[4096];
int fd;
if ((fp = fopen(".doscmdrc", "r")) == NULL) {
struct passwd *pwd = getpwuid(geteuid());
if (pwd) {
sprintf(buffer, "%s/.doscmdrc", pwd->pw_dir);
fp = fopen(buffer, "r");
}
if (!fp) {
char *home = getenv("HOME");
if (home) {
sprintf(buffer, "%s/.doscmdrc", home);
fp = fopen(buffer, "r");
}
}
if (!fp)
fp = fopen("/etc/doscmdrc", "r");
}
return(fp);
}
/*
** do_args
**
** commandline argument processing
*/
static int
do_args(int argc, char *argv[])
{
int i,c,p;
FILE *fp;
char *col;
while ((c = getopt (argc, argv, "234Oc:TkCIEMPRLAU:S:HDtzvVxXfbri:o:d:")) != -1) {
switch (c) {
case 'd':
if (fp = fopen(optarg, "w")) {
debugf = fp;
setbuf (fp, NULL);
} else
perror(optarg);
break;
case '2':
debug_flags |= D_TRAPS2;
break;
case '3':
debug_flags |= D_TRAPS3;
break;
case '4':
debug_flags |= D_DEBUGIN;
break;
case 'O':
debugf = stdout;
setbuf (stdout, NULL);
break;
case 'c':
if ((capture_fd = creat(optarg, 0666)) < 0) {
perror(optarg);
quit(1);
}
break;
case 'i':
i = 1;
if (col = strchr(optarg, ':')) {
*col++ = 0;
i = strtol(col, 0, 0);
}
p = strtol(optarg, 0, 0);
while (i-- > 0)
define_input_port_handler(p++, inb_traceport);
break;
case 'o':
i = 1;
if (col = strchr(optarg, ':')) {
*col++ = 0;
i = strtol(col, 0, 0);
}
p = strtol(optarg, 0, 0);
while (i-- > 0)
define_output_port_handler(p++, outb_traceport);
break;
case 'r':
raw_kbd = 1;
break;
case 'I':
debug_flags |= D_ITRAPS;
for (c = 0; c < 256; ++c)
debug_set(c);
break;
case 'k':
kargs.debug = 1;
break;
case 'T':
timer_disable = 1;
break;
case 'E':
debug_flags |= D_EXEC;
break;
case 'C':
debug_flags |= D_DOSCALL;
break;
case 'M':
debug_flags |= D_MEMORY;
break;
case 'P':
debug_flags |= D_PORT;
break;
case 'R':
debug_flags |= D_REDIR;
break;
case 'L':
debug_flags |= D_PRINTER;
break;
case 'A':
debug_flags |= D_TRAPS|D_ITRAPS;
for (c = 0; c < 256; ++c)
debug_set(c);
break;
case 'U':
debug_unset(strtol(optarg, 0, 0));
break;
case 'S':
debug_flags |= D_TRAPS|D_ITRAPS;
debug_set(strtol(optarg, 0, 0));
break;
case 'H':
debug_flags |= D_HALF;
break;
case 'x':
#ifdef NO_X
fatal("X11 support not compiled in.\n");
#endif
xmode = 1;
break;
case 't':
tmode = 1;
break;
case 'z':
zflag = 1;
break;
case 'D':
debug_flags |= D_DISK | D_FILE_OPS;
break;
case 'v':
debug_flags |= D_TRAPS | D_ITRAPS | D_HALF | 0xff;
break;
case 'V':
vflag = 1;
break;
case 'b':
booting = 1;
break;
default:
usage ();
}
}
return(optind);
}
/*
** Very helpful 8(
*/
void
usage (void)
{
fprintf (stderr, "usage: doscmd cmd args...\n");
quit (1);
}
/*
** look up a DOS command name
**
** XXX ordering is wrong!
*/
static int
open_name(char *name, char *ext)
{
int fd;
char *p = name + strlen(name);
char *q;
*ext = 0;
q = strrchr(name, '/');
if (q)
q++;
else
q = name;
if (!strchr(q, '.')) {
strcpy(ext, ".exe");
strcpy(p, ".exe");
if ((fd = open (name, O_RDONLY)) >= 0)
return (fd);
strcpy(ext, ".com");
strcpy(p, ".com");
if ((fd = open (name, O_RDONLY)) >= 0)
return (fd);
} else {
if ((fd = open (name, O_RDONLY)) >= 0)
return (fd);
}
return (-1);
}
/*
** look up a DOS command, search the path as well.
*/
int
open_prog(char *name)
{
int fd;
char fullname[1024], tmppath[1024];
char *p;
char *e;
char ext[5];
int error;
int drive;
char *path;
if (strpbrk(name, ":/\\")) {
error = translate_filename(name, fullname, &drive);
if (error)
return (-1);
fd = open_name(fullname, ext);
strcpy(cmdname, name);
if (*ext)
strcat(cmdname, ext);
return (fd);
}
path = dos_path;
while (*path) {
p = path;
while (*p && *p != ';')
++p;
memcpy(tmppath, path, p - path);
e = tmppath + (p - path);
*e++ = '\\';
strcpy(e, name);
path = *p ? p + 1 : p;
error = translate_filename(tmppath, fullname, &drive);
if (error)
continue;
fd = open_name(fullname, ext);
if (fd >= 0) {
strcpy(cmdname, tmppath);
if (*ext)
strcat(cmdname, ext);
return (fd);
}
}
return (-1);
}
/*
** append a value to the DOS environment
*/
void
put_dosenv(char *value)
{
if (ecnt < sizeof(envs)/sizeof(envs[0])) {
if ((envs[ecnt++] = strdup(value)) == NULL) {
perror("put_dosenv");
quit(1);
}
} else {
fprintf(stderr, "Environment full, ignoring %s\n", value);
}
}
/*
** replicate a fd up at the top of the range
*/
int
squirrel_fd(int fd)
{
int sfd = sysconf(_SC_OPEN_MAX);
struct stat sb;
do {
errno = 0;
fstat(--sfd, &sb);
} while (sfd > 0 && errno != EBADF);
if (errno == EBADF && dup2(fd, sfd) >= 0) {
close(fd);
return(sfd);
}
return(fd);
}
/*
** Exit-time stuff
*/
/*
** Going away time
**
** XXX belongs somewhere else perhaps
*/
void
done (regcontext_t *REGS, int val)
{
if (curpsp < 2) {
if (xmode) {
char *m;
tty_move(24, 0);
for (m = "END OF PROGRAM"; *m; ++m)
tty_write(*m, 0x8400);
for (m = "(PRESS <CTRL-ALT> ANY MOUSE BUTTON TO exit)"; *m; ++m)
tty_write(*m, 0x0900);
tty_move(-1, -1);
for (;;)
tty_pause();
} else {
quit(val);
}
}
exec_return(REGS, val);
}
typedef struct COQ {
void (*func)();
void *arg;
struct COQ *next;
} COQ;
COQ *coq = 0;
void
quit(int status)
{
while (coq) {
COQ *c = coq;
coq = coq->next;
c->func(c->arg);
}
if (!xmode) /* XXX not for bootmode */
puts("\n");
exit(status);
}
void
call_on_quit(void (*func)(void *), void *arg)
{
COQ *c = (COQ *)malloc(sizeof(COQ));
if (!c) {
perror("call_on_quit");
quit(1);
}
c->func = func;
c->arg = arg;
c->next = coq;
coq = c;
}
struct i386_ioperm_args {
u_short start;
u_short length;
char disable;
};
struct sysarch_args {
int op;
char *parms;
};
static void
iomap_init(void)
{
int i;
struct i386_ioperm_args args[] = {
#if 0
{ 0x200, 0x200, 1 }, /* 0x200 - 0x400 */
{ 0x1c80, 2, 1 }, /* 0x1c80 - 0x1c81 */
{ 0x2c80, 2, 1 }, /* 0x2c80 - 0x2c81 */
{ 0x3c80, 2, 1 }, /* 0x3c80 - 0x3c81 */
{ 0x3c4, 2, 1 }, /* 0x3c4 - 0x3c5 */
{ 0x3c5, 2, 1 }, /* 0x3ce - 0x3cf */
#else
{ 0x0, 0xffff, 1 }, /* entire i/o space */
#endif
{ 0, 0, 0 }
};
for (i = 0; args[i].length; i++)
if (sysarch(I386_SET_IOPERM, &(args[i])) < 0)
err(1, "sysarch");
}