execve(2) can now load programs from the filesystem.

Previously it was restricted to only the ramdisk.
This commit is contained in:
Jonas 'Sortie' Termansen 2011-11-22 13:53:36 +01:00
parent 324a9a1a22
commit e8fb8d885b
6 changed files with 180 additions and 71 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -23,12 +23,16 @@
******************************************************************************/
#include "platform.h"
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#include <libmaxsi/string.h>
#include <libmaxsi/sortedlist.h>
#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.

View File

@ -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; }

View File

@ -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()