; Execute a file - the equivalent of system() in stdio ; ==================================================== ; mdfs.net/Info/Comp/PDP11/ProgTips/System - J.G.Harston ; ; Forks to create a child process, and then in the child process creates the ; command string to load and execute the system shell, passing it the command ; line to execute, while the parent waits for it to complete. ; ; The Bell Unix API passes parameters inline after the TRAP call, the BSD Unix ; API passes parameters on the stack, so while the code is very similar, it ; is slightly different for each API. ; Bell Unix API (Unix v1-v8 and BSD2.9) ; ===================================== ; On entry, r1=>null-terminated command string ; On exit, CS=Couldn't fork ; CC=Forked or spawned successfully, r0=return value ; .CLIexecute trap 2 ; fork() br CLIchild ; Child process returns here bcs CLIerror ; Parent process returns here, R0=child PID mov r0,-(sp) ; Save child's PID .CLIwait trap 7 ; wait() cmp r0,(sp) beq CLIfinished cmp r0,#&FFFF bne CLIwait ; Loop until child finished .CLIfinished tst (sp)+ ; Drop child's PID swab r1 ; Move return value to bottom byte, clear Carry mov r1,r0 ; R0=return value .CLIerror rts pc ; CLI child process ; ----------------- .CLIchild clr -(sp) ; end of string array mov r1,-(sp) ; => command string mov #UXsh3,-(sp) ; => "-c" mov #UXsh2,-(sp) ; => "sh" mov #&890B,TRAP_BUF+0 ; exec mov #UXsh1,TRAP_BUF+2 ; => "/bin/sh" mov sp,TRAP_BUF+4 ; => pointers to command strings trap 0 ; indir() EQUW TRAP_BUF ; exec(shell, parameters) ; ; If we get back, we didn't fork, we spawned add #2*4,sp ; Balance stack, clear Carry rts pc ; Return value in R0 .UXsh1 EQUS "/bin/sh",0 .UXsh2 EQUS "sh",0 .UXsh3 EQUS "-c",0 ALIGN .TRAP_BUF EQUW 0 EQUW 0 EQUW 0 ; BSD Unix API (BSD2.10 and later) ; ================================ ; On entry, r1=>null-terminated command string ; On exit, CS=Couldn't fork ; CC=Forked or spawned successfully, r0=return value ; .CLIexecute trap 2 ; fork() br CLIchild ; Child process returns here bcs CLIerror ; Parent process returns here, R0=child PID clr -(sp) ; status mov sp,r1 ; r1=>status clr -(sp) ; 0 clr -(sp) ; 0 mov r1,-(sp) ; =>status mov #-1,-(sp) ; -1 mov r0,-(sp) ; Save child's PID .CLIwait trap 7 ; wait(-1,&status,0,0) cmp r0,(sp) beq CLIfinished cmp r0,#&FFFF bne CLIwait ; Loop until child finished .CLIfinished add #2*6,sp ; Drop from stack swab r1 ; Move return value to bottom byte, clear Carry mov r1,r0 ; R0=return value .CLIerror rts pc ; CLI child process ; ----------------- .UX_CLIchild clr -(sp) ; end of string array mov r1,-(sp) ; => command string mov #UXsh3,-(sp) ; => "-c" mov #UXsh2,-(sp) ; => "sh" mov sp,r1 ; r1=>list of string pointers mov r1,-(sp) ; =>list of string pointers mov #UXsh1,-(sp) ; =>"/bin/sh" clr -(sp) ; padding trap 11 ; execv("/bin/sh", =>parameters) ; ; If we get back, we didn't fork, we spawned add #2*7,sp ; Balance stack, clear Carry rts pc ; Return value in R0 .UXsh1 EQUS "/bin/sh",0 .UXsh2 EQUS "sh",0 .UXsh3 EQUS "-c",0 ALIGN ; Combined Bell and BSD source code ; ================================= ; The code is sufficiently similar that source can be written to build ; both routines. Define the symbol BSDAPI to build BSD code, otherwise ; Bell code will be built. ; .CLIexecute trap 2 ; fork() br CLIchild ; Child process returns here bcs CLIerror ; Parent process returns here, R0=child PID #ifdef BSDAPI clr -(sp) ; status mov sp,r1 ; r1=>status clr -(sp) ; 0 clr -(sp) ; 0 mov r1,-(sp) ; =>status mov #-1,-(sp) ; -1 #endif mov r0,-(sp) ; Save child's PID .CLIwait trap 7 ; wait() cmp r0,(sp) beq CLIfinished cmp r0,#&FFFF bne CLIwait ; Loop until child finished .CLIfinished #ifdef BSDAPI add #2*6,sp ; Drop from stack #else tst (sp)+ ; Drop child's PID #endif swab r1 ; Move return value to bottom byte, clear Carry mov r1,r0 ; R0=return value .CLIerror rts pc ; CLI child process ; ----------------- .CLIchild clr -(sp) ; end of string array mov r1,-(sp) ; => command string mov #UXsh3,-(sp) ; => "-c" mov #UXsh2,-(sp) ; => "sh" #ifdef BSDAPI mov sp,r1 ; r1=>list of string pointers mov r1,-(sp) ; =>list of string pointers mov #UXsh1,-(sp) ; =>"/bin/sh" clr -(sp) ; padding trap 11 ; execv("/bin/sh", =>parameters) ; ; If we get back, we didn't fork, we spawned add #2*7,sp ; Balance stack, clear Carry #else mov #&890B,TRAP_BUF+0 ; exec mov #UXsh1,TRAP_BUF+2 ; => "/bin/sh" mov sp,TRAP_BUF+4 ; => pointers to command strings trap 0 ; indir() EQUW TRAP_BUF ; exec(shell, parameters) ; ; If we get back, we didn't fork, we spawned add #2*4,sp ; Balance stack, clear Carry #endif rts pc ; Return value in R0 .UXsh1 EQUS "/bin/sh",0 .UXsh2 EQUS "sh",0 .UXsh3 EQUS "-c",0 ALIGN #ifndef BSDAPI .TRAP_BUF EQUW 0 EQUW 0 EQUW 0 #endif ; Combined Bell and BSD source code ; ================================= If the executing code knows what API is being used, the two routines can be combined and the appropriate calls made when executing in the appropriate environment.