gno/bin/gsh/invoke.asm

867 lines
16 KiB
NASM

*************************************************************************
*
* The GNO Shell Project
*
* Developed by:
* Jawaid Bazyar
* Tim Meekins
*
* $Id: invoke.asm,v 1.11 1999/01/14 17:44:25 tribby Exp $
*
**************************************************************************
*
* INVOKE.ASM
* By Tim Meekins
* Modified by Dave Tribby for GNO 2.0.6
*
* Command invocation routines.
*
* Note: text set up for tabs at col 16, 22, 41, 49, 57, 65
* | | | | | |
* ^ ^ ^ ^ ^ ^
**************************************************************************
*
* Interfaces defined in this file:
*
* redirect subroutine (4:sfile,4:dfile,4:efile,2:app,2:eapp,2:pipein,
* 2:pipeout,2:pipein2,2:pipeout2)
* returns with carry set/clear to indicate failure/success
*
* invoke subroutine (2:argc,4:argv,4:sfile,4:dfile,4:efile,2:app,
* 2:eapp,2:bg,4:cline,2:jobflag,2:pipein,2:pipeout,
* 2:pipein2,2:pipeout2,4:pipesem,4:awaitstatus)
* return 2:rtnval
*
**************************************************************************
mcopy /obj/gno/bin/gsh/invoke.mac
dummyinvoke start ; ends up in .root
end
setcom 60
SIGINT gequ 2
SIGKILL gequ 9
SIGTERM gequ 15
SIGSTOP gequ 17
SIGTSTP gequ 18
SIGCONT gequ 19
SIGCHLD gequ 20
SIGTTIN gequ 21
SIGTTOU gequ 22
**************************************************************************
*
* Redirect
*
**************************************************************************
redirect START
space equ 1
pipeout2 equ space+3
pipein2 equ pipeout2+2
pipeout equ pipein2+2
pipein equ pipeout+2
eapp equ pipein+2
app equ eapp+2
efile equ app+2
dfile equ efile+4
sfile equ dfile+4
end equ sfile+4
; subroutine (4:sfile,4:dfile,4:efile,2:app,2:eapp,2:pipein,2:pipeout,2:pipein2,2:pipeout2),space
* NOTE: only called from invoke, after fork_mutex is locked
tsc
phd
tcd
;
; Redirect standard input
;
ora2 sfile,sfile+2,@a If no name provided,
beq execa skip it.
pei (sfile+2) Convert c-string
pei (sfile) filename to
jsr c2gsstr GS/OS string.
sta RedirectFile Store filename pointer
stx RedirectFile+2 in parameter block.
stz RedirectDev stdin devnum = 0.
stz RedirectApp Cannot append.
RedirectGS RedirectParm
php
ph4 RedirectFile Free allocated GS/OS string.
jsl nullfree
plp
bcc execa If RedirectGS failed,
ldx #^err1 print error message:
lda #err1 'Error redirecting standard input.'
jmp badbye and quit.
;
; Redirect standard output
;
execa ora2 dfile,dfile+2,@a
beq execb
pei (dfile+2)
pei (dfile)
jsr c2gsstr
sta RedirectFile
stx RedirectFile+2
ld2 1,RedirectDev stdout devnum = 1
mv2 app,RedirectApp
RedirectGS RedirectParm
php
ph4 RedirectFile
jsl nullfree
plp
bcc execb
ldx #^err2 Print error message:
lda #err2 'Error redirecting standard output.'
jmp badbye
;
; Redirect standard error
;
execb ora2 efile,efile+2,@a
beq execc
pei (efile+2)
pei (efile)
jsr c2gsstr
sta RedirectFile
stx RedirectFile+2
ld2 2,RedirectDev
mv2 eapp,RedirectApp
RedirectGS RedirectParm
php
ph4 RedirectFile
jsl nullfree
plp
bcc execc
ldx #^err3 Print error message:
lda #err3 'Error redirecting standard error.'
jmp badbye
;
; Is input piped in?
;
execc lda pipein
beq execd
dup2 (pipein,#1)
mv2 pipein2,CloseRef
Close CloseParm
ldx #0
lda pipein
SetInputDevice (#3,@xa)
;
; Is output piped?
;
execd lda pipeout
beq exece
dup2 (pipeout,#2)
mv2 pipeout2,CloseRef
Close CloseParm
ldx #0
lda pipeout
SetOutputDevice (#3,@xa)
exece anop
;
; All the file and pipe redirection has been handled. Time to say goodbye.
;
goodbye ldy #0
bra exit
badbye jsr errputs
cop $7F ; get out of the way
ldy #1
exit lda space
sta end-3
lda space+1
sta end-2
pld
tsc
clc
adc #end-4
tcs
cpy #1 Clear/set carry for success/failure.
rtl
;
; Parameter block for shell call to redirect I/O (ORCA/M manual p.425)
;
RedirectParm dc i2'3' pCount
RedirectDev ds 2 Dev num (0=stdin,1=stdout,2=errout)
RedirectApp ds 2 Append flag (0=delete)
RedirectFile ds 4 File name (GS/OS input string)
;
; Parameter block for GS/OS call to close a file
;
CloseParm dc i'1' pCount
CloseRef dc i'0' refNum
err1 dc c'gsh: Error redirecting standard input.',h'0d00'
err2 dc c'gsh: Error redirecting standard output.',h'0d00'
err3 dc c'gsh: Error redirecting standard error.',h'0d00'
END
**************************************************************************
*
* Invoke a command (PHASE 0)
*
**************************************************************************
invoke START
using hashdata
using vardata
using global
using pdata
p equ 0
biflag equ p+4
ptr equ biflag+2
rtnval equ ptr+4 Return pid, -1 (error), or 0 (no fork)
cpath equ rtnval+2
hpath equ cpath+4
space equ hpath+4
subroutine (2:argc,4:argv,4:sfile,4:dfile,4:efile,2:app,2:eapp,2:bg,4:cline,2:jobflag,2:pipein,2:pipeout,2:pipein2,2:pipeout2,4:pipesem,4:awaitstatus),space
ld2 -1,rtnval
stz biflag Clear built-in flag
stz hpath and address from hash table.
stz hpath+2
lda argc If number of arguments == 0,
bne chknull nothing to do. (Shouldn't happen
nulldone jmp done because invoke checks argc==0).
;
; Check for null command
;
chknull ldy #2 Move 1st argument
lda [argv] pointer to
sta cpath cpath (4 bytes).
lda [argv],y
sta cpath+2 If pointer == NULL
ora cpath
beq nulldone all done.
lda [cpath] If first character == '\0',
and #$FF
beq nulldone all done.
;
; Check command: is it builtin, in hash table, etc?
;
pei (cpath+2) IsBuiltin returns
pei (cpath) 0 if forked built-in
jsl IsBuiltin 1 if non-forked built-in
cmp #-1 -1 if not a built-in
jne trybuiltin
;
; Command is not listed in the built-in table. See if it was hashed
;
pei (cpath+2)
pei (cpath)
ph4 hash_table
ph4 #hash_paths
jsl search
cmp #0
bne changeit
cpx #0
beq noentry
changeit sta cpath Use full path from
stx cpath+2 hash table.
sta hpath Save adddress for deallocation.
stx hpath+2
;
; Get information about the command's filename
;
noentry lock info_mutex
pei (cpath+2)
pei (cpath)
jsr c2gsstr
sta GRecPath
sta ptr
stx GRecPath+2
stx ptr+2
GetFileInfo GRec
unlock info_mutex
jcs notfound If error getting info, print error.
; Is file type $B5: GS/OS Shell application (EXE)?
if2 GRecFileType,eq,#$B5,doExec
; Is file type $B3: GS/OS application (S16)?
if2 @a,eq,#$B3,doExec
ldx vardirexec If $NODIREXEC isn't set, and
bne ft02
cmp #$0F
jeq doDir file is a directory: change to it.
; Is file type $B0: source code file (SRC)?
ft02 if2 @a,ne,#$B0,badfile
; Type $B0, Aux $00000006 is a shell command file (EXEC)
if2 GRecAuxType,ne,#6,badfile
if2 GRecAuxType+2,ne,#0,badfile
jmp doShell
;
; Command file is not an appropriate type
;
badfile ldx cpath+2
lda cpath
jsr errputs
ldx #^err1 Print error message:
lda #err1 'Not executable.'
jsr errputs
pei (ptr+2) Free memory used to hold
pei (ptr) GS/OS string with path.
jsl nullfree
jmp chkpipe If pipe was allocated, clean up.
*
* ---------------------------------------------------------------
*
* Launch an executable (EXE or S16 file)
doExec pei (ptr+2)
pei (ptr)
jsl nullfree
jsr prefork
fork #invoke0
jsr postfork
jmp done
;
; Forked shell starts here...
;
invoke0 phk Make sure data bank register
plb is the same as program bank.
;
; Make copies of command line (cline) and path (cpath) for child
;
pha
pha
tsc
phd
tcd
ldx #0
tsc
FindHandle @xa,1
ldy #6
lda [1],y ;This is the UserID!
and #$F0FF
pha
ph4 _cline
jsr cstrlen
inc a
ply
pha
ldx #0
NewHandle (@xa,@y,#$4018,#0),1
ply
ldx #0
PtrToHand (_cline,1,@xy)
ldy #2
lda [1],y
tax
lda [1]
pld
ply
ply
phx ;_cline
pha
;
pha
pha
tsc
phd
tcd
ldx #0
tsc
FindHandle @xa,1
ldy #6
lda [1],y ;This is the UserID!
and #$F0FF
pha
ph4 _cpath
jsr cstrlen
inc a
ply
pha
ldx #0
NewHandle (@xa,@y,#$4018,#0),1
ply
ldx #0
PtrToHand (_cpath,1,@xy)
ldy #2
lda [1],y
tax
lda [1]
pld
ply
ply
phx ;_cpath
pha
jsl infork
bcs invoke1
;
; Call _execve(_cpath,_cline) to replace forked shell with executable file
;
case on
jsl _execve For 2.0.6: call _execve, not execve
case off
rtl
;
; Error reported by infork; clean up stack and return to caller
;
invoke1 pla
pla
pla
pla
rtl
*
* ---------------------------------------------------------------
*
* Next command is a directory name, so change to that directory
doDir lock cdmutex
mv4 GRecPath,PRecPath
SetPrefix PRec
unlock cdmutex
pei (ptr+2) Free memory used to hold
pei (ptr) GS/OS string with path.
jsl nullfree
lda #0 Completion status = 0.
;
; Rest of cleanup is shared with non-forked builtin
;
jmp nfcleanup
*
* ---------------------------------------------------------------
*
* Next command is a shell command file: fork a shell script
doShell anop
inc biflag ;don't free argv...
jsr prefork
* int fork2(void *subr, int stack, int prio, char *name, word argc, ...)
pea 0
ldy #2
lda [argv],y
pha
lda [argv]
pha
pea 0
pea 1024
ph4 #exec0
case on
jsl fork2
case off
jsr postfork
pei (ptr+2) Free memory used to hold
pei (ptr) GS/OS string with path.
jsl nullfree
jmp done
;
; Forked shell starts here...
;
exec0 anop
ph4 _hpath argfree parameters.
ph2 _argc
ph4 _argv
ph4 _cpath ShellExec parameters
ph2 _argc
ph4 _argv
jsl infork
bcs exec0c
signal (#SIGCHLD,#0)
PushVariablesGS NullPB
pea 1 jobflag = 1
jsl ShellExec
jsl argfree
PopVariablesGS NullPB
rtl
;
; Error reported by infork; clean up stack and return to caller
;
exec0c pla
pla
pla
pla
pla
pla
pla
pla
rtl
; Null parameter block used for shell calls PushVariables
; (ORCA/M manual p.420) and PopVariablesGS (p. 419)
NullPB dc i2'0' pCount
*
* ---------------------------------------------------------------
*
* File name was found in the built-in table
trybuiltin inc biflag It's a built-in. Which type?
cmp #1 Either fork or don't fork.
beq noforkbuiltin
;
; It's a forked builtin
;
jsr prefork
fork #forkbuiltin
jsr postfork
jmp done
;
; Control transfers here for a forked built-in command
;
forkbuiltin anop
cop $7F Give palloc a chance
ph2 _argc
ph4 _argv
jsl infork
bcs fork0c
jsl builtin
rtl
;
; Error reported by infork; clean up stack and return to caller
;
fork0c pla
pla
pla
rtl
* ---------------------------------------------------------------
;
; It's a non-forked builtin
;
noforkbuiltin anop
pei (argc)
pei (argv+2)
pei (argv)
jsl builtin
and #$00FF Make return status look like result of
xba wait(): high-order byte = status.
nfcleanup anop
sta [awaitstatus]
stz rtnval Return value (pid) = no fork done.
;
; There might be a process waiting on a pipe
;
lda [pipesem]
sta _semaphore
bra chkpipe
*
* ---------------------------------------------------------------
*
* Command was not found as built-in or as a file
notfound pei (ptr+2)
pei (ptr)
jsl nullfree
ldy #2
lda [argv],y
tax
lda [argv]
jsr errputs
ldx #^err2 Print error message:
lda #err2 'Command not found.'
jsr errputs
chkpipe lda pipein
beq done
; Input being piped into a command that was not found.
ssignal _semaphore
sdelete _semaphore
mv4 pjoblist,p
ldy #16 ;p_jobid
lda [p],y Get forked process's pid.
_getpgrp @a Get forked process's group number.
eor #$FFFF
inc a
kill (@a,#9) Kill all processes in that group.
sigpause #0
done cop $7F
lda biflag If built-in flag is clear,
bne skipfrarg
pei (hpath+2) Free arguments.
pei (hpath)
pei (argc)
pei (argv+2)
pei (argv)
jsl argfree
skipfrarg pei (cline+2) Free command-line.
pei (cline)
jsl nullfree
return 2:rtnval
* ---------------------------------------------------------------
*
* Support routines
*
* ---------------------------------------------------------------
;
; Tasks to do just before forking
;
prefork lock fork_mutex Lock the fork mutual exclusion.
SetInGlobals (#$FF,#00)
;
; Move essential parameters from stack to mutual-exclusion protected memory
;
mv4 sfile,_sfile
mv4 dfile,_dfile
mv4 efile,_efile
mv2 app,_app
mv2 eapp,_eapp
mv4 cline,_cline
mv4 cpath,_cpath
mv4 hpath,_hpath
mv2 argc,_argc
mv4 argv,_argv
mv2 pipein,_pipein
mv2 pipeout,_pipeout
mv2 pipein2,_pipein2
mv2 pipeout2,_pipeout2
mv2 bg,_bg
mv2 jobflag,_jobflag
lda [pipesem]
sta _semaphore
lda pipesem Set address of
sta putsem+1 semaphone in
lda pipesem+1 LDA instruction.
sta putsem+2
rts
* ---------------------------------------------------------------
;
; Tasks the parent process does right after forking
;
postfork sta rtnval Save pid as return value.
lda pipein If pipein != 0,
beq postfork2
sta CloseRef
Close CloseParm close pipein.
postfork2 lda pipeout If pipeout != 0,
beq postfork3
sta CloseRef
Close CloseParm close pipeout.
postfork3 lda rtnval If return value == -1,
cmp #-1
bne postfork4
ldx #^deadstr Print error message:
lda #deadstr 'Cannot fork (too many processes?)'
jsr errputs
unlock fork_mutex Unlock the fork mutual exclusion.
jmp postfork6 Return to caller.
postfork4 ldx jobflag
dex
beq postfork5 If jobflag == 1,
pha
pei (bg)
pei (cline+2)
pei (cline)
lda pipein if pipein == 0,
bne postfork4a
jsl palloc palloc(0,cline)
bra postfork5 else
postfork4a jsl pallocpipe pallocpipe(0,cline)
postfork5 anop
;
; Wait for fork mutual exclusion lock to clear (cleared by infork)
;
lda >fork_mutex ;DANGER!!!!! Assumes knowledge of
beq postfork6 ;lock/unlock structure!!!!!!!!
cop $7F
bra postfork5
postfork6 rts
* ---------------------------------------------------------------
;
; Startup tasks by forked process
;
infork phk Make sure data bank register
plb is the same as program bank.
;
; NOTE: next two lines were added for v2.0.6 in order to prevent background
; processes from being waited on when they are kicked off from an
; exec file. The side effect of having the process become owned by
; the null process may not be desired. (Perhaps there is a better way!)
;
lda _bg If in background
bne optty
lda _jobflag or jobflag == 0,
bne infork0b
optty Open ttyopen Open tty.
jcs errinfork
lda _pipein
bne infork0a If not in pipeline,
tcnewpgrp ttyref allocate new process group.
infork0a settpgrp ttyref Set current process to have proc group.
lda _bg If in background,
beq infork0b
tctpgrp (gshtty,gshpid) reset tty to the shell process group.
infork0b ph4 _sfile Redirect I/O
ph4 _dfile
ph4 _efile
ph2 _app
ph2 _eapp
ph2 _pipein
ph2 _pipeout
ph2 _pipein2
ph2 _pipeout2
jsl redirect
jcs errinfork
unlock fork_mutex
;
; Wait on appropriate pipe semaphores
;
lda _pipein
bne infork0c
lda _pipeout
beq clean_exit
;
; _pipein == 0 && _pipeout != 0
;
screate #0 Create a semaphore with count=0.
putsem sta >$FFFFFF Store semaphore number in pipesem.
swait @a Block on semaphore until ssignal.
bra clean_exit
infork0c lda _pipeout
bne clean_exit
;
; _pipein != 0 && _pipeout != 0
;
waitsemy lda _semaphore While _semaphore == 0,
bne goodsemy
cop $7F allow other processes to run.
bra waitsemy
goodsemy ssignal _semaphore Release _semaphore
sdelete _semaphore and delete it.
clean_exit anop
clc
bra indone
;
; Arrive at "errinfork" if ttyopen or redirect didn't work.
;
errinfork unlock fork_mutex
; sec Note: carry already set
indone rtl Return to caller; status in carry.
fork_mutex key Mutual exclusion for forking
info_mutex key Mutual exclusion for GetFileInfo
cdmutex key Mutual exclusion for SetPrefix
;
; Variables protected by fork_mutex (set by parent, used by child process)
;
_argc dc i2'0'
_argv dc i4'0'
_sfile dc i4'0'
_dfile dc i4'0'
_efile dc i4'0'
_app dc i2'0'
_eapp dc i2'0'
_cline dc i4'0'
_cpath dc i4'0'
_hpath dc i4'0'
_pipein dc i2'0'
_pipeout dc i2'0'
_pipein2 dc i2'0'
_pipeout2 dc i2'0'
_bg dc i2'0'
_jobflag dc i2'0'
_semaphore dc i2'0'
;
; String constants
;
str dc c'[0]',h'0d00'
err1 dc c': Not executable.',h'0d00'
err2 dc c': Command not found.',h'0d00'
deadstr dc c'Cannot fork (too many processes?)',h'0d00' ;try a spoon
; Parameter block for GS/OS call GetFileInfo
GRec dc i'4' pCount (# of parameters)
GRecPath ds 4 pathname (input; ptr to GS/OS string)
ds 2 access (access attributes)
GRecFileType ds 2 fileType (file type attribute)
GRecAuxType ds 4 auxType (auxiliary type attribute)
PRec dc i'2'
PRecNum dc i'0'
PRecPath ds 4
CloseParm dc i2'1'
CloseRef dc i2'0'
Err dc i2'0'
; Parameter block for opening tty
ttyopen dc i2'2'
ttyref dc i2'0'
dc i4'ttyname'
ttyname gsstr '.tty'
END