mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
execve(2) can now load programs from the filesystem.
Previously it was restricted to only the ramdisk.
This commit is contained in:
parent
324a9a1a22
commit
e8fb8d885b
6 changed files with 180 additions and 71 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue