From e8fb8d885b7f28769a55a60a723e52e2e8e226d5 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Tue, 22 Nov 2011 13:53:36 +0100 Subject: [PATCH] execve(2) can now load programs from the filesystem. Previously it was restricted to only the ramdisk. --- libmaxsi/hsrc/error.h | 1 + sortix/elf.cpp | 4 +- sortix/interrupt.cpp | 12 ++- sortix/process.cpp | 216 ++++++++++++++++++++++++++++++------------ sortix/process.h | 3 +- sortix/scheduler.cpp | 15 ++- 6 files changed, 180 insertions(+), 71 deletions(-) diff --git a/libmaxsi/hsrc/error.h b/libmaxsi/hsrc/error.h index 0b12aaec..369d500f 100644 --- a/libmaxsi/hsrc/error.h +++ b/libmaxsi/hsrc/error.h @@ -59,6 +59,7 @@ namespace Maxsi const int ERANGE = 24; const int EISDIR = 25; const int EPERM = 26; + const int EIO = 27; extern int _errornumber; diff --git a/sortix/elf.cpp b/sortix/elf.cpp index 7c770cce..9d219052 100644 --- a/sortix/elf.cpp +++ b/sortix/elf.cpp @@ -117,12 +117,14 @@ namespace Sortix addr_t Construct(Process* process, const void* file, size_t filelen) { - if ( filelen < sizeof(Header) ) { return 0; } + // TODO: These messages should be returned by errno instead! + if ( filelen < sizeof(Header) ) { Log::PrintF("File is not executable\n"); return 0; } const Header* header = (const Header*) file; if ( !(header->magic[0] == 0x7F && header->magic[1] == 'E' && header->magic[2] == 'L' && header->magic[3] == 'F' ) ) { + Log::PrintF("File is not executable\n"); return 0; } diff --git a/sortix/interrupt.cpp b/sortix/interrupt.cpp index 700a4d0f..d7ac6d0c 100644 --- a/sortix/interrupt.cpp +++ b/sortix/interrupt.cpp @@ -29,9 +29,14 @@ #include "process.h" // Hack for SIGSEGV #include "sound.h" // Hack for SIGSEGV +#include "thread.h" // HACK FOR SIGSEGV +#include "syscall.h" // HACK FOR SIGSEGV +#include "scheduler.h" // HACK FOR SIGSEGV namespace Sortix { + void SysExit(int status); // HACK + namespace Interrupt { const bool DEBUG_EXCEPTION = false; @@ -78,9 +83,10 @@ namespace Sortix message, regs->eip, regs->cr2, regs->err_code); Sound::Mute(); - const char* programname = "sh"; - const char* const argv[] = { "sh" }; - CurrentProcess()->Execute(programname, 1, argv, regs); + + CurrentProcess()->Exit(139); + Scheduler::ProcessTerminated(regs); + return; } diff --git a/sortix/process.cpp b/sortix/process.cpp index 7f6148c5..2122c8d2 100644 --- a/sortix/process.cpp +++ b/sortix/process.cpp @@ -23,12 +23,16 @@ ******************************************************************************/ #include "platform.h" +#include #include #include #include #include "thread.h" #include "process.h" #include "device.h" +#include "stream.h" +#include "filesystem.h" +#include "directory.h" #include "scheduler.h" #include "memorymanagement.h" #include "initrd.h" @@ -260,26 +264,12 @@ namespace Sortix } } - int Process::Execute(const char* programname, int argc, const char* const* argv, CPU::InterruptRegisters* regs) + int Process::Execute(const char* programname, const byte* program, size_t programsize, int argc, const char* const* argv, CPU::InterruptRegisters* regs) { ASSERT(CurrentProcess() == this); - size_t programsize = 0; - byte* program = InitRD::Open(programname, &programsize); - if ( !program ) { return -1; } - addr_t entry = ELF::Construct(CurrentProcess(), program, programsize); - if ( !entry ) - { - Log::PrintF("Could not create process '%s'", programname); - if ( String::Compare(programname, "sh") == 0 ) - { - Panic("Couldn't create the shell process"); - } - - const char* const SHARGV[]= { "sh" }; - return Execute("sh", 1, SHARGV, regs); - } + if ( !entry ) { return -1; } // TODO: This may be an ugly hack! // TODO: Move this to x86/process.cpp. @@ -311,41 +301,138 @@ namespace Sortix return 0; } + class SysExecVEState + { + public: + char* filename; + DevBuffer* dev; + byte* buffer; + size_t count; + size_t sofar; + int argc; + char** argv; + + public: + SysExecVEState() + { + filename = NULL; + dev = NULL; + buffer = NULL; + count = 0; + sofar = 0; + argc = 0; + argv = NULL; + } + + ~SysExecVEState() + { + delete[] filename; + dev->Unref(); + delete[] buffer; + for ( int i = 0; i < argc; i++ ) { delete[] argv[i]; } + delete[] argv; + } + + }; + + int SysExevVEStage2(SysExecVEState* state) + { + if ( !state->dev->IsReadable() ) { Error::Set(Error::EBADF); delete state; return -1; } + + byte* dest = state->buffer + state->sofar; + size_t amount = state->count - state->sofar; + ssize_t bytesread = state->dev->Read(dest, amount); + + // Check for premature end-of-file. + if ( bytesread == 0 && amount != 0 ) + { + Error::Set(Error::EIO); delete state; return -1; + } + + // We actually managed to read some data. + if ( 0 <= bytesread ) + { + state->sofar += bytesread; + if ( state->sofar <= state->count ) + { + CPU::InterruptRegisters* regs = Syscall::InterruptRegs(); + Process* process = CurrentProcess(); + int result = process->Execute(state->filename, state->buffer, state->count, state->argc, state->argv, regs); + if ( result == 0 ) { Syscall::AsIs(); } + delete state; + return result; + } + + return SysExevVEStage2(state); + } + + if ( Error::Last() != Error::EWOULDBLOCK ) { delete state; return -1; } + + // The stream will resume our system call once progress has been + // made. Our request is certainly not forgotten. + + // Resume the system call with these parameters. + Thread* thread = CurrentThread(); + thread->scfunc = (void*) SysExevVEStage2; + thread->scstate[0] = (size_t) state; + thread->scsize = sizeof(state); + + // Now go do something else. + Syscall::Incomplete(); + return 0; + } + + DevBuffer* OpenProgramImage(const char* progname) + { + // TODO: Use the PATH enviromental variable. + char* abs = Directory::MakeAbsolute("/bin", progname); + if ( !abs ) { Error::Set(Error::ENOMEM); return NULL; } + + // TODO: Use O_EXEC here! + Device* dev = FileSystem::Open(abs, O_RDONLY, 0); + delete[] abs; + + if ( !dev ) { return NULL; } + if ( !dev->IsType(Device::BUFFER) ) { dev->Unref(); return NULL; } + return (DevBuffer*) dev; + } + int SysExecVE(const char* filename, int argc, char* const argv[], char* const /*envp*/[]) { // TODO: Validate that all the pointer-y parameters are SAFE! + // Use a container class to store everything and handle cleaning up. + SysExecVEState* state = new SysExecVEState; + if ( !state ) { return -1; } + // Make a copy of argv and filename as they are going to be destroyed // when the address space is reset. - filename = String::Clone(filename); - if ( !filename ) { return -1; /* TODO: errno */ } + state->filename = String::Clone(filename); + if ( !state->filename ) { delete state; return -1; } - char** newargv = new char*[argc]; - if ( !newargv ) { delete[] filename; return -1; /* TODO: errno */ } + state->argc = argc; + state->argv = new char*[state->argc]; + Maxsi::Memory::Set(state->argv, 0, sizeof(char*) * state->argc); + if ( !state->argv ) { delete state; return -1; } - for ( int i = 0; i < argc; i++ ) + for ( int i = 0; i < state->argc; i++ ) { - newargv[i] = String::Clone(argv[i]); - if ( !newargv[i] ) - { - while ( i ) { delete[] newargv[--i]; } - - return -1; /* TODO: errno */ - } + state->argv[i] = String::Clone(argv[i]); + if ( !state->argv[i] ) { delete state; return -1; } } - argv = newargv; + state->dev = OpenProgramImage(state->filename); + if ( !state->dev ) { delete state; return -1; } - CPU::InterruptRegisters* regs = Syscall::InterruptRegs(); - Process* process = CurrentProcess(); - int result = process->Execute(filename, argc, argv, regs); - Syscall::AsIs(); + state->dev->Refer(); // TODO: Rules of GC may change soon. + uintmax_t needed = state->dev->Size(); + if ( SIZE_MAX < needed ) { Error::Set(Error::ENOMEM); delete state; return -1; } - for ( int i = 0; i < argc; i++ ) { delete[] argv[i]; } - delete[] argv; - delete[] filename; + state->count = needed; + state->buffer = new byte[state->count]; + if ( !state->buffer ) { delete state; return -1; } - return result; + return SysExevVEStage2(state); } pid_t SysFork() @@ -430,19 +517,19 @@ namespace Sortix } } - void SysExit(int status) + void Process::Exit(int status) { - // Status codes can only contain 8 bits according to ISO C and POSIX. + // Status codes can only contain 8 bits according to ISO C and POSIX. status %= 256; - Process* process = CurrentProcess(); - Process* parent = process->parent; + ASSERT(this == CurrentProcess()); + Process* init = Scheduler::GetInitProcess(); - if ( process->pid == 0 ) { Panic("System idle process exited"); } + if ( pid == 0 ) { Panic("System idle process exited"); } // If the init process terminated successfully, time to halt. - if ( process == init ) + if ( this == init ) { switch ( status ) { @@ -453,11 +540,11 @@ namespace Sortix } // Take care of the orphans, so give them to init. - while ( process->firstchild ) + while ( firstchild ) { - Process* orphan = process->firstchild; - process->firstchild = orphan->nextsibling; - if ( process->firstchild ) { process->firstchild->prevsibling = NULL; } + Process* orphan = firstchild; + firstchild = orphan->nextsibling; + if ( firstchild ) { firstchild->prevsibling = NULL; } orphan->parent = init; orphan->prevsibling = NULL; orphan->nextsibling = init->firstchild; @@ -466,50 +553,55 @@ namespace Sortix } // Remove the current process from the family tree. - if ( !process->prevsibling ) + if ( !prevsibling ) { - parent->firstchild = process->nextsibling; + parent->firstchild = nextsibling; } else { - process->prevsibling->nextsibling = process->nextsibling; + prevsibling->nextsibling = nextsibling; } - if ( process->nextsibling ) + if ( nextsibling ) { - process->nextsibling->prevsibling = process->prevsibling; + nextsibling->prevsibling = prevsibling; } // TODO: Close all the file descriptors! // Make all threads belonging to process unrunnable. - for ( Thread* t = process->firstthread; t; t = t->nextsibling ) + for ( Thread* t = firstthread; t; t = t->nextsibling ) { Scheduler::EarlyWakeUp(t); Scheduler::SetThreadState(t, Thread::State::NONE); } // Delete the threads. - while ( process->firstthread ) + while ( firstthread ) { - Thread* todelete = process->firstthread; - process->firstthread = process->firstthread->nextsibling; + Thread* todelete = firstthread; + firstthread = firstthread->nextsibling; delete todelete; } // Now clean up the address space. - process->ResetAddressSpace(); + ResetAddressSpace(); // TODO: Actually delete the address space. This is a small memory leak // of a couple pages. - process->exitstatus = status; - process->nextsibling = parent->zombiechild; - if ( parent->zombiechild ) { parent->zombiechild->prevsibling = process; } - parent->zombiechild = process; + exitstatus = status; + nextsibling = parent->zombiechild; + if ( parent->zombiechild ) { parent->zombiechild->prevsibling = this; } + parent->zombiechild = this; // Notify the parent process that the child has become a zombie. - parent->OnChildProcessExit(process); + parent->OnChildProcessExit(this); + } + + void SysExit(int status) + { + CurrentProcess()->Exit(status); // And so, the process had vanished from existence. But as fate would // have it, soon a replacement took its place. diff --git a/sortix/process.h b/sortix/process.h index 19facd6b..514b1750 100644 --- a/sortix/process.h +++ b/sortix/process.h @@ -88,8 +88,9 @@ namespace Sortix bool sigint; public: - int Execute(const char* programname, int argc, const char* const* argv, CPU::InterruptRegisters* regs); + int Execute(const char* programname, const byte* program, size_t programsize, int argc, const char* const* argv, CPU::InterruptRegisters* regs); void ResetAddressSpace(); + void Exit(int status); public: bool IsSane() { return addrspace != 0; } diff --git a/sortix/scheduler.cpp b/sortix/scheduler.cpp index 1e398df3..4fbdec08 100644 --- a/sortix/scheduler.cpp +++ b/sortix/scheduler.cpp @@ -30,9 +30,12 @@ #include "scheduler.h" #include "memorymanagement.h" #include "syscall.h" +#include "sound.h" // HACK FOR SIGINT namespace Sortix { + void SysExit(int status); // HACK FOR SIGINT + // Internal forward-declarations. namespace Scheduler { @@ -292,12 +295,16 @@ namespace Sortix if ( currentthread == idlethread ) { return; } hacksigintpending = false; + + // HACK: Don't crash init or sh. + Process* process = CurrentProcess(); + if ( process->pid < 3 ) { return; } + + Sound::Mute(); Log::PrintF("^C\n"); - const char* programname = "sh"; - const char* const argv[] = { "sh" }; - CurrentProcess()->Execute(programname, 1, argv, regs); - return; + process->Exit(130); + currentthread = dummythread; } void SigIntHack()