Compare commits

...

16 Commits

Author SHA1 Message Date
Bananymous 1f467580ee Userspace: Add test for popen 2024-02-14 17:23:26 +02:00
Bananymous 1ba883719a LibC: Implement popen and pclose 2024-02-14 17:22:45 +02:00
Bananymous f73e954b28 Kernel: Remove SpinLock from Pipe
Pipe already is using lock on the inode. If you read from pipe when
there was no data, pipe blocked indefinately since writes were blocked
by Inode::m_lock.
2024-02-14 17:21:32 +02:00
Bananymous de629291b9 LibC: Implement freopen, rewind and fix bugs in code
Now everything will be properly locked once threads are implemented.
All functions "lock" the stream for the wanted operation
2024-02-14 16:36:48 +02:00
Bananymous 7eb5d220fd Userspace: Implement getopt for testing libc getopt() 2024-02-14 15:01:27 +02:00
Bananymous 4cd9abdd15 LibC: Implement getopt() 2024-02-14 15:00:58 +02:00
Bananymous 198dde8365 Kernel: Add klibc for kernel
Now building same source as libc is not needed and libc doesn't
have to do hacks to allow kernel compilation
2024-02-14 15:00:04 +02:00
Bananymous b165340662 Kernel: Don't use strcat in kernel code 2024-02-14 14:59:13 +02:00
Bananymous 5ad4340679 BAN: Use strerrordesc_np instead of strerror 2024-02-14 14:58:27 +02:00
Bananymous b56fa4a29d LibC: Implement fscanf
I had missed this when I was implementing *scanf functions
2024-02-14 14:39:22 +02:00
Bananymous e946b392c9 LibC: Add definition for S_IFMT
I was using S_IFMASK, but linux seems to use this
2024-02-14 14:39:22 +02:00
Bananymous 81689b5f02 LibC: Implement most of missing functions from string.h
only strcoll*, strxfrm* and strerror_l are left unimplemented
2024-02-14 14:39:22 +02:00
Bananymous c18d926174 LibC: Fix timeval field name 2024-02-14 03:36:18 +02:00
Bananymous 00662bad46 Kernel: Rewrite HPET code
Now the set timer frequency actually works... :D
2024-02-13 17:59:48 +02:00
Bananymous 420a7b60ca resolver: use select for client communication 2024-02-12 23:47:39 +02:00
Bananymous 2ab3eb4109 Kernel: Fix bugs in select
Unix domain socket is now select readable when it has pending
connection
2024-02-12 23:46:27 +02:00
24 changed files with 1273 additions and 271 deletions

View File

@ -62,7 +62,9 @@ namespace BAN
if (m_error_code & kernel_error_mask)
return Kernel::error_string(kernel_error());
#endif
return strerror(m_error_code);
if (auto* desc = strerrordesc_np(m_error_code))
return desc;
return "Unknown error"sv;
}
private:

View File

@ -141,9 +141,9 @@ set(BAN_SOURCES
../BAN/BAN/Time.cpp
)
set(LIBC_SOURCES
../libc/ctype.cpp
../libc/string.cpp
set(KLIBC_SOURCES
klibc/ctype.cpp
klibc/string.cpp
)
set(LIBELF_SOURCES
@ -154,7 +154,7 @@ set(KERNEL_SOURCES
${KERNEL_SOURCES}
${LAI_SOURCES}
${BAN_SOURCES}
${LIBC_SOURCES}
${KLIBC_SOURCES}
${LIBELF_SOURCES}
)

View File

@ -48,7 +48,6 @@ namespace Kernel
timespec m_mtime {};
timespec m_ctime {};
BAN::Vector<uint8_t> m_buffer;
SpinLock m_lock;
Semaphore m_semaphore;
uint32_t m_writing_count { 1 };

View File

@ -6,10 +6,13 @@
namespace Kernel
{
struct HPETRegisters;
class HPET final : public Timer, public Interruptable
{
public:
static BAN::ErrorOr<BAN::UniqPtr<HPET>> create(bool force_pic);
~HPET();
virtual uint64_t ms_since_boot() const override;
virtual uint64_t ns_since_boot() const override;
@ -21,11 +24,19 @@ namespace Kernel
HPET() = default;
BAN::ErrorOr<void> initialize(bool force_pic);
void write_register(ptrdiff_t reg, uint64_t value) const;
uint64_t read_register(ptrdiff_t reg) const;
volatile HPETRegisters& registers();
const volatile HPETRegisters& registers() const;
uint64_t read_main_counter() const;
private:
uint64_t m_counter_tick_period_fs { 0 };
bool m_is_64bit { false };
uint64_t m_last_ticks { 0 };
uint32_t m_32bit_wraps { 0 };
uint32_t m_ticks_per_s { 0 };
vaddr_t m_mmio_base { 0 };
};

View File

@ -253,6 +253,8 @@ namespace Kernel
if (m_info.has<ConnectionInfo>())
{
auto& connection_info = m_info.get<ConnectionInfo>();
if (!connection_info.pending_connections.empty())
return true;
if (!connection_info.connection)
return false;
}

View File

@ -1048,8 +1048,8 @@ namespace Kernel
fd_set writefds; FD_ZERO(&writefds);
fd_set errorfds; FD_ZERO(&errorfds);
long set_bits = 0;
while (set_bits == 0)
int set_bits = 0;
for (;;)
{
if (arguments->timeout && SystemTimer::get().ms_since_boot() >= timedout_ms)
break;
@ -1072,7 +1072,7 @@ namespace Kernel
if (!mode.ifreg() && !mode.ififo() && !mode.ifsock() && !inode->is_pipe() && !inode->is_tty())
return;
if ((inode_or_error.value().ptr()->*func)())
if ((inode.ptr()->*func)())
{
FD_SET(fd, dest);
set_bits++;
@ -1083,18 +1083,33 @@ namespace Kernel
{
update_fds(i, arguments->readfds, &readfds, &Inode::can_read);
update_fds(i, arguments->writefds, &writefds, &Inode::can_write);
update_fds(i, arguments->errorfds, &errorfds, &Inode::can_read);
update_fds(i, arguments->errorfds, &errorfds, &Inode::has_error);
}
if (set_bits > 0)
break;
LockFreeGuard free(m_lock);
SystemTimer::get().sleep(1);
}
if (arguments->readfds)
memcpy(arguments->readfds, &readfds, sizeof(fd_set));
FD_ZERO(arguments->readfds);
if (arguments->writefds)
memcpy(arguments->writefds, &writefds, sizeof(fd_set));
FD_ZERO(arguments->writefds);
if (arguments->errorfds)
memcpy(arguments->errorfds, &errorfds, sizeof(fd_set));
FD_ZERO(arguments->errorfds);
for (int i = 0; i < arguments->nfds; i++)
{
if (arguments->readfds && FD_ISSET(i, &readfds))
FD_SET(i, arguments->readfds);
if (arguments->writefds && FD_ISSET(i, &writefds))
FD_SET(i, arguments->writefds);
if (arguments->errorfds && FD_ISSET(i, &errorfds))
FD_SET(i, arguments->errorfds);
}
return set_bits;
}

View File

@ -46,15 +46,16 @@ namespace Kernel
BAN::ErrorOr<void> NVMeNamespace::initialize()
{
BAN::String name_prefix;
TRY(name_prefix.append(m_name));
TRY(name_prefix.push_back('p'));
m_dma_region = TRY(DMARegion::create(PAGE_SIZE));
add_disk_cache();
DevFileSystem::get().add_device(this);
char name_prefix[20];
strcpy(name_prefix, m_name);
strcat(name_prefix, "p");
if (auto res = initialize_partitions(name_prefix); res.is_error())
dprintln("{}", res.error());

View File

@ -7,21 +7,7 @@
#include <kernel/Scheduler.h>
#include <kernel/Timer/HPET.h>
#define HPET_REG_CAPABILIES 0x00
#define HPET_REG_CONFIG 0x10
#define HPET_REG_COUNTER 0xF0
#define HPET_CONFIG_ENABLE 0x01
#define HPET_CONFIG_LEG_RT 0x02
#define HPET_REG_TIMER_CONFIG(N) (0x100 + 0x20 * N)
#define HPET_REG_TIMER_COMPARATOR(N) (0x108 + 0x20 * N)
#define HPET_Tn_INT_ENB_CNF (1 << 2)
#define HPET_Tn_TYPE_CNF (1 << 3)
#define HPET_Tn_PER_INT_CAP (1 << 4)
#define HPET_Tn_VAL_SET_CNF (1 << 6)
#define HPET_Tn_INT_ROUTE_CNF_SHIFT 9
#define HPET_PERIOD_MAX 0x05F5E100
#define FS_PER_S 1'000'000'000'000'000
#define FS_PER_MS 1'000'000'000'000
@ -31,17 +17,116 @@
namespace Kernel
{
enum HPETCapabilities : uint32_t
{
LEG_RT_CAP = 1 << 16,
COUNT_SIZE_CAP = 1 << 13,
NUM_TIM_CAP_MASK = 0x1F << 8,
NUM_TIM_CAP_SHIFT = 8,
};
enum HPETConfiguration : uint32_t
{
LEG_RT_CNF = 1 << 1,
ENABLE_CNF = 1 << 0,
};
enum HPETTimerConfiguration : uint32_t
{
Tn_INT_TYPE_CNF = 1 << 1,
Tn_INT_ENB_CNF = 1 << 2,
Tn_TYPE_CNF = 1 << 3,
Tn_PER_INT_CAP = 1 << 4,
Tn_SIZE_CAP = 1 << 5,
Tn_VAL_SET_CNF = 1 << 6,
Tn_32MODE_CNF = 1 << 8,
Tn_FSB_EN_CNF = 1 << 14,
Tn_FSB_INT_DEL_CAP = 1 << 14,
Tn_INT_ROUTE_CNF_MASK = 0x1F << 9,
Tn_INT_ROUTE_CNF_SHIFT = 9,
};
struct HPETRegister
{
union
{
uint64_t full;
struct
{
uint32_t low;
uint32_t high;
};
};
};
static_assert(sizeof(HPETRegister) == 8);
struct HPETTimer
{
uint32_t configuration;
uint32_t int_route_cap;
HPETRegister comparator;
HPETRegister fsb_interrupt_route;
uint64_t __reserved;
};
static_assert(sizeof(HPETTimer) == 32);
struct HPETRegisters
{
/*
63:32 COUNTER_CLK_PERIOD
31:16 VENDOR_ID
15 LEG_RT_CAP
13 COUNT_SIZE_CAP
12:8 NUM_TIM_CAP
7:0 REV_ID
*/
uint32_t capabilities;
uint32_t counter_clk_period;
uint64_t __reserved0;
/*
1 LEG_RT_CNF
0 ENABLE_CNF
*/
HPETRegister configuration;
uint64_t __reserved1;
/*
N Tn_INT_STS
*/
HPETRegister interrupt_status;
uint8_t __reserved2[0xF0 - 0x28];
HPETRegister main_counter;
uint64_t __reserved3;
HPETTimer timers[32];
};
static_assert(offsetof(HPETRegisters, main_counter) == 0xF0);
static_assert(offsetof(HPETRegisters, timers[0]) == 0x100);
static_assert(offsetof(HPETRegisters, timers[1]) == 0x120);
BAN::ErrorOr<BAN::UniqPtr<HPET>> HPET::create(bool force_pic)
{
HPET* hpet = new HPET();
if (hpet == nullptr)
HPET* hpet_ptr = new HPET();
if (hpet_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
if (auto ret = hpet->initialize(force_pic); ret.is_error())
{
delete hpet;
return ret.release_error();
}
return BAN::UniqPtr<HPET>::adopt(hpet);
auto hpet = BAN::UniqPtr<HPET>::adopt(hpet_ptr);
TRY(hpet->initialize(force_pic));
return hpet;
}
HPET::~HPET()
{
if (m_mmio_base)
PageTable::kernel().unmap_page(m_mmio_base);
m_mmio_base = 0;
}
BAN::ErrorOr<void> HPET::initialize(bool force_pic)
@ -63,115 +148,155 @@ namespace Kernel
ASSERT(m_mmio_base);
PageTable::kernel().map_page_at(header->base_address.address, m_mmio_base, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
BAN::ScopeGuard unmapper([this] { PageTable::kernel().unmap_page(m_mmio_base); });
m_counter_tick_period_fs = read_register(HPET_REG_CAPABILIES) >> 32;
auto& regs = registers();
// period has to be less than 100 ns
if (m_counter_tick_period_fs == 0 || m_counter_tick_period_fs > FS_PER_NS * 100)
return BAN::Error::from_errno(EINVAL);
m_is_64bit = regs.capabilities & COUNT_SIZE_CAP;
uint64_t ticks_per_ms = FS_PER_MS / m_counter_tick_period_fs;
// Disable main counter and reset value
regs.configuration.low = regs.configuration.low & ~ENABLE_CNF;
regs.main_counter.high = 0;
regs.main_counter.low = 0;
// Enable legacy routing if available
if (regs.capabilities & LEG_RT_CAP)
regs.configuration.low = regs.configuration.low | LEG_RT_CNF;
uint32_t period_fs = regs.counter_clk_period;
if (period_fs == 0 || period_fs > HPET_PERIOD_MAX)
{
const char* units[] = { "fs", "ps", "ns" };
int index = 0;
uint64_t temp = m_counter_tick_period_fs;
while (temp >= 1000)
{
temp /= 1000;
index++;
}
dprintln("HPET percision {} {}", temp, units[index]);
dwarnln("HPET: Invalid counter period");
return BAN::Error::from_errno(EINVAL);
}
uint64_t timer0_config = read_register(HPET_REG_TIMER_CONFIG(0));
if (!(timer0_config & HPET_Tn_PER_INT_CAP))
m_ticks_per_s = FS_PER_S / period_fs;
dprintln("HPET frequency {} Hz", m_ticks_per_s);
uint8_t last_timer = (regs.capabilities & NUM_TIM_CAP_MASK) >> NUM_TIM_CAP_SHIFT;
dprintln("HPET has {} timers", last_timer + 1);
// Disable all timers
for (uint8_t i = 0; i <= last_timer; i++)
{
dwarnln("timer 0 doesn't support periodic");
auto& timer_regs = regs.timers[i];
timer_regs.configuration = timer_regs.configuration & ~Tn_INT_ENB_CNF;
}
auto& timer0 = regs.timers[0];
if (!(timer0.configuration & Tn_PER_INT_CAP))
{
dwarnln("HPET: timer0 cannot be periodic");
return BAN::Error::from_errno(ENOTSUP);
}
int irq = 0;
if (!force_pic)
// enable interrupts
timer0.configuration = timer0.configuration | Tn_INT_ENB_CNF;
// clear interrupt mask (set irq to 0)
timer0.configuration = timer0.configuration & ~Tn_INT_ROUTE_CNF_MASK;
// edge triggered interrupts
timer0.configuration = timer0.configuration & ~Tn_INT_TYPE_CNF;
// periodic timer
timer0.configuration = timer0.configuration | Tn_TYPE_CNF;
// disable 32 bit mode
timer0.configuration = timer0.configuration & ~Tn_32MODE_CNF;
// disable FSB interrupts
if (timer0.configuration & Tn_FSB_INT_DEL_CAP)
timer0.configuration = timer0.configuration & ~Tn_FSB_EN_CNF;
// set timer period to 1000 Hz
uint64_t ticks_per_ms = m_ticks_per_s / 1000;
timer0.configuration = timer0.configuration | Tn_VAL_SET_CNF;
timer0.comparator.low = ticks_per_ms;
if (timer0.configuration & Tn_SIZE_CAP)
{
uint32_t irq_cap = timer0_config >> 32;
if (irq_cap == 0)
{
dwarnln("HPET doesn't have any interrupts available");
return BAN::Error::from_errno(EINVAL);
}
for (irq = 0; irq < 32; irq++)
if (irq_cap & (1 << irq))
break;
timer0.configuration = timer0.configuration | Tn_VAL_SET_CNF;
timer0.comparator.high = ticks_per_ms >> 32;
}
else if (ticks_per_ms > 0xFFFFFFFF)
{
dprintln("HPET: cannot create 1 kHz timer");
return BAN::Error::from_errno(ENOTSUP);
}
TRY(InterruptController::get().reserve_irq(irq));
unmapper.disable();
// enable main counter
regs.configuration.low = regs.configuration.low | ENABLE_CNF;
uint64_t main_flags = HPET_CONFIG_ENABLE;
if (force_pic)
main_flags |= HPET_CONFIG_LEG_RT;
// Enable main counter
write_register(HPET_REG_CONFIG, read_register(HPET_REG_CONFIG) | main_flags);
uint64_t timer0_flags = 0;
timer0_flags |= HPET_Tn_INT_ENB_CNF;
timer0_flags |= HPET_Tn_TYPE_CNF;
timer0_flags |= HPET_Tn_VAL_SET_CNF;
if (!force_pic)
timer0_flags |= irq << HPET_Tn_INT_ROUTE_CNF_SHIFT;
// Enable timer 0 as 1 ms periodic
write_register(HPET_REG_TIMER_CONFIG(0), timer0_flags);
write_register(HPET_REG_TIMER_COMPARATOR(0), read_register(HPET_REG_COUNTER) + ticks_per_ms);
write_register(HPET_REG_TIMER_COMPARATOR(0), ticks_per_ms);
// Disable timers 1->
for (int i = 1; i <= header->comparator_count; i++)
write_register(HPET_REG_TIMER_CONFIG(i), 0);
set_irq(irq);
TRY(InterruptController::get().reserve_irq(0));
set_irq(0);
enable_interrupt();
return {};
}
volatile HPETRegisters& HPET::registers()
{
return *reinterpret_cast<volatile HPETRegisters*>(m_mmio_base);
}
const volatile HPETRegisters& HPET::registers() const
{
return *reinterpret_cast<const volatile HPETRegisters*>(m_mmio_base);
}
uint64_t HPET::read_main_counter() const
{
auto& regs = registers();
if (m_is_64bit)
return regs.main_counter.full;
uint32_t current = regs.main_counter.low;
uint64_t wraps = m_32bit_wraps;
if (current < (uint32_t)m_last_ticks)
wraps++;
return (wraps << 32) | current;
}
void HPET::handle_irq()
{
auto& regs = registers();
uint64_t current_ticks { 0 };
if (m_is_64bit)
current_ticks = regs.main_counter.full;
else
{
uint32_t current = regs.main_counter.low;
if (current < (uint32_t)m_last_ticks)
m_32bit_wraps++;
current_ticks = ((uint64_t)m_32bit_wraps << 32) | current;
}
m_last_ticks = current_ticks;
Scheduler::get().timer_reschedule();
}
uint64_t HPET::ms_since_boot() const
{
// FIXME: 32 bit CPUs should use 32 bit counter with 32 bit reads
return read_register(HPET_REG_COUNTER) * m_counter_tick_period_fs / FS_PER_MS;
return ns_since_boot() / 1'000'000;
}
uint64_t HPET::ns_since_boot() const
{
// FIXME: 32 bit CPUs should use 32 bit counter with 32 bit reads
return read_register(HPET_REG_COUNTER) * m_counter_tick_period_fs / FS_PER_NS;
auto current = time_since_boot();
return current.tv_sec * 1'000'000'000 + current.tv_nsec;
}
timespec HPET::time_since_boot() const
{
uint64_t time_fs = read_register(HPET_REG_COUNTER) * m_counter_tick_period_fs;
auto& regs = registers();
uint64_t counter = read_main_counter();
uint64_t seconds = counter / m_ticks_per_s;
uint64_t ticks_this_second = counter % m_ticks_per_s;
long ns_this_second = ticks_this_second * regs.counter_clk_period / FS_PER_NS;
return timespec {
.tv_sec = time_fs / FS_PER_S,
.tv_nsec = (long)((time_fs % FS_PER_S) / FS_PER_NS)
.tv_sec = seconds,
.tv_nsec = ns_this_second
};
}
void HPET::write_register(ptrdiff_t reg, uint64_t value) const
{
MMIO::write64(m_mmio_base + reg, value);
}
uint64_t HPET::read_register(ptrdiff_t reg) const
{
return MMIO::read64(m_mmio_base + reg);
}
}

80
kernel/klibc/ctype.cpp Normal file
View File

@ -0,0 +1,80 @@
#include <ctype.h>
int isalnum(int c)
{
return isdigit(c) || isalpha(c);
}
int isalpha(int c)
{
return islower(c) || isupper(c);
}
int isascii(int c)
{
return c <= 0x7F;
}
int isblank(int c)
{
return c == ' ' || c == '\t';
}
int iscntrl(int c)
{
return c < 32 || c == 0x7F;
}
int isdigit(int c)
{
return '0' <= c && c <= '9';
}
int isgraph(int c)
{
return 0x21 <= c && c <= 0x7E;
}
int islower(int c)
{
return 'a' <= c && c <= 'z';
}
int isprint(int c)
{
return isgraph(c) || c == ' ';
}
int ispunct(int c)
{
return isgraph(c) && !isalnum(c);
}
int isspace(int c)
{
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
}
int isupper(int c)
{
return 'A' <= c && c <= 'Z';
}
int isxdigit(int c)
{
return isdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
}
int toupper(int c)
{
if (!islower(c))
return c;
return 'A' + (c - 'a');
}
int tolower(int c)
{
if (!isupper(c))
return c;
return 'a' + (c - 'A');
}

166
kernel/klibc/string.cpp Normal file
View File

@ -0,0 +1,166 @@
#include <errno.h>
#include <string.h>
int memcmp(const void* s1, const void* s2, size_t n)
{
auto* u1 = static_cast<const unsigned char*>(s1);
auto* u2 = static_cast<const unsigned char*>(s2);
for (size_t i = 0; i < n; i++)
if (u1[i] != u2[i])
return u1[i] - u2[i];
return 0;
}
void* memcpy(void* __restrict s1, const void* __restrict s2, size_t n)
{
auto* dst = static_cast<unsigned char*>(s1);
auto* src = static_cast<const unsigned char*>(s2);
for (size_t i = 0; i < n; i++)
dst[i] = src[i];
return s1;
}
void* memmove(void* s1, const void* s2, size_t n)
{
auto* dst = static_cast<unsigned char*>(s1);
auto* src = static_cast<const unsigned char*>(s2);
if (dst < src)
{
for (size_t i = 0; i < n; i++)
dst[i] = src[i];
}
else
{
for (size_t i = 1; i <= n; i++)
dst[n - i] = src[n - i];
}
return s1;
}
void* memset(void* s, int c, size_t n)
{
auto* u = static_cast<unsigned char*>(s);
for (size_t i = 0; i < n; i++)
u[i] = c;
return s;
}
size_t strlen(const char* str)
{
size_t len = 0;
while (str[len])
len++;
return len;
}
char* strcpy(char* __restrict dst, const char* __restrict src)
{
size_t i = 0;
for (; src[i]; i++)
dst[i] = src[i];
dst[i] = '\0';
return dst;
}
char* strncpy(char* __restrict dst, const char* __restrict src, size_t len)
{
size_t i = 0;
for (; i < len && src[i]; i++)
dst[i] = src[i];
if (i < len)
dst[i] = '\0';
return dst;
}
const char* strerrordesc_np(int error)
{
switch (error)
{
case 0: return "Success";
case E2BIG: return "Argument list too long.";
case EACCES: return "Permission denied.";
case EADDRINUSE: return "Address in use.";
case EADDRNOTAVAIL: return "Address not available.";
case EAFNOSUPPORT: return "Address family not supported.";
case EAGAIN: return "Resource unavailable, try again.";
case EALREADY: return "Connection already in progress.";
case EBADF: return "Bad file descriptor.";
case EBADMSG: return "Bad message.";
case EBUSY: return "Device or resource busy.";
case ECANCELED: return "Operation canceled.";
case ECHILD: return "No child processes.";
case ECONNABORTED: return "Connection aborted.";
case ECONNREFUSED: return "Connection refused.";
case ECONNRESET: return "Connection reset.";
case EDEADLK: return "Resource deadlock would occur.";
case EDESTADDRREQ: return "Destination address required.";
case EDOM: return "Mathematics argument out of domain of function.";
case EDQUOT: return "Reserved.";
case EEXIST: return "File exists.";
case EFAULT: return "Bad address.";
case EFBIG: return "File too large.";
case EHOSTUNREACH: return "Host is unreachable.";
case EIDRM: return "Identifier removed.";
case EILSEQ: return "Illegal byte sequence.";
case EINPROGRESS: return "Operation in progress.";
case EINTR: return "Interrupted function.";
case EINVAL: return "Invalid argument.";
case EIO: return "I/O error.";
case EISCONN: return "Socket is connected.";
case EISDIR: return "Is a directory.";
case ELOOP: return "Too many levels of symbolic links.";
case EMFILE: return "File descriptor value too large.";
case EMLINK: return "Too many links.";
case EMSGSIZE: return "Message too large.";
case EMULTIHOP: return "Reserved.";
case ENAMETOOLONG: return "Filename too long.";
case ENETDOWN: return "Network is down.";
case ENETRESET: return "Connection aborted by network.";
case ENETUNREACH: return "Network unreachable.";
case ENFILE: return "Too many files open in system.";
case ENOBUFS: return "No buffer space available.";
case ENODATA: return "No message is available on the STREAM head read queue.";
case ENODEV: return "No such device.";
case ENOENT: return "No such file or directory.";
case ENOEXEC: return "Executable file format error.";
case ENOLCK: return "No locks available.";
case ENOLINK: return "Reserved.";
case ENOMEM: return "Not enough space.";
case ENOMSG: return "No message of the desired type.";
case ENOPROTOOPT: return "Protocol not available.";
case ENOSPC: return "No space left on device.";
case ENOSR: return "No STREAM resources.";
case ENOSTR: return "Not a STREAM.";
case ENOSYS: return "Functionality not supported.";
case ENOTCONN: return "The socket is not connected.";
case ENOTDIR: return "Not a directory or a symbolic link to a directory.";
case ENOTEMPTY: return "Directory not empty.";
case ENOTRECOVERABLE: return "State not recoverable.";
case ENOTSOCK: return "Not a socket.";
case ENOTSUP: return "Not supported.";
case ENOTTY: return "Inappropriate I/O control operation.";
case ENXIO: return "No such device or address.";
case EOPNOTSUPP: return "Operation not supported on socket .";
case EOVERFLOW: return "Value too large to be stored in data type.";
case EOWNERDEAD: return "Previous owner died.";
case EPERM: return "Operation not permitted.";
case EPIPE: return "Broken pipe.";
case EPROTO: return "Protocol error.";
case EPROTONOSUPPORT: return "Protocol not supported.";
case EPROTOTYPE: return "Protocol wrong type for socket.";
case ERANGE: return "Result too large.";
case EROFS: return "Read-only file system.";
case ESPIPE: return "Invalid seek.";
case ESRCH: return "No such process.";
case ESTALE: return "Reserved.";
case ETIME: return "Stream ioctl() timeout.";
case ETIMEDOUT: return "Connection timed out.";
case ETXTBSY: return "Text file busy.";
case EWOULDBLOCK: return "Operation would block.";
case EXDEV: return "Cross-device link.";
case EEXISTS: return "File exists";
case ENOTBLK: return "Block device required";
case EUNKNOWN: return "Unknown error";
}
return nullptr;
}

View File

@ -12,7 +12,7 @@ __BEGIN_DECLS
struct timeval
{
time_t tv_sec; /* Seconds. */
suseconds_t tc_usec; /* Microseconds. */
suseconds_t tv_usec; /* Microseconds. */
};
__END_DECLS

View File

@ -25,7 +25,7 @@ int strcmp(const char* s1, const char* s2);
int strcoll(const char* s1, const char* s2);
int strcoll_l(const char* s1, const char* s2, locale_t locale);
char* strcpy(char* __restrict s1, const char* __restrict s2);
size_t strcspn(const char* , const char* );
size_t strcspn(const char* s1, const char* s2);
char* strdup(const char* s);
char* strerror(int errnum);
char* strerror_l(int errnum, locale_t locale);

View File

@ -66,6 +66,7 @@ struct stat
#define S_IFLNK ((mode_t)0120000)
#define S_IFSOCK ((mode_t)0140000)
#define S_IFMASK ((mode_t)0170000)
#define S_IFMT S_IFMASK
#define S_ISBLK(mode) ((mode & S_IFMASK) == S_IFBLK)
#define S_ISCHR(mode) ((mode & S_IFMASK) == S_IFCHR)

View File

@ -160,7 +160,7 @@ long gethostid(void);
int gethostname(char* name, size_t namelen);
char* getlogin(void);
int getlogin_r(char* name, size_t namesize);
int getopt(int argc, char* const argv[], const char* optstring);
int getopt(int argc, char* const argv[], const char* optstring);
pid_t getpgid(pid_t pid);
pid_t getpgrp(void);
pid_t getpid(void);

View File

@ -4,6 +4,7 @@
#include <scanf_impl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
@ -11,18 +12,37 @@
struct FILE
{
int fd { -1 };
mode_t mode { 0 };
bool eof { false };
bool error { false };
int pid { -1 };
unsigned char buffer[BUFSIZ] {};
uint32_t buffer_index { 0 };
};
struct ScopeLock
{
ScopeLock(FILE* file)
: m_file(file)
{
flockfile(m_file);
}
~ScopeLock()
{
funlockfile(m_file);
}
FILE* m_file;
};
static FILE s_files[FOPEN_MAX] {
{ .fd = STDIN_FILENO },
{ .fd = STDOUT_FILENO },
{ .fd = STDERR_FILENO },
{ .fd = STDDBG_FILENO },
{ .fd = STDIN_FILENO, .mode = O_RDONLY },
{ .fd = STDOUT_FILENO, .mode = O_WRONLY },
{ .fd = STDERR_FILENO, .mode = O_WRONLY },
{ .fd = STDDBG_FILENO, .mode = O_WRONLY },
};
FILE* stdin = &s_files[0];
@ -32,6 +52,7 @@ FILE* stddbg = &s_files[3];
void clearerr(FILE* file)
{
ScopeLock _(file);
file->eof = false;
file->error = false;
}
@ -46,22 +67,51 @@ char* ctermid(char* buffer)
int fclose(FILE* file)
{
if (close(file->fd) == -1)
return EOF;
ScopeLock _(file);
(void)fflush(file);
int ret = (close(file->fd) == -1) ? EOF : 0;
file->fd = -1;
return ret;
}
static mode_t parse_mode_string(const char* mode_str)
{
size_t len = strlen(mode_str);
if (len == 0 || len > 3)
return 0;
if (len == 3 && mode_str[1] == mode_str[2])
return 0;
if (strcspn(mode_str + 1, "b+") != len - 1)
return 0;
bool plus = (mode_str[1] == '+' || mode_str[2] == '+');
switch (mode_str[0])
{
case 'r': return plus ? O_RDWR : O_RDONLY;
case 'w': return plus ? O_RDWR | O_CREAT | O_TRUNC : O_WRONLY | O_CREAT | O_TRUNC;
case 'a': return plus ? O_RDWR | O_CREAT | O_APPEND : O_WRONLY | O_CREAT | O_APPEND;
}
return 0;
}
FILE* fdopen(int fd, const char* mode)
FILE* fdopen(int fd, const char* mode_str)
{
// FIXME
(void)mode;
mode_t mode = parse_mode_string(mode_str);
if (mode == 0)
{
errno = EINVAL;
return nullptr;
}
mode &= ~O_TRUNC;
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
if (s_files[i].fd == -1)
{
s_files[i] = { .fd = fd };
s_files[i] = {
.fd = fd,
.mode = mode & O_ACCMODE,
};
return &s_files[i];
}
}
@ -91,6 +141,8 @@ int fflush(FILE* file)
return 0;
}
ScopeLock _(file);
if (file->buffer_index == 0)
return 0;
@ -106,29 +158,13 @@ int fflush(FILE* file)
int fgetc(FILE* file)
{
if (file->eof)
return EOF;
unsigned char c;
long ret = syscall(SYS_READ, file->fd, &c, 1);
if (ret < 0)
{
file->error = true;
return EOF;
}
if (ret == 0)
{
file->eof = true;
return EOF;
}
return c;
ScopeLock _(file);
return getc_unlocked(file);
}
int fgetpos(FILE* file, fpos_t* pos)
{
ScopeLock _(file);
off_t offset = ftello(file);
if (offset == -1)
return -1;
@ -140,10 +176,11 @@ char* fgets(char* str, int size, FILE* file)
{
if (size == 0)
return nullptr;
ScopeLock _(file);
int i = 0;
for (; i < size - 1; i++)
{
char c = fgetc(file);
int c = getc_unlocked(file);
if (c == EOF)
{
if (i == 0)
@ -168,54 +205,33 @@ int fileno(FILE* fp)
return fp->fd;
}
// TODO
void flockfile(FILE*);
FILE* fopen(const char* pathname, const char* mode)
void flockfile(FILE*)
{
uint8_t flags = 0;
if (mode[0] == 'r')
flags |= O_RDONLY;
else if (mode[0] == 'w')
flags |= O_WRONLY | O_CREAT | O_TRUNC;
else if (mode[0] == 'a')
flags |= O_WRONLY | O_CREAT | O_APPEND;
else
// FIXME: when threads are implemented
}
FILE* fopen(const char* pathname, const char* mode_str)
{
mode_t mode = parse_mode_string(mode_str);
if (mode == 0)
{
errno = EINVAL;
return nullptr;
}
if (mode[1] && mode[2] && mode[1] == mode[2])
{
errno = EINVAL;
return nullptr;
}
for (int i = 1; i <= 2; i++)
{
if (mode[i] == 0)
break;
else if (mode[i] == '+')
flags |= O_RDWR;
else if (mode[i] == 'b')
continue;
else
{
errno = EINVAL;
return nullptr;
}
}
int fd = open(pathname, flags);
int fd = open(pathname, mode, 0666);
if (fd == -1)
return nullptr;
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
if (s_files[i].fd == -1)
{
s_files[i] = { .fd = fd };
s_files[i] = {
.fd = fd,
.mode = mode & O_ACCMODE
};
return &s_files[i];
}
}
@ -235,18 +251,16 @@ int fprintf(FILE* file, const char* format, ...)
int fputc(int c, FILE* file)
{
file->buffer[file->buffer_index++] = c;
if (c == '\n' || file->buffer_index == sizeof(file->buffer))
if (fflush(file) == EOF)
return EOF;
return (unsigned char)c;
ScopeLock _(file);
return putc_unlocked(c, file);
}
int fputs(const char* str, FILE* file)
{
ScopeLock _(file);
while (*str)
{
if (fputc(*str, file) == EOF)
if (putc_unlocked(*str, file) == EOF)
return EOF;
str++;
}
@ -255,6 +269,7 @@ int fputs(const char* str, FILE* file)
size_t fread(void* buffer, size_t size, size_t nitems, FILE* file)
{
ScopeLock _(file);
if (file->eof || nitems * size == 0)
return 0;
@ -279,11 +294,51 @@ size_t fread(void* buffer, size_t size, size_t nitems, FILE* file)
return nread / size;
}
// TODO
FILE* freopen(const char*, const char*, FILE*);
FILE* freopen(const char* pathname, const char* mode_str, FILE* file)
{
mode_t mode = parse_mode_string(mode_str);
if (mode == 0)
{
errno = EINVAL;
return nullptr;
}
// TODO
int fscanf(FILE*, const char*, ...);
ScopeLock _(file);
(void)fflush(file);
if (pathname)
{
close(file->fd);
file->fd = open(pathname, mode, 0666);
file->mode = mode & O_ACCMODE;
if (file->fd == -1)
return nullptr;
}
else
{
mode &= O_ACCMODE;
if ((file->mode & mode) != mode)
{
close(file->fd);
file->fd = -1;
errno = EBADF;
return nullptr;
}
file->mode = mode;
}
return file;
}
int fscanf(FILE* file, const char* format, ...)
{
va_list arguments;
va_start(arguments, format);
int ret = vfscanf(file, format, arguments);
va_end(arguments);
return ret;
}
int fseek(FILE* file, long offset, int whence)
{
@ -292,10 +347,10 @@ int fseek(FILE* file, long offset, int whence)
int fseeko(FILE* file, off_t offset, int whence)
{
ScopeLock _(file);
long ret = syscall(SYS_SEEK, file->fd, offset, whence);
if (ret < 0)
return -1;
file->eof = false;
return 0;
}
@ -312,30 +367,38 @@ long ftell(FILE* file)
off_t ftello(FILE* file)
{
ScopeLock _(file);
long ret = syscall(SYS_TELL, file->fd);
if (ret < 0)
return -1;
return ret;
}
// TODO
int ftrylockfile(FILE*);
int ftrylockfile(FILE*)
{
// FIXME: when threads are implemented
return 0;
}
// TODO
void funlockfile(FILE*);
void funlockfile(FILE*)
{
// FIXME: when threads are implemented
}
size_t fwrite(const void* buffer, size_t size, size_t nitems, FILE* file)
{
ScopeLock _(file);
unsigned char* ubuffer = (unsigned char*)buffer;
for (size_t byte = 0; byte < nitems * size; byte++)
if (fputc(ubuffer[byte], file) == EOF)
if (putc_unlocked(ubuffer[byte], file) == EOF)
return byte / size;
return nitems;
}
int getc(FILE* file)
{
return fgetc(file);
ScopeLock _(file);
return getc_unlocked(file);
}
int getchar(void)
@ -343,11 +406,33 @@ int getchar(void)
return getc(stdin);
}
// TODO
int getc_unlocked(FILE*);
int getc_unlocked(FILE* file)
{
if (file->eof)
return EOF;
// TODO
int getchar_unlocked(void);
unsigned char c;
long ret = syscall(SYS_READ, file->fd, &c, 1);
if (ret < 0)
{
file->error = true;
return EOF;
}
if (ret == 0)
{
file->eof = true;
return EOF;
}
return c;
}
int getchar_unlocked(void)
{
return getc_unlocked(stdin);
}
char* gets(char* buffer)
{
@ -373,11 +458,32 @@ char* gets(char* buffer)
}
}
// TODO
int pclose(FILE*);
int pclose(FILE* file)
{
if (file->pid == -1)
{
errno = EBADF;
return -1;
}
pid_t pid = file->pid;
(void)fclose(file);
int stat;
while (waitpid(pid, &stat, 0) != -1)
{
if (errno != EINTR)
{
stat = -1;
break;
}
}
return stat;
}
void perror(const char* string)
{
ScopeLock _(stderr);
if (string && *string)
{
fputs(string, stderr);
@ -388,8 +494,60 @@ void perror(const char* string)
stderr->error = true;
}
// TODO
FILE* popen(const char*, const char*);
FILE* popen(const char* command, const char* mode_str)
{
if ((mode_str[0] != 'r' && mode_str[0] != 'w') || mode_str[1] != '\0')
{
errno = EINVAL;
return nullptr;
}
bool read = (mode_str[0] == 'r');
int fds[2];
if (pipe(fds) == -1)
return nullptr;
pid_t pid = fork();
if (pid == 0)
{
if (read)
dup2(fds[1], STDOUT_FILENO);
else
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
close(fds[1]);
execl("/bin/Shell", "sh", "-c", command, nullptr);
exit(1);
}
if (pid == -1)
{
close(fds[0]);
close(fds[1]);
return nullptr;
}
close(read ? fds[1] : fds[0]);
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
if (s_files[i].fd == -1)
{
s_files[i] = {
.fd = read ? fds[0] : fds[1],
.mode = (unsigned)(read ? O_RDONLY : O_WRONLY),
.pid = pid
};
return &s_files[i];
}
}
errno = EMFILE;
return nullptr;
}
int printf(const char* format, ...)
{
@ -410,14 +568,23 @@ int putchar(int c)
return putc(c, stdout);
}
// TODO
int putc_unlocked(int, FILE*);
int putc_unlocked(int c, FILE* file)
{
file->buffer[file->buffer_index++] = c;
if (c == '\n' || file->buffer_index == sizeof(file->buffer))
if (fflush(file) == EOF)
return EOF;
return (unsigned char)c;
}
// TODO
int putchar_unlocked(int);
int putchar_unlocked(int c)
{
return putc_unlocked(c, stdout);
}
int puts(const char* string)
{
ScopeLock _(stdout);
if (fputs(string, stdout) == EOF)
return EOF;
if (fputc('\n', stdout) == EOF)
@ -438,8 +605,12 @@ int remove(const char* path)
// TODO
int rename(const char*, const char*);
// TODO
void rewind(FILE*);
void rewind(FILE* file)
{
ScopeLock _(file);
file->error = false;
(void)fseek(file, 0L, SEEK_SET);
}
int scanf(const char* format, ...)
{
@ -497,12 +668,14 @@ int ungetc(int, FILE*);
int vfprintf(FILE* file, const char* format, va_list arguments)
{
return printf_impl(format, arguments, [](int c, void* file) { return fputc(c, static_cast<FILE*>(file)); }, file);
ScopeLock _(file);
return printf_impl(format, arguments, [](int c, void* file) { return putc_unlocked(c, static_cast<FILE*>(file)); }, file);
}
int vfscanf(FILE* file, const char* format, va_list arguments)
{
return scanf_impl(format, arguments, [](void* file) { return fgetc(static_cast<FILE*>(file)); }, file);
ScopeLock _(file);
return scanf_impl(format, arguments, [](void* file) { return getc_unlocked(static_cast<FILE*>(file)); }, file);
}
int vprintf(const char* format, va_list arguments)

View File

@ -1,10 +1,30 @@
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int errno = 0;
void* memccpy(void* __restrict s1, const void* __restrict s2, int c, size_t n)
{
unsigned char* dst = static_cast<unsigned char*>(s1);
const unsigned char* src = static_cast<const unsigned char*>(s2);
for (size_t i = 0; i < n; i++)
if ((dst[i] = src[i]) == c)
return dst + i + 1;
return nullptr;
}
void* memchr(const void* s, int c, size_t n)
{
const unsigned char* u = static_cast<const unsigned char*>(s);
for (size_t i = 0; i < n; i++)
if (u[i] == c)
return const_cast<unsigned char*>(u + i);
return nullptr;
}
int memcmp(const void* s1, const void* s2, size_t n)
{
const unsigned char* a = static_cast<const unsigned char*>(s1);
@ -116,7 +136,6 @@ char* strncat(char* __restrict__ dest, const char* __restrict__ src, size_t n)
return dest;
}
#ifndef __is_kernel
char* strdup(const char* str)
{
const size_t size = strlen(str) + 1;
@ -142,9 +161,7 @@ char* strndup(const char* str, size_t size)
memcpy(new_str, str, size);
return new_str;
}
#endif
__attribute__((optimize("-O0")))
size_t strlen(const char* str)
{
size_t len = 0;
@ -153,6 +170,14 @@ size_t strlen(const char* str)
return len;
}
size_t strnlen(const char* str, size_t maxlen)
{
size_t len = 0;
while (len < maxlen && str[len])
len++;
return len;
}
char* strchr(const char* str, int c)
{
while (*str)
@ -189,20 +214,156 @@ char* strrchr(const char* str, int c)
char* strstr(const char* haystack, const char* needle)
{
size_t needle_len = strlen(needle);
for (size_t i = 0; haystack[i]; i++)
if (memcmp(haystack + i, needle, strlen(needle)) == 0)
return (char*)haystack + i;
return NULL;
if (strncmp(haystack + i, needle, needle_len) == 0)
return const_cast<char*>(haystack + i);
return nullptr;
}
size_t strcspn(const char* s1, const char* s2)
{
size_t i = 0;
for (; s1[i]; i++)
for (size_t j = 0; s2[j]; j++)
if (s1[i] == s2[j])
return i;
return i;
}
char* strpbrk(const char* s1, const char* s2)
{
for (size_t i = 0; s1[i]; i++)
for (size_t j = 0; s2[j]; j++)
if (s1[i] == s2[j])
return const_cast<char*>(s1 + i);
return nullptr;
}
size_t strspn(const char* s1, const char* s2)
{
size_t i = 0;
for (; s1[i]; i++)
{
bool found = false;
for (size_t j = 0; s2[j] && !found; j++)
if (s1[i] == s2[j])
found = true;
if (!found)
break;
}
return i;
}
char* strtok(char* __restrict s, const char* __restrict sep)
{
static char* state = nullptr;
return strtok_r(s, sep, &state);
}
char* strtok_r(char* __restrict str, const char* __restrict sep, char** __restrict state)
{
if (str)
{
while (*str)
{
bool found = false;
for (size_t i = 0; sep[i] && !found; i++)
if (*str == sep[i])
found = true;
if (!found)
break;
str++;
}
if (!*str)
{
*state = nullptr;
return nullptr;
}
*state = str;
}
if (!*state)
return nullptr;
str = *state;
for (size_t i = 0; str[i]; i++)
{
for (size_t j = 0; sep[j]; j++)
{
if (str[i] == sep[j])
{
str[i] = '\0';
*state = str + i + 1;
return str;
}
}
}
*state = nullptr;
return str;
}
char* strsignal(int signum)
{
static char buffer[128];
switch (signum)
{
case SIGABRT: strcpy(buffer, "SIGABRT"); break;
case SIGALRM: strcpy(buffer, "SIGALRM"); break;
case SIGBUS: strcpy(buffer, "SIGBUS"); break;
case SIGCHLD: strcpy(buffer, "SIGCHLD"); break;
case SIGCONT: strcpy(buffer, "SIGCONT"); break;
case SIGFPE: strcpy(buffer, "SIGFPE"); break;
case SIGHUP: strcpy(buffer, "SIGHUP"); break;
case SIGILL: strcpy(buffer, "SIGILL"); break;
case SIGINT: strcpy(buffer, "SIGINT"); break;
case SIGKILL: strcpy(buffer, "SIGKILL"); break;
case SIGPIPE: strcpy(buffer, "SIGPIPE"); break;
case SIGQUIT: strcpy(buffer, "SIGQUIT"); break;
case SIGSEGV: strcpy(buffer, "SIGSEGV"); break;
case SIGSTOP: strcpy(buffer, "SIGSTOP"); break;
case SIGTERM: strcpy(buffer, "SIGTERM"); break;
case SIGTSTP: strcpy(buffer, "SIGTSTP"); break;
case SIGTTIN: strcpy(buffer, "SIGTTIN"); break;
case SIGTTOU: strcpy(buffer, "SIGTTOU"); break;
case SIGUSR1: strcpy(buffer, "SIGUSR1"); break;
case SIGUSR2: strcpy(buffer, "SIGUSR2"); break;
case SIGPOLL: strcpy(buffer, "SIGPOLL"); break;
case SIGPROF: strcpy(buffer, "SIGPROF"); break;
case SIGSYS: strcpy(buffer, "SIGSYS"); break;
case SIGTRAP: strcpy(buffer, "SIGTRAP"); break;
case SIGURG: strcpy(buffer, "SIGURG"); break;
case SIGVTALRM: strcpy(buffer, "SIGVTALRM"); break;
case SIGXCPU: strcpy(buffer, "SIGXCPU"); break;
case SIGXFSZ: strcpy(buffer, "SIGXFSZ"); break;
}
return buffer;
}
char* strerror(int error)
{
static char buffer[1024];
buffer[0] = 0;
strcpy(buffer, strerrordesc_np(error));
static char buffer[128];
if (const char* str = strerrordesc_np(error))
strcpy(buffer, str);
else
sprintf(buffer, "Unknown error %d", error);
return buffer;
}
int strerror_r(int error, char* strerrbuf, size_t buflen)
{
const char* str = strerrordesc_np(error);
if (!str)
return EINVAL;
if (strlen(str) + 1 > buflen)
return ERANGE;
strcpy(strerrbuf, str);
return 0;
}
const char* strerrorname_np(int error)
{
switch (error)
@ -295,7 +456,7 @@ const char* strerrorname_np(int error)
}
errno = EINVAL;
return "EUNKNOWN";
return nullptr;
}
const char* strerrordesc_np(int error)
@ -390,5 +551,5 @@ const char* strerrordesc_np(int error)
}
errno = EINVAL;
return "Unknown error";
return nullptr;
}

View File

@ -22,7 +22,7 @@ int select(int nfds, fd_set* __restrict readfds, fd_set* __restrict writefds, fd
if (timeout)
{
ts.tv_sec = timeout->tv_sec;
ts.tv_nsec = timeout->tc_usec * 1000;
ts.tv_nsec = timeout->tv_usec * 1000;
pts = &ts;
}

View File

@ -295,6 +295,91 @@ int rmdir(const char* path)
return syscall(SYS_UNLINK, path);
}
char* optarg = nullptr;
int opterr = 1;
int optind = 1;
int optopt = 0;
int getopt(int argc, char* const argv[], const char* optstring)
{
if (optind >= argc)
return -1;
static int idx = 1;
const char* current = argv[optind];
// if "--" is encountered, no more options are parsed
if (idx == -1)
return -1;
// if current is nullptr, does not start with '-' or is string "-", return -1
if (current == nullptr || current[0] != '-' || current[1] == '\0')
return -1;
// if current points to string "--" increment optind and return -1
if (current[1] == '-' && current[2] == '\0')
{
idx = -1;
optind++;
return -1;
}
for (size_t i = 0; optstring[i]; i++)
{
if (optstring[i] == ':')
continue;
if (current[idx] != optstring[i])
continue;
if (optstring[i + 1] == ':')
{
if (current[idx + 1])
{
optarg = const_cast<char*>(current + idx + 1);
optind += 1;
}
else
{
optarg = const_cast<char*>(argv[optind + 1]);
optind += 2;
}
idx = 1;
if (optind > argc)
{
if (opterr && optstring[0] != ':')
fprintf(stderr, "%s: option requires an argument -- %c\n", argv[0], optstring[i]);
optopt = optstring[i];
return optstring[0] == ':' ? ':' : '?';
}
return optstring[i];
}
else
{
if (current[++idx] == '\0')
{
idx = 1;
optind++;
}
return optstring[i];
}
}
if (opterr && optstring[0] != ':')
fprintf(stderr, "%s: illegal option -- %c\n", argv[0], current[idx]);
if (current[++idx] == '\0')
{
idx = 1;
optind++;
}
return '?';
}
pid_t getpid(void)
{
return syscall(SYS_GET_PID);

View File

@ -10,6 +10,7 @@ set(USERSPACE_PROJECTS
dd
dhcp-client
echo
getopt
id
image
init
@ -33,6 +34,7 @@ set(USERSPACE_PROJECTS
test-framebuffer
test-globals
test-mouse
test-popen
test-sort
test-tcp
test-udp

View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.26)
project(getopt CXX)
set(SOURCES
main.cpp
)
add_executable(getopt ${SOURCES})
target_compile_options(getopt PUBLIC -O2 -g)
target_link_libraries(getopt PUBLIC libc ban)
add_custom_target(getopt-install
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/getopt ${BANAN_BIN}/
DEPENDS getopt
)

44
userspace/getopt/main.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <BAN/String.h>
#include <BAN/Vector.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc < 2)
{
fprintf(stderr, "usage: %s OPTSTRING [PARAMETERS]...", argv[0]);
return 1;
}
BAN::Vector<char*> argv_copy(argc - 1);
argv_copy[0] = argv[0];
for (int i = 2; i < argc; i++)
argv_copy[i - 1] = argv[i];
int opt;
BAN::String parsed;
while ((opt = getopt(argc - 1, argv_copy.data(), argv[1])) != -1)
{
if (opt == ':' || opt == '?')
continue;
MUST(parsed.append(" -"));
MUST(parsed.push_back(opt));
if (optarg)
{
MUST(parsed.push_back(' '));
MUST(parsed.append(optarg));
}
optarg = nullptr;
}
printf("%s --", parsed.data());
for (; optind < argc - 1; optind++)
printf(" %s", argv_copy[optind]);
printf("\n");
return 0;
}

View File

@ -11,6 +11,7 @@
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
@ -53,6 +54,12 @@ struct DNSEntry
BAN::IPv4Address address { 0 };
};
struct DNSResponse
{
uint16_t id;
DNSEntry entry;
};
bool send_dns_query(int socket, BAN::StringView domain, uint16_t id)
{
static uint8_t buffer[4096];
@ -91,7 +98,7 @@ bool send_dns_query(int socket, BAN::StringView domain, uint16_t id)
return true;
}
BAN::Optional<DNSEntry> read_dns_response(int socket, uint16_t id)
BAN::Optional<DNSResponse> read_dns_response(int socket)
{
static uint8_t buffer[4096];
@ -103,11 +110,6 @@ BAN::Optional<DNSEntry> read_dns_response(int socket, uint16_t id)
}
DNSPacket& reply = *reinterpret_cast<DNSPacket*>(buffer);
if (reply.identification != id)
{
dprintln("Reply to invalid packet");
return {};
}
if (reply.flags & 0x0F)
{
dprintln("DNS error (rcode {})", (unsigned)(reply.flags & 0xF));
@ -134,9 +136,10 @@ BAN::Optional<DNSEntry> read_dns_response(int socket, uint16_t id)
return {};
}
DNSEntry result;
result.valid_until = time(nullptr) + answer.ttl();
result.address = BAN::IPv4Address(*reinterpret_cast<uint32_t*>(answer.data));
DNSResponse result;
result.id = reply.identification;
result.entry.valid_until = time(nullptr) + answer.ttl();
result.entry.address = BAN::IPv4Address(*reinterpret_cast<uint32_t*>(answer.data));
return result;
}
@ -207,55 +210,125 @@ int main(int, char**)
BAN::HashMap<BAN::String, DNSEntry> dns_cache;
struct Client
{
const int socket;
bool close { false };
uint16_t query_id { 0 };
BAN::String query;
};
BAN::LinkedList<Client> clients;
for (;;)
{
int client = accept(service_socket, nullptr, nullptr);
if (client == -1)
int max_sock = BAN::Math::max(service_socket, dns_socket);
fd_set fds;
FD_ZERO(&fds);
FD_SET(service_socket, &fds);
FD_SET(dns_socket, &fds);
for (auto& client : clients)
{
dprintln("accept: {}", strerror(errno));
FD_SET(client.socket, &fds);
max_sock = BAN::Math::max(max_sock, client.socket);
}
int nselect = select(max_sock + 1, &fds, nullptr, nullptr, nullptr);
if (nselect == -1)
{
perror("select");
continue;
}
auto query = read_service_query(client);
if (!query.has_value())
if (FD_ISSET(service_socket, &fds))
{
close(client);
continue;
}
BAN::Optional<DNSEntry> result;
if (dns_cache.contains(*query))
{
auto& cached = dns_cache[*query];
if (time(nullptr) <= cached.valid_until)
result = cached;
else
dns_cache.remove(*query);
}
if (!result.has_value())
{
uint16_t id = rand() % 0xFFFF;
if (send_dns_query(dns_socket, *query, id))
int client = accept(service_socket, nullptr, nullptr);
if (client == -1)
{
result = read_dns_response(dns_socket, id);
if (result.has_value())
(void)dns_cache.insert(*query, *result);
perror("accept");
continue;
}
MUST(clients.emplace_back(client));
}
if (FD_ISSET(dns_socket, &fds))
{
auto result = read_dns_response(dns_socket);
if (!result.has_value())
continue;
for (auto& client : clients)
{
if (client.query_id != result->id)
continue;
(void)dns_cache.insert(client.query, result->entry);
sockaddr_storage storage;
storage.ss_family = AF_INET;
memcpy(storage.ss_storage, &result->entry.address.raw, sizeof(result->entry.address.raw));
if (send(client.socket, &storage, sizeof(storage), 0) == -1)
dprintln("send: {}", strerror(errno));
client.close = true;
break;
}
}
if (!result.has_value())
result = DNSEntry { .valid_until = 0, .address = BAN::IPv4Address(INADDR_ANY) };
for (auto& client : clients)
{
if (!FD_ISSET(client.socket, &fds))
continue;
sockaddr_storage storage;
storage.ss_family = AF_INET;
memcpy(storage.ss_storage, &result->address.raw, sizeof(result->address.raw));
if (!client.query.empty())
{
dprintln("Client already has a query");
continue;
}
if (send(client, &storage, sizeof(storage), 0) == -1)
dprintln("send: {}", strerror(errno));
auto query = read_service_query(client.socket);
if (!query.has_value())
continue;
close(client);
BAN::Optional<DNSEntry> result;
if (dns_cache.contains(*query))
{
auto& cached = dns_cache[*query];
if (time(nullptr) <= cached.valid_until)
result = cached;
else
dns_cache.remove(*query);
}
if (result.has_value())
{
sockaddr_storage storage;
storage.ss_family = AF_INET;
memcpy(storage.ss_storage, &result->address.raw, sizeof(result->address.raw));
if (send(client.socket, &storage, sizeof(storage), 0) == -1)
dprintln("send: {}", strerror(errno));
client.close = true;
continue;
}
client.query = query.release_value();
client.query_id = rand() % 0xFFFF;
send_dns_query(dns_socket, client.query, client.query_id);
}
for (auto it = clients.begin(); it != clients.end();)
{
if (!it->close)
{
it++;
continue;
}
close(it->socket);
it = clients.remove(it);
}
}
return 0;

View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.26)
project(test-popen CXX)
set(SOURCES
main.cpp
)
add_executable(test-popen ${SOURCES})
target_compile_options(test-popen PUBLIC -O2 -g)
target_link_libraries(test-popen PUBLIC libc)
add_custom_target(test-popen-install
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/test-popen ${BANAN_BIN}/
DEPENDS test-popen
)

View File

@ -0,0 +1,30 @@
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc != 2)
{
fprintf(stderr, "usage: %s COMMAND\n", argv[0]);
return 1;
}
FILE* fp = popen(argv[1], "r");
if (fp == nullptr)
{
perror("popen");
return 1;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp) != NULL)
printf("%s", buffer);
if (pclose(fp) == -1)
{
perror("pclose");
return 1;
}
return 0;
}