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) if (m_error_code & kernel_error_mask)
return Kernel::error_string(kernel_error()); return Kernel::error_string(kernel_error());
#endif #endif
return strerror(m_error_code); if (auto* desc = strerrordesc_np(m_error_code))
return desc;
return "Unknown error"sv;
} }
private: private:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -46,15 +46,16 @@ namespace Kernel
BAN::ErrorOr<void> NVMeNamespace::initialize() 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)); m_dma_region = TRY(DMARegion::create(PAGE_SIZE));
add_disk_cache(); add_disk_cache();
DevFileSystem::get().add_device(this); 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()) if (auto res = initialize_partitions(name_prefix); res.is_error())
dprintln("{}", res.error()); dprintln("{}", res.error());

View File

@ -7,21 +7,7 @@
#include <kernel/Scheduler.h> #include <kernel/Scheduler.h>
#include <kernel/Timer/HPET.h> #include <kernel/Timer/HPET.h>
#define HPET_REG_CAPABILIES 0x00 #define HPET_PERIOD_MAX 0x05F5E100
#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 FS_PER_S 1'000'000'000'000'000 #define FS_PER_S 1'000'000'000'000'000
#define FS_PER_MS 1'000'000'000'000 #define FS_PER_MS 1'000'000'000'000
@ -31,17 +17,116 @@
namespace Kernel 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) BAN::ErrorOr<BAN::UniqPtr<HPET>> HPET::create(bool force_pic)
{ {
HPET* hpet = new HPET(); HPET* hpet_ptr = new HPET();
if (hpet == nullptr) if (hpet_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
if (auto ret = hpet->initialize(force_pic); ret.is_error()) auto hpet = BAN::UniqPtr<HPET>::adopt(hpet_ptr);
{ TRY(hpet->initialize(force_pic));
delete hpet; return hpet;
return ret.release_error();
} }
return BAN::UniqPtr<HPET>::adopt(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) BAN::ErrorOr<void> HPET::initialize(bool force_pic)
@ -63,115 +148,155 @@ namespace Kernel
ASSERT(m_mmio_base); ASSERT(m_mmio_base);
PageTable::kernel().map_page_at(header->base_address.address, m_mmio_base, PageTable::Flags::ReadWrite | PageTable::Flags::Present); 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 m_is_64bit = regs.capabilities & COUNT_SIZE_CAP;
if (m_counter_tick_period_fs == 0 || m_counter_tick_period_fs > FS_PER_NS * 100)
// 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)
{
dwarnln("HPET: Invalid counter period");
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(EINVAL);
uint64_t ticks_per_ms = FS_PER_MS / m_counter_tick_period_fs;
{
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]);
} }
uint64_t timer0_config = read_register(HPET_REG_TIMER_CONFIG(0)); m_ticks_per_s = FS_PER_S / period_fs;
if (!(timer0_config & HPET_Tn_PER_INT_CAP)) 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); return BAN::Error::from_errno(ENOTSUP);
} }
int irq = 0; // enable interrupts
if (!force_pic) 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; timer0.configuration = timer0.configuration | Tn_VAL_SET_CNF;
if (irq_cap == 0) timer0.comparator.high = ticks_per_ms >> 32;
}
else if (ticks_per_ms > 0xFFFFFFFF)
{ {
dwarnln("HPET doesn't have any interrupts available"); dprintln("HPET: cannot create 1 kHz timer");
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(ENOTSUP);
} }
for (irq = 0; irq < 32; irq++)
if (irq_cap & (1 << irq))
break;
}
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; TRY(InterruptController::get().reserve_irq(0));
if (force_pic) set_irq(0);
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);
enable_interrupt(); enable_interrupt();
return {}; 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() 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(); Scheduler::get().timer_reschedule();
} }
uint64_t HPET::ms_since_boot() const uint64_t HPET::ms_since_boot() const
{ {
// FIXME: 32 bit CPUs should use 32 bit counter with 32 bit reads return ns_since_boot() / 1'000'000;
return read_register(HPET_REG_COUNTER) * m_counter_tick_period_fs / FS_PER_MS;
} }
uint64_t HPET::ns_since_boot() const uint64_t HPET::ns_since_boot() const
{ {
// FIXME: 32 bit CPUs should use 32 bit counter with 32 bit reads auto current = time_since_boot();
return read_register(HPET_REG_COUNTER) * m_counter_tick_period_fs / FS_PER_NS; return current.tv_sec * 1'000'000'000 + current.tv_nsec;
} }
timespec HPET::time_since_boot() const 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 { return timespec {
.tv_sec = time_fs / FS_PER_S, .tv_sec = seconds,
.tv_nsec = (long)((time_fs % FS_PER_S) / FS_PER_NS) .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 struct timeval
{ {
time_t tv_sec; /* Seconds. */ time_t tv_sec; /* Seconds. */
suseconds_t tc_usec; /* Microseconds. */ suseconds_t tv_usec; /* Microseconds. */
}; };
__END_DECLS __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(const char* s1, const char* s2);
int strcoll_l(const char* s1, const char* s2, locale_t locale); int strcoll_l(const char* s1, const char* s2, locale_t locale);
char* strcpy(char* __restrict s1, const char* __restrict s2); 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* strdup(const char* s);
char* strerror(int errnum); char* strerror(int errnum);
char* strerror_l(int errnum, locale_t locale); char* strerror_l(int errnum, locale_t locale);

View File

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

View File

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

View File

@ -1,10 +1,30 @@
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
int errno = 0; 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) int memcmp(const void* s1, const void* s2, size_t n)
{ {
const unsigned char* a = static_cast<const unsigned char*>(s1); 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; return dest;
} }
#ifndef __is_kernel
char* strdup(const char* str) char* strdup(const char* str)
{ {
const size_t size = strlen(str) + 1; const size_t size = strlen(str) + 1;
@ -142,9 +161,7 @@ char* strndup(const char* str, size_t size)
memcpy(new_str, str, size); memcpy(new_str, str, size);
return new_str; return new_str;
} }
#endif
__attribute__((optimize("-O0")))
size_t strlen(const char* str) size_t strlen(const char* str)
{ {
size_t len = 0; size_t len = 0;
@ -153,6 +170,14 @@ size_t strlen(const char* str)
return len; 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) char* strchr(const char* str, int c)
{ {
while (*str) while (*str)
@ -189,20 +214,156 @@ char* strrchr(const char* str, int c)
char* strstr(const char* haystack, const char* needle) char* strstr(const char* haystack, const char* needle)
{ {
size_t needle_len = strlen(needle);
for (size_t i = 0; haystack[i]; i++) for (size_t i = 0; haystack[i]; i++)
if (memcmp(haystack + i, needle, strlen(needle)) == 0) if (strncmp(haystack + i, needle, needle_len) == 0)
return (char*)haystack + i; return const_cast<char*>(haystack + i);
return NULL; 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) char* strerror(int error)
{ {
static char buffer[1024]; static char buffer[128];
buffer[0] = 0; if (const char* str = strerrordesc_np(error))
strcpy(buffer, strerrordesc_np(error)); strcpy(buffer, str);
else
sprintf(buffer, "Unknown error %d", error);
return buffer; 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) const char* strerrorname_np(int error)
{ {
switch (error) switch (error)
@ -295,7 +456,7 @@ const char* strerrorname_np(int error)
} }
errno = EINVAL; errno = EINVAL;
return "EUNKNOWN"; return nullptr;
} }
const char* strerrordesc_np(int error) const char* strerrordesc_np(int error)
@ -390,5 +551,5 @@ const char* strerrordesc_np(int error)
} }
errno = EINVAL; 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) if (timeout)
{ {
ts.tv_sec = timeout->tv_sec; ts.tv_sec = timeout->tv_sec;
ts.tv_nsec = timeout->tc_usec * 1000; ts.tv_nsec = timeout->tv_usec * 1000;
pts = &ts; pts = &ts;
} }

View File

@ -295,6 +295,91 @@ int rmdir(const char* path)
return syscall(SYS_UNLINK, 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) pid_t getpid(void)
{ {
return syscall(SYS_GET_PID); return syscall(SYS_GET_PID);

View File

@ -10,6 +10,7 @@ set(USERSPACE_PROJECTS
dd dd
dhcp-client dhcp-client
echo echo
getopt
id id
image image
init init
@ -33,6 +34,7 @@ set(USERSPACE_PROJECTS
test-framebuffer test-framebuffer
test-globals test-globals
test-mouse test-mouse
test-popen
test-sort test-sort
test-tcp test-tcp
test-udp 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 <netinet/in.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/select.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/un.h> #include <sys/un.h>
@ -53,6 +54,12 @@ struct DNSEntry
BAN::IPv4Address address { 0 }; BAN::IPv4Address address { 0 };
}; };
struct DNSResponse
{
uint16_t id;
DNSEntry entry;
};
bool send_dns_query(int socket, BAN::StringView domain, uint16_t id) bool send_dns_query(int socket, BAN::StringView domain, uint16_t id)
{ {
static uint8_t buffer[4096]; static uint8_t buffer[4096];
@ -91,7 +98,7 @@ bool send_dns_query(int socket, BAN::StringView domain, uint16_t id)
return true; 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]; 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); DNSPacket& reply = *reinterpret_cast<DNSPacket*>(buffer);
if (reply.identification != id)
{
dprintln("Reply to invalid packet");
return {};
}
if (reply.flags & 0x0F) if (reply.flags & 0x0F)
{ {
dprintln("DNS error (rcode {})", (unsigned)(reply.flags & 0xF)); dprintln("DNS error (rcode {})", (unsigned)(reply.flags & 0xF));
@ -134,9 +136,10 @@ BAN::Optional<DNSEntry> read_dns_response(int socket, uint16_t id)
return {}; return {};
} }
DNSEntry result; DNSResponse result;
result.valid_until = time(nullptr) + answer.ttl(); result.id = reply.identification;
result.address = BAN::IPv4Address(*reinterpret_cast<uint32_t*>(answer.data)); result.entry.valid_until = time(nullptr) + answer.ttl();
result.entry.address = BAN::IPv4Address(*reinterpret_cast<uint32_t*>(answer.data));
return result; return result;
} }
@ -207,22 +210,87 @@ int main(int, char**)
BAN::HashMap<BAN::String, DNSEntry> dns_cache; 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 (;;) for (;;)
{
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)
{
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;
}
if (FD_ISSET(service_socket, &fds))
{ {
int client = accept(service_socket, nullptr, nullptr); int client = accept(service_socket, nullptr, nullptr);
if (client == -1) if (client == -1)
{ {
dprintln("accept: {}", strerror(errno)); perror("accept");
continue; continue;
} }
auto query = read_service_query(client); MUST(clients.emplace_back(client));
if (!query.has_value()) }
if (FD_ISSET(dns_socket, &fds))
{ {
close(client); 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;
}
}
for (auto& client : clients)
{
if (!FD_ISSET(client.socket, &fds))
continue;
if (!client.query.empty())
{
dprintln("Client already has a query");
continue; continue;
} }
auto query = read_service_query(client.socket);
if (!query.has_value())
continue;
BAN::Optional<DNSEntry> result; BAN::Optional<DNSEntry> result;
if (dns_cache.contains(*query)) if (dns_cache.contains(*query))
@ -234,28 +302,33 @@ int main(int, char**)
dns_cache.remove(*query); dns_cache.remove(*query);
} }
if (!result.has_value())
{
uint16_t id = rand() % 0xFFFF;
if (send_dns_query(dns_socket, *query, id))
{
result = read_dns_response(dns_socket, id);
if (result.has_value()) if (result.has_value())
(void)dns_cache.insert(*query, *result); {
}
}
if (!result.has_value())
result = DNSEntry { .valid_until = 0, .address = BAN::IPv4Address(INADDR_ANY) };
sockaddr_storage storage; sockaddr_storage storage;
storage.ss_family = AF_INET; storage.ss_family = AF_INET;
memcpy(storage.ss_storage, &result->address.raw, sizeof(result->address.raw)); memcpy(storage.ss_storage, &result->address.raw, sizeof(result->address.raw));
if (send(client.socket, &storage, sizeof(storage), 0) == -1)
if (send(client, &storage, sizeof(storage), 0) == -1)
dprintln("send: {}", strerror(errno)); dprintln("send: {}", strerror(errno));
client.close = true;
continue;
}
close(client); 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; 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;
}