Compare commits
16 Commits
9314528b9b
...
1f467580ee
Author | SHA1 | Date |
---|---|---|
Bananymous | 1f467580ee | |
Bananymous | 1ba883719a | |
Bananymous | f73e954b28 | |
Bananymous | de629291b9 | |
Bananymous | 7eb5d220fd | |
Bananymous | 4cd9abdd15 | |
Bananymous | 198dde8365 | |
Bananymous | b165340662 | |
Bananymous | 5ad4340679 | |
Bananymous | b56fa4a29d | |
Bananymous | e946b392c9 | |
Bananymous | 81689b5f02 | |
Bananymous | c18d926174 | |
Bananymous | 00662bad46 | |
Bananymous | 420a7b60ca | |
Bananymous | 2ab3eb4109 |
|
@ -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:
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
369
libc/stdio.cpp
369
libc/stdio.cpp
|
@ -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)
|
||||
|
|
183
libc/string.cpp
183
libc/string.cpp
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue