]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
posix: add fcntl(F_SETFL) O_NONBLOCK for pipes and tty/pty
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 01:12:20 +0000 (22:12 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 01:12:20 +0000 (22:12 -0300)
include/errno.h
include/syscall.h
src/kernel/syscall.c
user/init.c

index e19670c1371e8d2eebd0ccac8626e6a841a5df73..603b20dabcbcb49916bac1c1b2b65fc47f0d78f0 100644 (file)
@@ -8,6 +8,7 @@
 #define E2BIG 7
 #define EBADF 9
 #define ECHILD 10
+#define EAGAIN 11
 #define EFAULT 14
 #define EEXIST 17
 #define ENOMEM 12
index a31dd0bd358e2b9a9794cf81917a2a36cab7b58d..8026b6d9f42e1394b8dbd18497a9ee176dab3967 100644 (file)
@@ -42,6 +42,8 @@ enum {
     SYSCALL_UNLINK = 29,
 
     SYSCALL_GETDENTS = 30,
+
+    SYSCALL_FCNTL = 31,
 };
 
 #endif
index 61b14c7a76d644841f7686a5c9b0f286abf981da..fcdd2489ee4ca7501ca277fbff5ca36ebd1bb54e 100644 (file)
 
 #include <stddef.h>
 
+enum {
+    O_NONBLOCK = 0x800,
+};
+
+enum {
+    FCNTL_F_GETFL = 3,
+    FCNTL_F_SETFL = 4,
+};
+
 #if defined(__i386__)
 static const uint32_t SIGFRAME_MAGIC = 0x53494746U; // 'SIGF'
 struct sigframe {
@@ -784,7 +793,7 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
     if (!f) return -ENOMEM;
     f->node = node;
     f->offset = 0;
-    f->flags = 0;
+    f->flags = flags;
     f->refcount = 1;
 
     int fd = fd_alloc(f);
@@ -795,6 +804,23 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
     return fd;
 }
 
+static int syscall_fcntl_impl(int fd, int cmd, uint32_t arg) {
+    struct file* f = fd_get(fd);
+    if (!f) return -EBADF;
+
+    if (cmd == FCNTL_F_GETFL) {
+        return (int)f->flags;
+    }
+    if (cmd == FCNTL_F_SETFL) {
+        // Minimal: allow toggling O_NONBLOCK only.
+        uint32_t keep = f->flags & ~O_NONBLOCK;
+        uint32_t set = arg & O_NONBLOCK;
+        f->flags = keep | set;
+        return 0;
+    }
+    return -EINVAL;
+}
+
 static int syscall_mkdir_impl(const char* user_path) {
     if (!user_path) return -EFAULT;
 
@@ -886,6 +912,29 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) {
     struct file* f = fd_get(fd);
     if (!f || !f->node) return -EBADF;
 
+    int nonblock = (f->flags & O_NONBLOCK) ? 1 : 0;
+    if (nonblock) {
+        // Non-blocking pipes: if empty but writers exist, return -EAGAIN.
+        if (f->node->name[0] == 'p' && f->node->name[1] == 'i' && f->node->name[2] == 'p' && f->node->name[3] == 'e' && f->node->name[4] == ':') {
+            struct pipe_node* pn = (struct pipe_node*)f->node;
+            struct pipe_state* ps = pn ? pn->ps : 0;
+            if (pn && ps && pn->is_read_end && ps->count == 0 && ps->writers != 0) {
+                return -EAGAIN;
+            }
+        }
+
+        // Non-blocking char devices (tty/pty) need special handling, since devfs read blocks.
+        if (f->node->flags == FS_CHARDEVICE) {
+            if (f->node->inode == 3) {
+                if (!tty_can_read()) return -EAGAIN;
+            } else if (f->node->inode == 4) {
+                if (!pty_master_can_read()) return -EAGAIN;
+            } else if (f->node->inode == 6) {
+                if (!pty_slave_can_read()) return -EAGAIN;
+            }
+        }
+    }
+
     if (f->node->flags == FS_CHARDEVICE) {
         uint8_t kbuf[256];
         uint32_t total = 0;
@@ -930,6 +979,8 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) {
     return (int)total;
 }
 
+static int syscall_write_impl(int fd, const void* user_buf, uint32_t len);
+
 static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) {
     if (len > 1024 * 1024) return -EINVAL;
     if (user_range_ok(user_buf, (size_t)len) == 0) return -EFAULT;
@@ -942,6 +993,20 @@ static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) {
 
     struct file* f = fd_get(fd);
     if (!f || !f->node) return -EBADF;
+
+    int nonblock = (f->flags & O_NONBLOCK) ? 1 : 0;
+    if (nonblock) {
+        // Non-blocking pipe write: if full but readers exist, return -EAGAIN.
+        if (f->node->name[0] == 'p' && f->node->name[1] == 'i' && f->node->name[2] == 'p' && f->node->name[3] == 'e' && f->node->name[4] == ':') {
+            struct pipe_node* pn = (struct pipe_node*)f->node;
+            struct pipe_state* ps = pn ? pn->ps : 0;
+            if (pn && ps && !pn->is_read_end) {
+                if (ps->readers != 0 && (ps->cap - ps->count) == 0) {
+                    return -EAGAIN;
+                }
+            }
+        }
+    }
     if (!f->node->write) return -ESPIPE;
     if (((f->node->flags & FS_FILE) == 0) && f->node->flags != FS_CHARDEVICE) return -ESPIPE;
 
@@ -1282,6 +1347,14 @@ static void syscall_handler(struct registers* regs) {
         return;
     }
 
+    if (syscall_no == SYSCALL_FCNTL) {
+        int fd = (int)regs->ebx;
+        int cmd = (int)regs->ecx;
+        uint32_t arg = (uint32_t)regs->edx;
+        regs->eax = (uint32_t)syscall_fcntl_impl(fd, cmd, arg);
+        return;
+    }
+
     if (syscall_no == SYSCALL_MKDIR) {
         const char* path = (const char*)regs->ebx;
         regs->eax = (uint32_t)syscall_mkdir_impl(path);
index 277faab17dd3f61429061fac6c1ac55f007ff344..9082795099e5d95f40931802af5afd236b15697e 100644 (file)
@@ -67,6 +67,13 @@ enum {
     SYSCALL_UNLINK = 29,
 
     SYSCALL_GETDENTS = 30,
+
+    SYSCALL_FCNTL = 31,
+};
+
+enum {
+    F_GETFL = 3,
+    F_SETFL = 4,
 };
 
 enum {
@@ -121,6 +128,11 @@ enum {
 enum {
     O_CREAT = 0x40,
     O_TRUNC = 0x200,
+    O_NONBLOCK = 0x800,
+};
+
+enum {
+    EAGAIN = 11,
 };
 
 #define S_IFMT  0170000
@@ -144,6 +156,17 @@ static int sys_write(int fd, const void* buf, uint32_t len) {
     return __syscall_fix(ret);
 }
 
+static int sys_fcntl(int fd, int cmd, uint32_t arg) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_FCNTL), "b"(fd), "c"(cmd), "d"(arg)
+        : "memory"
+    );
+    return __syscall_fix(ret);
+}
+
 static int sys_ioctl(int fd, uint32_t cmd, void* arg);
 
 static int isatty_fd(int fd) {
@@ -1711,6 +1734,68 @@ void _start(void) {
         sys_write(1, "[init] isatty OK\n", (uint32_t)(sizeof("[init] isatty OK\n") - 1));
     }
 
+    // B6: O_NONBLOCK smoke (pipe + pty)
+    {
+        int fds[2];
+        if (sys_pipe(fds) < 0) {
+            sys_write(1, "[init] pipe for nonblock failed\n",
+                      (uint32_t)(sizeof("[init] pipe for nonblock failed\n") - 1));
+            sys_exit(1);
+        }
+
+        if (sys_fcntl(fds[0], F_SETFL, O_NONBLOCK) < 0) {
+            sys_write(1, "[init] fcntl nonblock pipe failed\n",
+                      (uint32_t)(sizeof("[init] fcntl nonblock pipe failed\n") - 1));
+            sys_exit(1);
+        }
+
+        char b;
+        int r = sys_read(fds[0], &b, 1);
+        if (r != -1 || errno != EAGAIN) {
+            sys_write(1, "[init] nonblock pipe read expected EAGAIN\n",
+                      (uint32_t)(sizeof("[init] nonblock pipe read expected EAGAIN\n") - 1));
+            sys_exit(1);
+        }
+
+        if (sys_write(fds[1], "x", 1) != 1) {
+            sys_write(1, "[init] pipe write failed\n",
+                      (uint32_t)(sizeof("[init] pipe write failed\n") - 1));
+            sys_exit(1);
+        }
+        r = sys_read(fds[0], &b, 1);
+        if (r != 1 || b != 'x') {
+            sys_write(1, "[init] nonblock pipe read after write failed\n",
+                      (uint32_t)(sizeof("[init] nonblock pipe read after write failed\n") - 1));
+            sys_exit(1);
+        }
+
+        (void)sys_close(fds[0]);
+        (void)sys_close(fds[1]);
+
+        int p = sys_open("/dev/ptmx", 0);
+        if (p < 0) {
+            sys_write(1, "[init] open /dev/ptmx failed\n",
+                      (uint32_t)(sizeof("[init] open /dev/ptmx failed\n") - 1));
+            sys_exit(1);
+        }
+        if (sys_fcntl(p, F_SETFL, O_NONBLOCK) < 0) {
+            sys_write(1, "[init] fcntl nonblock ptmx failed\n",
+                      (uint32_t)(sizeof("[init] fcntl nonblock ptmx failed\n") - 1));
+            sys_exit(1);
+        }
+        char pch;
+        r = sys_read(p, &pch, 1);
+        if (r != -1 || errno != EAGAIN) {
+            sys_write(1, "[init] nonblock ptmx read expected EAGAIN\n",
+                      (uint32_t)(sizeof("[init] nonblock ptmx read expected EAGAIN\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(p);
+
+        sys_write(1, "[init] O_NONBLOCK OK\n",
+                  (uint32_t)(sizeof("[init] O_NONBLOCK OK\n") - 1));
+    }
+
     enum { NCHILD = 100 };
     int children[NCHILD];
     for (int i = 0; i < NCHILD; i++) {