mirror of
https://github.com/RGBCube/serenity
synced 2025-05-23 22:05:07 +00:00
Implement errno in LibC.
This also meant I had to implement BSS (SHT_NOBITS) sections in ELFLoader. I also added an strerror() so we can print out what the errors are.
This commit is contained in:
parent
434b6a8688
commit
260b14e505
14 changed files with 209 additions and 27 deletions
|
@ -56,6 +56,22 @@ bool ELFLoader::layout()
|
||||||
m_sections.set(section.name(), move(ptr));
|
m_sections.set(section.name(), move(ptr));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
m_image->forEachSectionOfType(SHT_NOBITS, [this, &failed] (const ELFImage::Section& section) {
|
||||||
|
#ifdef ELFLOADER_DEBUG
|
||||||
|
kprintf("[ELFLoader] Allocating nobits section: %s\n", section.name());
|
||||||
|
#endif
|
||||||
|
if (!section.size())
|
||||||
|
return true;
|
||||||
|
char* ptr = m_execSpace.allocateArea(section.name(), section.size());
|
||||||
|
if (!ptr) {
|
||||||
|
kprintf("ELFLoader: failed to allocate section '%s'\n", section.name());
|
||||||
|
failed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memset(ptr, 0, section.size());
|
||||||
|
m_sections.set(section.name(), move(ptr));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
return !failed;
|
return !failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <VirtualFileSystem/VirtualFileSystem.h>
|
#include <VirtualFileSystem/VirtualFileSystem.h>
|
||||||
#include <ELFLoader/ExecSpace.h>
|
#include <ELFLoader/ExecSpace.h>
|
||||||
#include "MemoryManager.h"
|
#include "MemoryManager.h"
|
||||||
|
#include "errno.h"
|
||||||
|
|
||||||
//#define DEBUG_IO
|
//#define DEBUG_IO
|
||||||
//#define TASK_DEBUG
|
//#define TASK_DEBUG
|
||||||
|
@ -166,25 +167,32 @@ int Task::sys$munmap(void* addr, size_t size)
|
||||||
|
|
||||||
int Task::sys$spawn(const char* path)
|
int Task::sys$spawn(const char* path)
|
||||||
{
|
{
|
||||||
auto* child = Task::createUserTask(path, m_uid, m_gid, m_pid);
|
int error = 0;
|
||||||
|
auto* child = Task::createUserTask(path, m_uid, m_gid, m_pid, error);
|
||||||
if (child)
|
if (child)
|
||||||
return child->pid();
|
return child->pid();
|
||||||
return -1;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task* Task::createUserTask(const String& path, uid_t uid, gid_t gid, pid_t parentPID)
|
Task* Task::createUserTask(const String& path, uid_t uid, gid_t gid, pid_t parentPID, int& error)
|
||||||
{
|
{
|
||||||
auto parts = path.split('/');
|
auto parts = path.split('/');
|
||||||
if (parts.isEmpty())
|
if (parts.isEmpty()) {
|
||||||
|
error = -ENOENT;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto handle = VirtualFileSystem::the().open(path);
|
auto handle = VirtualFileSystem::the().open(path);
|
||||||
if (!handle)
|
if (!handle) {
|
||||||
|
error = -ENOENT; // FIXME: Get a more detailed error from VFS.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto elfData = handle->readEntireFile();
|
auto elfData = handle->readEntireFile();
|
||||||
if (!elfData)
|
if (!elfData) {
|
||||||
|
error = -EIO; // FIXME: Get a more detailed error from VFS.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
InterruptDisabler disabler; // FIXME: Get rid of this, jesus christ. This "critical" section is HUGE.
|
InterruptDisabler disabler; // FIXME: Get rid of this, jesus christ. This "critical" section is HUGE.
|
||||||
Task* t = new Task(parts.takeLast(), uid, gid, parentPID, Ring3);
|
Task* t = new Task(parts.takeLast(), uid, gid, parentPID, Ring3);
|
||||||
|
@ -203,12 +211,14 @@ Task* Task::createUserTask(const String& path, uid_t uid, gid_t gid, pid_t paren
|
||||||
if (!success) {
|
if (!success) {
|
||||||
delete t;
|
delete t;
|
||||||
kprintf("Failure loading ELF %s\n", path.characters());
|
kprintf("Failure loading ELF %s\n", path.characters());
|
||||||
|
error = -ENOEXEC;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
t->m_tss.eip = (dword)space.symbolPtr("_start");
|
t->m_tss.eip = (dword)space.symbolPtr("_start");
|
||||||
if (!t->m_tss.eip) {
|
if (!t->m_tss.eip) {
|
||||||
delete t;
|
delete t;
|
||||||
|
error = -ENOEXEC;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,6 +231,7 @@ Task* Task::createUserTask(const String& path, uid_t uid, gid_t gid, pid_t paren
|
||||||
kprintf("Task %u (%s) spawned @ %p\n", t->pid(), t->name().characters(), t->m_tss.eip);
|
kprintf("Task %u (%s) spawned @ %p\n", t->pid(), t->name().characters(), t->m_tss.eip);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
error = 0;
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,11 +769,6 @@ Task* Task::kernelTask()
|
||||||
return s_kernelTask;
|
return s_kernelTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Task::setError(int error)
|
|
||||||
{
|
|
||||||
m_error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
Task::Region::Region(LinearAddress a, size_t s, RetainPtr<Zone>&& z, String&& n)
|
Task::Region::Region(LinearAddress a, size_t s, RetainPtr<Zone>&& z, String&& n)
|
||||||
: linearAddress(a)
|
: linearAddress(a)
|
||||||
, size(s)
|
, size(s)
|
||||||
|
|
|
@ -16,7 +16,7 @@ class Task : public InlineLinkedListNode<Task> {
|
||||||
friend class InlineLinkedListNode<Task>;
|
friend class InlineLinkedListNode<Task>;
|
||||||
public:
|
public:
|
||||||
static Task* createKernelTask(void (*entry)(), String&& name);
|
static Task* createKernelTask(void (*entry)(), String&& name);
|
||||||
static Task* createUserTask(const String& path, uid_t, gid_t, pid_t parentPID);
|
static Task* createUserTask(const String& path, uid_t, gid_t, pid_t parentPID, int& error);
|
||||||
~Task();
|
~Task();
|
||||||
|
|
||||||
static Vector<Task*> allTasks();
|
static Vector<Task*> allTasks();
|
||||||
|
@ -100,7 +100,6 @@ public:
|
||||||
int sys$getcwd(char*, size_t);
|
int sys$getcwd(char*, size_t);
|
||||||
|
|
||||||
static void initialize();
|
static void initialize();
|
||||||
void setError(int);
|
|
||||||
|
|
||||||
static void taskDidCrash(Task*);
|
static void taskDidCrash(Task*);
|
||||||
|
|
||||||
|
|
Binary file not shown.
38
Kernel/errno.h
Normal file
38
Kernel/errno.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define EPERM 1 // Operation not permitted
|
||||||
|
#define ENOENT 2 // No such file or directory
|
||||||
|
#define ESRCH 3 // No such process
|
||||||
|
#define EINTR 4 // Interrupted system call
|
||||||
|
#define EIO 5 // I/O error
|
||||||
|
#define ENXIO 6 // No such device or address
|
||||||
|
#define E2BIG 7 // Argument list too long
|
||||||
|
#define ENOEXEC 8 // Exec format error
|
||||||
|
#define EBADF 9 // Bad file number
|
||||||
|
#define ECHILD 10 // No child processes
|
||||||
|
#define EAGAIN 11 // Try again
|
||||||
|
#define ENOMEM 12 // Out of memory
|
||||||
|
#define EACCES 13 // Permission denied
|
||||||
|
#define EFAULT 14 // Bad address
|
||||||
|
#define ENOTBLK 15 // Block device required
|
||||||
|
#define EBUSY 16 // Device or resource busy
|
||||||
|
#define EEXIST 17 // File exists
|
||||||
|
#define EXDEV 18 // Cross-device link
|
||||||
|
#define ENODEV 19 // No such device
|
||||||
|
#define ENOTDIR 20 // Not a directory
|
||||||
|
#define EISDIR 21 // Is a directory
|
||||||
|
#define EINVAL 22 // Invalid argument
|
||||||
|
#define ENFILE 23 // File table overflow
|
||||||
|
#define EMFILE 24 // Too many open files
|
||||||
|
#define ENOTTY 25 // Not a typewriter
|
||||||
|
#define ETXTBSY 26 // Text file busy
|
||||||
|
#define EFBIG 27 // File too large
|
||||||
|
#define ENOSPC 28 // No space left on device
|
||||||
|
#define ESPIPE 29 // Illegal seek
|
||||||
|
#define EROFS 30 // Read-only file system
|
||||||
|
#define EMLINK 31 // Too many links
|
||||||
|
#define EPIPE 32 // Broken pipe
|
||||||
|
#define EDOM 33 // Math argument out of domain of func
|
||||||
|
#define ERANGE 34 // Math result not representable
|
||||||
|
|
||||||
|
#define EOVERFLOW 75 // Value too large for defined data type
|
|
@ -140,7 +140,8 @@ static void init_stage2()
|
||||||
dword lastAlloc = sum_alloc;
|
dword lastAlloc = sum_alloc;
|
||||||
|
|
||||||
for (unsigned i = 0; i < 100; ++i) {
|
for (unsigned i = 0; i < 100; ++i) {
|
||||||
auto* shTask = Task::createUserTask("/bin/id", (uid_t)100, (gid_t)100, (pid_t)0);
|
int error;
|
||||||
|
auto* shTask = Task::createUserTask("/bin/id", (uid_t)100, (gid_t)100, (pid_t)0, error);
|
||||||
kprintf("malloc stats: alloc:%u free:%u\n", sum_alloc, sum_free);
|
kprintf("malloc stats: alloc:%u free:%u\n", sum_alloc, sum_free);
|
||||||
kprintf("sizeof(Task):%u\n", sizeof(Task));
|
kprintf("sizeof(Task):%u\n", sizeof(Task));
|
||||||
kprintf("delta:%u\n",sum_alloc - lastAlloc);
|
kprintf("delta:%u\n",sum_alloc - lastAlloc);
|
||||||
|
@ -149,7 +150,8 @@ static void init_stage2()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto* shTask = Task::createUserTask("/bin/sh", (uid_t)100, (gid_t)100, (pid_t)0);
|
int error;
|
||||||
|
auto* shTask = Task::createUserTask("/bin/sh", (uid_t)100, (gid_t)100, (pid_t)0, error);
|
||||||
|
|
||||||
banner();
|
banner();
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,12 @@
|
||||||
|
|
||||||
extern "C" int main(int, char**);
|
extern "C" int main(int, char**);
|
||||||
|
|
||||||
|
int errno;
|
||||||
|
|
||||||
extern "C" int _start()
|
extern "C" int _start()
|
||||||
{
|
{
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
// FIXME: Pass appropriate argc/argv.
|
// FIXME: Pass appropriate argc/argv.
|
||||||
int status = main(0, nullptr);
|
int status = main(0, nullptr);
|
||||||
|
|
||||||
|
|
57
LibC/errno.h
Normal file
57
LibC/errno.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define EPERM 1 // Operation not permitted
|
||||||
|
#define ENOENT 2 // No such file or directory
|
||||||
|
#define ESRCH 3 // No such process
|
||||||
|
#define EINTR 4 // Interrupted system call
|
||||||
|
#define EIO 5 // I/O error
|
||||||
|
#define ENXIO 6 // No such device or address
|
||||||
|
#define E2BIG 7 // Argument list too long
|
||||||
|
#define ENOEXEC 8 // Exec format error
|
||||||
|
#define EBADF 9 // Bad file number
|
||||||
|
#define ECHILD 10 // No child processes
|
||||||
|
#define EAGAIN 11 // Try again
|
||||||
|
#define ENOMEM 12 // Out of memory
|
||||||
|
#define EACCES 13 // Permission denied
|
||||||
|
#define EFAULT 14 // Bad address
|
||||||
|
#define ENOTBLK 15 // Block device required
|
||||||
|
#define EBUSY 16 // Device or resource busy
|
||||||
|
#define EEXIST 17 // File exists
|
||||||
|
#define EXDEV 18 // Cross-device link
|
||||||
|
#define ENODEV 19 // No such device
|
||||||
|
#define ENOTDIR 20 // Not a directory
|
||||||
|
#define EISDIR 21 // Is a directory
|
||||||
|
#define EINVAL 22 // Invalid argument
|
||||||
|
#define ENFILE 23 // File table overflow
|
||||||
|
#define EMFILE 24 // Too many open files
|
||||||
|
#define ENOTTY 25 // Not a typewriter
|
||||||
|
#define ETXTBSY 26 // Text file busy
|
||||||
|
#define EFBIG 27 // File too large
|
||||||
|
#define ENOSPC 28 // No space left on device
|
||||||
|
#define ESPIPE 29 // Illegal seek
|
||||||
|
#define EROFS 30 // Read-only file system
|
||||||
|
#define EMLINK 31 // Too many links
|
||||||
|
#define EPIPE 32 // Broken pipe
|
||||||
|
#define EDOM 33 // Math argument out of domain of func
|
||||||
|
#define ERANGE 34 // Math result not representable
|
||||||
|
|
||||||
|
#define EOVERFLOW 75 // Value too large for defined data type
|
||||||
|
|
||||||
|
|
||||||
|
#define __RETURN_WITH_ERRNO(rc, good_ret, bad_ret) \
|
||||||
|
do { \
|
||||||
|
if (rc < 0) { \
|
||||||
|
errno = -rc; \
|
||||||
|
return (bad_ret); \
|
||||||
|
} else { \
|
||||||
|
errno = 0; \
|
||||||
|
return (good_ret); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
extern int errno;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
#include "mman.h"
|
#include "mman.h"
|
||||||
|
#include "errno.h"
|
||||||
#include <Kernel/Syscall.h>
|
#include <Kernel/Syscall.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
void* mmap(void* addr, size_t size)
|
void* mmap(void* addr, size_t size)
|
||||||
{
|
{
|
||||||
return (void*)Syscall::invoke(Syscall::PosixMmap, (dword)addr, (dword)size);
|
int rc = Syscall::invoke(Syscall::PosixMmap, (dword)addr, (dword)size);
|
||||||
|
__RETURN_WITH_ERRNO(rc, (void*)rc, (void*)-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int munmap(void* addr, size_t size)
|
int munmap(void* addr, size_t size)
|
||||||
{
|
{
|
||||||
return Syscall::invoke(Syscall::PosixMunmap, (dword)addr, (dword)size);
|
int rc = Syscall::invoke(Syscall::PosixMunmap, (dword)addr, (dword)size);
|
||||||
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
|
#include "errno.h"
|
||||||
#include <Kernel/Syscall.h>
|
#include <Kernel/Syscall.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
int spawn(const char* path)
|
int spawn(const char* path)
|
||||||
{
|
{
|
||||||
return Syscall::invoke(Syscall::Spawn, (dword)path);
|
int rc = Syscall::invoke(Syscall::Spawn, (dword)path);
|
||||||
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
#include "errno.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
@ -10,5 +12,49 @@ size_t strlen(const char* str)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* strerror(int errnum)
|
||||||
|
{
|
||||||
|
switch (errnum) {
|
||||||
|
case 0: return "No error";
|
||||||
|
case EPERM: return "Operation not permitted";
|
||||||
|
case ENOENT: return "No such file or directory";
|
||||||
|
case ESRCH: return "No such process";
|
||||||
|
case EINTR: return "Interrupted syscall";
|
||||||
|
case EIO: return "I/O error";
|
||||||
|
case ENXIO: return "No such device/address";
|
||||||
|
case E2BIG: return "Argument list too long";
|
||||||
|
case ENOEXEC: return "Exec format error";
|
||||||
|
case EBADF: return "Bad fd number";
|
||||||
|
case ECHILD: return "No child processes";
|
||||||
|
case EAGAIN: return "Try again";
|
||||||
|
case ENOMEM: return "Out of memory";
|
||||||
|
case EACCES: return "Access denied";
|
||||||
|
case EFAULT: return "Bad address";
|
||||||
|
case ENOTBLK: return "Not a block device";
|
||||||
|
case EBUSY: return "Resource busy";
|
||||||
|
case EEXIST: return "File already exists";
|
||||||
|
case EXDEV: return "Cross-device link";
|
||||||
|
case ENODEV: return "No such device";
|
||||||
|
case ENOTDIR: return "Not a directory";
|
||||||
|
case EISDIR: return "Is a directory";
|
||||||
|
case EINVAL: return "Invalid argument";
|
||||||
|
case ENFILE: return "File table overflow";
|
||||||
|
case EMFILE: return "Too many open files";
|
||||||
|
case ENOTTY: return "Not a TTY";
|
||||||
|
case ETXTBSY: return "Text file busy";
|
||||||
|
case EFBIG: return "File too big";
|
||||||
|
case ENOSPC: return "No space left";
|
||||||
|
case ESPIPE: return "Illegal seek";
|
||||||
|
case EROFS: return "File system is read-only";
|
||||||
|
case EMLINK: return "Too many links";
|
||||||
|
case EPIPE: return "Broken pipe";
|
||||||
|
case EDOM: return "Math argument out of domain";
|
||||||
|
case ERANGE: return "Math result not representable";
|
||||||
|
case EOVERFLOW: return "Value too large for data type";
|
||||||
|
}
|
||||||
|
printf("strerror() missing string for errnum=%d\n", errnum);
|
||||||
|
return "Unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
size_t strlen(const char*);
|
size_t strlen(const char*);
|
||||||
|
const char* strerror(int errnum);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "unistd.h"
|
#include "unistd.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
#include "errno.h"
|
||||||
#include <Kernel/Syscall.h>
|
#include <Kernel/Syscall.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -22,35 +23,38 @@ pid_t getpid()
|
||||||
int open(const char* path)
|
int open(const char* path)
|
||||||
{
|
{
|
||||||
size_t length = strlen(path);
|
size_t length = strlen(path);
|
||||||
return Syscall::invoke(Syscall::PosixOpen, (dword)path, (dword)length);
|
int rc = Syscall::invoke(Syscall::PosixOpen, (dword)path, (dword)length);
|
||||||
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t read(int fd, void* buf, size_t count)
|
ssize_t read(int fd, void* buf, size_t count)
|
||||||
{
|
{
|
||||||
return Syscall::invoke(Syscall::PosixRead, (dword)fd, (dword)buf, (dword)count);
|
int rc = Syscall::invoke(Syscall::PosixRead, (dword)fd, (dword)buf, (dword)count);
|
||||||
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int close(int fd)
|
int close(int fd)
|
||||||
{
|
{
|
||||||
return Syscall::invoke(Syscall::PosixClose, fd);
|
int rc = Syscall::invoke(Syscall::PosixClose, fd);
|
||||||
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t waitpid(pid_t waitee)
|
pid_t waitpid(pid_t waitee)
|
||||||
{
|
{
|
||||||
return Syscall::invoke(Syscall::PosixWaitpid, waitee);
|
int rc = Syscall::invoke(Syscall::PosixWaitpid, waitee);
|
||||||
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lstat(const char* path, stat* statbuf)
|
int lstat(const char* path, stat* statbuf)
|
||||||
{
|
{
|
||||||
return Syscall::invoke(Syscall::PosixLstat, (dword)path, (dword)statbuf);
|
int rc = Syscall::invoke(Syscall::PosixLstat, (dword)path, (dword)statbuf);
|
||||||
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* getcwd(char* buffer, size_t size)
|
char* getcwd(char* buffer, size_t size)
|
||||||
{
|
{
|
||||||
int rc = Syscall::invoke(Syscall::PosixGetcwd, (dword)buffer, (dword)size);
|
int rc = Syscall::invoke(Syscall::PosixGetcwd, (dword)buffer, (dword)size);
|
||||||
if (rc != 0)
|
__RETURN_WITH_ERRNO(rc, buffer, nullptr);
|
||||||
return nullptr;
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include <LibC/stdio.h>
|
#include <LibC/stdio.h>
|
||||||
#include <LibC/unistd.h>
|
#include <LibC/unistd.h>
|
||||||
#include <LibC/process.h>
|
#include <LibC/process.h>
|
||||||
|
#include <LibC/errno.h>
|
||||||
|
#include <LibC/string.h>
|
||||||
|
|
||||||
static void prompt()
|
static void prompt()
|
||||||
{
|
{
|
||||||
|
@ -12,12 +14,14 @@ static void prompt()
|
||||||
|
|
||||||
static int runcmd(char* cmd)
|
static int runcmd(char* cmd)
|
||||||
{
|
{
|
||||||
|
if (cmd[0] == 0)
|
||||||
|
return 0;
|
||||||
//printf("command: '%s'\n", cmd);
|
//printf("command: '%s'\n", cmd);
|
||||||
char buf[128];
|
char buf[128];
|
||||||
sprintf(buf, "/bin/%s", cmd);
|
sprintf(buf, "/bin/%s", cmd);
|
||||||
int ret = spawn(buf);
|
int ret = spawn(buf);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
printf("spawn failed: %s\n", cmd);
|
printf("spawn failed: %s (%s)\n", cmd, strerror(errno));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
waitpid(ret);
|
waitpid(ret);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue