diff options
-rw-r--r-- | lib/libc/include/unistd.h | 5 | ||||
-rw-r--r-- | lib/libc/src/hyra/socket.c | 13 | ||||
-rw-r--r-- | lib/libc/src/main.c | 2 | ||||
-rw-r--r-- | lib/libc/src/posix/getopt.c | 100 | ||||
-rw-r--r-- | lib/libgfx/include/libgfx/draw.h | 20 | ||||
-rw-r--r-- | lib/libgfx/src/draw.c | 136 | ||||
-rw-r--r-- | lib/liboda/include/liboda/input.h | 11 | ||||
-rw-r--r-- | lib/liboda/include/liboda/oda.h | 23 | ||||
-rw-r--r-- | lib/liboda/include/liboda/types.h | 1 | ||||
-rw-r--r-- | lib/liboda/src/input.c | 27 | ||||
-rw-r--r-- | lib/liboda/src/window.c | 206 | ||||
-rw-r--r-- | share/docs/lib/liboda.md | 38 | ||||
-rw-r--r-- | share/man/man1/cat.1 | 7 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/machdep.c | 36 | ||||
-rw-r--r-- | sys/include/sys/proc.h | 2 | ||||
-rw-r--r-- | sys/include/sys/sched.h | 2 | ||||
-rw-r--r-- | sys/include/sys/socket.h | 17 | ||||
-rw-r--r-- | sys/include/sys/syscall.h | 1 | ||||
-rw-r--r-- | sys/include/sys/sysctl.h | 5 | ||||
-rw-r--r-- | sys/kern/init_main.c | 1 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 33 | ||||
-rw-r--r-- | sys/kern/kern_sched.c | 86 | ||||
-rw-r--r-- | sys/kern/kern_socket.c | 211 | ||||
-rw-r--r-- | sys/kern/kern_syscall.c | 1 | ||||
-rw-r--r-- | usr.bin/cat/cat.c | 54 |
25 files changed, 1009 insertions, 29 deletions
diff --git a/lib/libc/include/unistd.h b/lib/libc/include/unistd.h index b9c9f5e..8b03b81 100644 --- a/lib/libc/include/unistd.h +++ b/lib/libc/include/unistd.h @@ -60,6 +60,11 @@ off_t lseek(int fildes, off_t offset, int whence); pid_t getpid(void); pid_t getppid(void); +extern char *optarg; +extern int optind, opterr, optopt; + +int getopt(int argc, char *argv[], const char *optstring); + __END_DECLS #endif /* !_UNISTD_H */ diff --git a/lib/libc/src/hyra/socket.c b/lib/libc/src/hyra/socket.c index b3039f6..2a62541 100644 --- a/lib/libc/src/hyra/socket.c +++ b/lib/libc/src/hyra/socket.c @@ -71,3 +71,16 @@ connect(int socket, const struct sockaddr *address, socklen_t len) { return syscall(SYS_connect, socket, (uintptr_t)address, len); } + +int +setsockopt(int sockfd, int level, int name, const void *v, socklen_t len) +{ + return syscall( + SYS_setsockopt, + sockfd, + level, + name, + (uintptr_t)v, + len + ); +} diff --git a/lib/libc/src/main.c b/lib/libc/src/main.c index 02a648b..68f9bdb 100644 --- a/lib/libc/src/main.c +++ b/lib/libc/src/main.c @@ -30,6 +30,7 @@ #include <sys/exec.h> #include <stdint.h> #include <stddef.h> +#include <unistd.h> extern int __libc_stdio_init(void); extern int __malloc_mem_init(void); @@ -51,6 +52,7 @@ __libc_entry(uint64_t *ctx) char **argv; char **envp; + optind = 1; argc = *ctx; argv = (char **)(ctx + 1); envp = (char **)(argv + argc + 1); diff --git a/lib/libc/src/posix/getopt.c b/lib/libc/src/posix/getopt.c new file mode 100644 index 0000000..d3cd530 --- /dev/null +++ b/lib/libc/src/posix/getopt.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/errno.h> +#include <unistd.h> +#include <stdbool.h> +#include <string.h> +#include <stdio.h> + +char *optarg; +int optind, opterr, optopt; + +int +getopt(int argc, char *argv[], const char *optstring) +{ + size_t optstr_len; + char *arg; + bool has_arg = false; + + if (argc == 0 || optstring == NULL) { + opterr = -EINVAL; + return -1; + } + + if (optind >= argc) { + return -1; + } + + arg = argv[optind]; + optstr_len = strlen(optstring); + + /* Non option argument? */ + if (arg[0] != '-') { + return -1; + } + + /* + * We will look through each possible flag/option + * in the optstring and match it against our arg. + */ + for (size_t i = 0; i < optstr_len; ++i) { + if (arg[1] != optstring[i]) { + continue; + } + + /* + * If this option has a ':' right next to it, + * it also has an argument. + */ + if (i < optstr_len - 1) { + if (optstring[i + 1] == ':') { + has_arg = true; + } + } + + break; + } + + /* + * Handle cases where the option has an argument + * with it (-opt=arg) + */ + if (has_arg && optind < argc ) { + if (arg[2] != '=') { + opterr = -EINVAL; + return -1; + } + optarg = &arg[3]; + ++optind; + } + + ++optind; + return arg[1]; +} diff --git a/lib/libgfx/include/libgfx/draw.h b/lib/libgfx/include/libgfx/draw.h index 9f56f6c..4140593 100644 --- a/lib/libgfx/include/libgfx/draw.h +++ b/lib/libgfx/include/libgfx/draw.h @@ -34,7 +34,8 @@ #include <libgfx/gfx.h> /* Shape types */ -#define SHAPE_SQUARE 0x00000000 +#define SHAPE_SQUARE 0x00000000 +#define SHAPE_SQUARE_BORDER 0x00000001 /* Basic color defines */ #define GFX_BLACK 0x000000 @@ -101,9 +102,26 @@ struct gfx_point { color_t rgb; }; +/* + * Represents a rectangular region on + * the screen. + * + * @x,y: Position of this region on the screen + * @width: Region width + * @heght: Region height + */ +struct gfx_region { + scrpos_t x, y; + dimm_t width; + dimm_t height; +}; + int gfx_draw_shape(struct gfx_ctx *ctx, const struct gfx_shape *shape); int gfx_plot_point(struct gfx_ctx *ctx, const struct gfx_point *point); +int gfx_copy_region(struct gfx_ctx *ctx, struct gfx_region *r, scrpos_t x, scrpos_t y); +color_t gfx_get_pix(struct gfx_ctx *ctx, uint32_t x, uint32_t y); + __always_inline static inline size_t gfx_io_index(struct gfx_ctx *ctx, scrpos_t x, scrpos_t y) { diff --git a/lib/libgfx/src/draw.c b/lib/libgfx/src/draw.c index 9ef8630..4df64a8 100644 --- a/lib/libgfx/src/draw.c +++ b/lib/libgfx/src/draw.c @@ -58,7 +58,7 @@ gfx_pixel_bounds(struct gfx_ctx *ctx, uint32_t x, uint32_t y) scr_width = fbdev.width; scr_height = fbdev.height; - if (x >= scr_height || y >= scr_width) { + if (x >= scr_width || y >= scr_height) { return -1; } @@ -75,6 +75,7 @@ static int gfx_draw_square(struct gfx_ctx *ctx, const struct gfx_shape *shape) { struct fbattr fbdev; + struct gfx_point p; off_t idx; scrpos_t x, y; @@ -84,14 +85,67 @@ gfx_draw_square(struct gfx_ctx *ctx, const struct gfx_shape *shape) for (x = shape->x; x < shape->x + shape->width; ++x) { for (y = shape->y; y < shape->y + shape->height; ++y) { - if (gfx_pixel_bounds(ctx, x, y) < 0) { - break; + p.x = x; + p.y = y; + p.rgb = shape->color; + gfx_plot_point(ctx, &p); + } + } + return 0; +} + +/* + * Draw a bordered square onto the screen. + * + * @ctx: Graphics context pointer + * @shape: Bordered square to draw + */ +static int +gfx_draw_bsquare(struct gfx_ctx *ctx, const struct gfx_shape *shape) +{ + struct gfx_point p; + scrpos_t x_i, y_i; + scrpos_t x_f, y_f; + scrpos_t x, y; + + if (ctx == NULL || shape == NULL) { + return -EINVAL; + } + + x_i = shape->x; + y_i = shape->y; + x_f = shape->x + shape->width; + y_f = shape->y + shape->height; + + /* + * Draw an unfilled square. + * + * If we are at the `y_i' or `y_f' position, draw + * pixels from 'x_i' to 'x_f'. If we are away + * from the `y_i' position, draw two pixels, + * one at `x_i' and the other at `x_f' for that + * current 'y' value. + */ + for (y = y_i; y < y_f; ++y) { + for (x = x_i; x < x_f; ++x) { + p.x = x; + p.y = y; + p.rgb = shape->color; + + /* Origin y, draw entire width */ + if (y == y_i || y == y_f - 1) { + gfx_plot_point(ctx, &p); + continue; } - idx = gfx_io_index(ctx, x, y); - ctx->io[idx] = shape->color; + p.x = x_i; + gfx_plot_point(ctx, &p); + + p.x = x_f - 1; + gfx_plot_point(ctx, &p); } } + return 0; } @@ -129,6 +183,34 @@ gfx_plot_point(struct gfx_ctx *ctx, const struct gfx_point *point) } /* + * Grab the RGB value of a single pixel on + * the scren. + * + * @ctx: Graphics context pointer + * @x: X position to sample + * @y: Y position to sample + */ +color_t +gfx_get_pix(struct gfx_ctx *ctx, uint32_t x, uint32_t y) +{ + const color_t ERROR_COLOR = GFX_BLACK; + uint32_t index; + + /* The 'ctx' argument is required */ + if (ctx == NULL) { + return ERROR_COLOR; + } + + /* Are we within bounds of the screen */ + if (gfx_pixel_bounds(ctx, x, y) < 0) { + return ERROR_COLOR; + } + + index = gfx_io_index(ctx, x, y); + return ctx->io[index]; +} + +/* * Draw a shape onto the screen * * @ctx: libgfx graphics context @@ -150,7 +232,51 @@ gfx_draw_shape(struct gfx_ctx *ctx, const struct gfx_shape *shape) switch (shape->type) { case SHAPE_SQUARE: return gfx_draw_square(ctx, shape); + case SHAPE_SQUARE_BORDER: + return gfx_draw_bsquare(ctx, shape); } return -1; } + +/* + * Copy a region on one part of a screen to + * another part of a screen. + * + * @ctx: Graphics context pointer + * @r: Region to copy + * @x: X position for copy dest + * @y: Y position for copy dest + */ +int +gfx_copy_region(struct gfx_ctx *ctx, struct gfx_region *r, scrpos_t x, scrpos_t y) +{ + struct gfx_point point; + color_t pixel; + scrpos_t src_cx, src_cy; + dimm_t w, h; + + if (ctx == NULL || r == NULL) { + return -EINVAL; + } + + w = r->width; + h = r->height; + + for (int xoff = 0; xoff < w; ++xoff) { + for (int yoff = 0; yoff < h; ++yoff) { + /* Source position */ + src_cx = r->x + xoff; + src_cy = r->y + yoff; + + /* Plot the new pixel */ + pixel = gfx_get_pix(ctx, src_cx, src_cy); + point.x = x + xoff; + point.y = y + yoff; + point.rgb = pixel; + gfx_plot_point(ctx, &point); + } + } + + return 0; +} diff --git a/lib/liboda/include/liboda/input.h b/lib/liboda/include/liboda/input.h index 9906329..c74a304 100644 --- a/lib/liboda/include/liboda/input.h +++ b/lib/liboda/include/liboda/input.h @@ -30,7 +30,6 @@ #ifndef LIBODA_INPUT_H #define LIBODA_INPUT_H -#include <sys/ascii.h> #include <stdint.h> #include <stddef.h> @@ -42,12 +41,22 @@ #define ODA_KEYCHAR(KEY) ((char )(KEY) & 0xFF) /* + * Key defines + */ +#define ODA_KEY_OTHER 0x0000 +#define ODA_KEY_ESCAPE 0x0001 +#define ODA_KEY_TAB 0x0002 +#define ODA_KEY_BACKSPACE 0x0003 + +/* * Represents a key press * + * @type: Key types (see ODA_KEY_*) * @scancode: Scancode * @ch: Character */ struct oda_key { + uint16_t type; uint8_t scancode; char ch; }; diff --git a/lib/liboda/include/liboda/oda.h b/lib/liboda/include/liboda/oda.h index 10738a9..9d96f2f 100644 --- a/lib/liboda/include/liboda/oda.h +++ b/lib/liboda/include/liboda/oda.h @@ -40,10 +40,12 @@ /* * ODA representation of a window. * + * @wid: Window ID (identifies the window) * @surface: Window surface descriptor * @session: Session this window belongs to */ struct oda_window { + odawid_t wid; struct gfx_shape surface; struct oda_state *session; TAILQ_ENTRY(oda_window) link; @@ -81,6 +83,22 @@ struct oda_wattr { }; /* + * Arguments for oda_movewin() are stored + * within this structure to minimize the + * number of arguments within the function + * signature. + * + * @wp: Window to be moved + * @to_x: X position to move window to + * @to_y: Y position to move window to + */ +struct oda_movewin { + struct oda_window *wp; + odapos_t to_x; + odapos_t to_y; +}; + +/* * A pixel point that can be plotted * onto a window. * @@ -99,9 +117,12 @@ struct oda_point { int oda_reqwin(struct oda_wattr *params, struct oda_window **res); int oda_termwin(struct oda_state *state, struct oda_window *win); -int oda_plotwin(struct oda_state *state, const struct oda_point *point); +int oda_plotwin(struct oda_state *state, const struct oda_point *point); +int oda_movewin(struct oda_state *state, struct oda_movewin *params); int oda_start_win(struct oda_state *state, struct oda_window *win); + int oda_init(struct oda_state *res); +int oda_shutdown(struct oda_state *state); #endif /* !LIBODA_ODA_H */ diff --git a/lib/liboda/include/liboda/types.h b/lib/liboda/include/liboda/types.h index 8b77aa7..ec12330 100644 --- a/lib/liboda/include/liboda/types.h +++ b/lib/liboda/include/liboda/types.h @@ -36,5 +36,6 @@ typedef uint32_t odapos_t; /* X/Y positions */ typedef uint32_t odapix_t; /* RGB pixel */ typedef odapix_t odacolor_t; /* RGB color */ typedef uint32_t odadimm_t; /* Dimensions */ +typedef uint32_t odawid_t; /* Window ID */ #endif /* !LIBODA_TYPE_H */ diff --git a/lib/liboda/src/input.c b/lib/liboda/src/input.c index 88c4256..797a9d4 100644 --- a/lib/liboda/src/input.c +++ b/lib/liboda/src/input.c @@ -27,13 +27,39 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <sys/ascii.h> #include <sys/errno.h> +#include <sys/param.h> #include <liboda/oda.h> #include <liboda/input.h> #include <stdint.h> #include <stdio.h> /* + * Convert key scancode/char values to fixed + * ODA key constants + */ +static inline uint16_t +oda_map_key(const struct oda_key *key) +{ + uint16_t type = ODA_KEY_OTHER; + + switch (key->ch) { + case ASCII_ESC: + type = ODA_KEY_ESCAPE; + break; + case ASCII_HT: + type = ODA_KEY_TAB; + break; + case ASCII_BS: + type = ODA_KEY_BACKSPACE; + break; + } + + return type; +} + +/* * Dispatch keyboard events. This is typically * called in an event loop so that keyboard events * are handled per iteration. @@ -57,5 +83,6 @@ oda_kbd_dispatch(struct oda_kbd *kbd) key.scancode = ODA_SCANCODE(input); key.ch = ODA_KEYCHAR(input); + key.type = oda_map_key(&key); return kbd->handle_keyev(kbd, &key); } diff --git a/lib/liboda/src/window.c b/lib/liboda/src/window.c index 9a8b799..216b106 100644 --- a/lib/liboda/src/window.c +++ b/lib/liboda/src/window.c @@ -28,12 +28,121 @@ */ #include <sys/errno.h> +#include <sys/queue.h> #include <stdlib.h> #include <string.h> #include <liboda/oda.h> #include <liboda/odavar.h> #include <liboda/types.h> #include <libgfx/gfx.h> +#include <libgfx/draw.h> + +/* + * The window cache is used to reduce how many + * calls to malloc() and free() are made during + * window creation and destruction. + */ +static TAILQ_HEAD(, oda_window) wcache; +static uint32_t wcache_cookie = 0; +static odawid_t next_wid = 1; + +/* + * Pop a window from the window cache. + * Returns NULL there are no more windows. + */ +static struct oda_window * +oda_window_pop(void) +{ + struct oda_window *wdp; + + if (wcache_cookie != ODA_COOKIE) { + TAILQ_INIT(&wcache); + wcache_cookie = ODA_COOKIE; + return NULL; + } + + wdp = TAILQ_FIRST(&wcache); + TAILQ_REMOVE(&wcache, wdp, link); + return wdp; +} + +/* + * Place a window into the window cache. + */ +static void +oda_window_cache(struct oda_window *wdp) +{ + /* Ensure arg is valid */ + if (wdp == NULL) { + return; + } + + if (wcache_cookie != ODA_COOKIE) { + TAILQ_INIT(&wcache); + wcache_cookie = ODA_COOKIE; + } + + TAILQ_INSERT_TAIL(&wcache, wdp, link); +} + +/* + * Allocate an ODP window + * + * Returns NULL on failure + */ +static struct oda_window * +oda_window_alloc(void) +{ + struct oda_window *wdp; + + /* + * First check if there are any entries + * we can grab from the window cache. + */ + wdp = oda_window_pop(); + if (wdp != NULL) { + return wdp; + } + + /* Allocate a new window */ + wdp = malloc(sizeof(*wdp)); + if (wdp == NULL) { + return NULL; + } + + memset(wdp, 0, sizeof(*wdp)); + wdp->wid = next_wid++; + return wdp; +} + +/* + * Release a given ODA window descriptor + * + * @wdp: Window to free + */ +static void +oda_window_release(struct oda_state *state, struct oda_window *wdp) +{ + if (wdp == NULL) { + return; + } + + /* + * It is probably a good idea to ensure previous + * state other than the old window ID is reset + * and zeroed. + */ + wdp->session = NULL; + memset(&wdp->surface, 0, sizeof(wdp->surface)); + + /* + * Now we can remove this window from the list + * of windows we are tracking and add it to the + * cache. + */ + TAILQ_REMOVE(&state->winq, wdp, link); + oda_window_cache(wdp); +} /* * Check if a point is within the bounds of @@ -78,6 +187,34 @@ oda_check_point(struct oda_window *wp, struct oda_point *point) } /* + * Clean up after ourselves and release + * each entry of the wcache. + * + * Returns 0 on success. + */ +static int +oda_free_wcache(void) +{ + struct oda_window *wdp, *next; + + if (wcache_cookie != ODA_COOKIE) { + return -1; + } + + /* + * Go through each entry and call free() + * on them. + */ + wdp = TAILQ_FIRST(&wcache); + while (wdp != NULL) { + next = TAILQ_NEXT(wdp, link); + free(wdp); + wdp = next; + } + return 0; +} + +/* * Plot a pixel onto a window * * @state: ODA state pointer @@ -176,7 +313,7 @@ oda_reqwin(struct oda_wattr *params, struct oda_window **res) } /* Allocate a new window */ - wp = malloc(sizeof(*wp)); + wp = oda_window_alloc(); if (wp == NULL) { return -ENOMEM; } @@ -205,6 +342,61 @@ oda_reqwin(struct oda_wattr *params, struct oda_window **res) } /* + * Move a window to a new position on the + * screen. + * + * @state: ODA state pointer + * @params: Arguments to this function + */ +int +oda_movewin(struct oda_state *state, struct oda_movewin *params) +{ + struct oda_window *win; + struct gfx_shape *wsurf; + struct gfx_region r; + odadimm_t w, h; + odapos_t x, y, x_f, y_f; + odapos_t to_x, to_y; + uint32_t i = 0; + int error; + + /* Make sure arguments are valid */ + if (state == NULL || params == NULL) { + return -EINVAL; + } + + /* We need the window */ + if ((win = params->wp) == NULL) { + return -EINVAL; + } + + /* Verify state cookie */ + if ((error = oda_cookie_verify(state)) != 0) { + return error; + } + + wsurf = &win->surface; + to_x = params->to_x; + to_y = params->to_y; + + r.x = wsurf->x; + r.y = wsurf->y; + r.width = wsurf->width; + r.height = wsurf->height; + + /* + * We will copy the window to the new location and + * fill where the old window was with GFX_BLACK. + * + * TODO: Handle overlapping of windows + */ + gfx_copy_region(&state->gctx, &r, to_x, to_y); + wsurf->x = to_x; + wsurf->y = to_y; + return 0; +} + +/* * Register a window into the current ODA state. * Everytime a compositor requests a window, we * must keep track of it. @@ -255,7 +447,15 @@ oda_termwin(struct oda_state *state, struct oda_window *win) return error; } - TAILQ_REMOVE(&state->winq, win, link); - free(win); + oda_window_release(state, win); return 0; } + +/* + * Shutdown the ODA library + */ +int +oda_shutdown(struct oda_state *state) +{ + return oda_free_wcache(); +} diff --git a/share/docs/lib/liboda.md b/share/docs/lib/liboda.md index ee82dc0..e5345a4 100644 --- a/share/docs/lib/liboda.md +++ b/share/docs/lib/liboda.md @@ -189,3 +189,41 @@ structure to minimize the number of parameters used in the function signature. Upon failure of ``oda_reqwin()`` a negative POSIX errno value is returned (see ``sys/errno.h``). + +### Input management + +The liboda library additionally provides an input API that facilitates +management of user input (e.g., keyboard). + +(See ``liboda/input.h``) + +#### The ``oda_key`` structure + +ODA provides a standard description of keys received from a keyboard +device. Keyboard input is represented by the ``oda_key`` structure shown +below: + +```c +struct oda_key { + uint16_t type; + uint8_t scancode; + char ch; + ... +}; +``` + +The ``type`` field describes the key type, valid key types can be +found within the ``ODA_KEY_*`` definitions in ``liboda/input.h``. + +The ``scancode`` field contains the raw keyboard scancode received +from the device. + +The ``ch`` field contains the ASCII representation of the input received +from the device. + +#### The ``oda_kbd`` structure + +ODA represents keyboard devices through the ``oda_kbd`` structure found +in ``liboda/input.h``. This structure contains callbacks that are set up +by the compositor to be invoked when their respective keyboard related +event occurs. diff --git a/share/man/man1/cat.1 b/share/man/man1/cat.1 index 8214724..1759407 100644 --- a/share/man/man1/cat.1 +++ b/share/man/man1/cat.1 @@ -24,7 +24,7 @@ .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. -.Dd Jul 17 2025 +.Dd Aug 6 2025 .Dt CAT 1 .Os HYRA .Sh NAME @@ -39,6 +39,11 @@ The command can be used concatenate files or simply write their contents to standard output. +.Bd -literal +[-n]: Number each line +[-b]: Number each non-blank line +.Ed + .Sh SEE ALSO mex(1) diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c index 19dcf44..9ff96e1 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -47,6 +47,13 @@ #include <dev/cons/cons.h> #include <string.h> +/* + * This defines the max number of frames + * we will pass while walking the callstack + * in md_backtrace() + */ +#define MAX_FRAME_DEPTH 16 + #define pr_trace(fmt, ...) kprintf("cpu: " fmt, ##__VA_ARGS__) #define pr_error(...) pr_trace(__VA_ARGS__) #define pr_trace_bsp(...) \ @@ -259,24 +266,43 @@ void md_backtrace(void) { uintptr_t *rbp; - uintptr_t rip; + uintptr_t rip, tmp; off_t off; const char *name; char line[256]; + uint8_t n = 0; __ASMV("mov %%rbp, %0" : "=r" (rbp) :: "memory"); while (1) { + if (n >= MAX_FRAME_DEPTH) { + break; + } + rip = rbp[1]; rbp = (uintptr_t *)rbp[0]; - name = backtrace_addr_to_name(rip, &off); - if (rbp == NULL) + /* + * RBP should be aligned on an 8-byte + * boundary... Don't trust this state + * anymore if it is not. + */ + tmp = (uintptr_t)rbp; + if ((tmp & (8 - 1)) != 0) { + break; + } + + /* + * This is not a valid value, get out + * of this loop!! + */ + if (rbp == NULL || rip == 0) { break; - if (name == NULL) - name = "???"; + } + name = backtrace_addr_to_name(rip, &off); snprintf(line, sizeof(line), "%p @ <%s+0x%x>\n", rip, name, off); cons_putstr(&g_root_scr, line, strlen(line)); + ++n; } } diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h index 89fe638..809ee23 100644 --- a/sys/include/sys/proc.h +++ b/sys/include/sys/proc.h @@ -122,8 +122,10 @@ struct proc { #define PROC_PINNED BIT(7) /* Pinned to CPU */ struct proc *this_td(void); +struct proc *td_copy(struct proc *td); struct proc *get_child(struct proc *cur, pid_t pid); +int proc_init(struct proc *td, struct proc *parent); void proc_pin(struct proc *td, affinity_t cpu); void proc_unpin(struct proc *td); diff --git a/sys/include/sys/sched.h b/sys/include/sys/sched.h index 7a859b2..19ceb7e 100644 --- a/sys/include/sys/sched.h +++ b/sys/include/sys/sched.h @@ -33,6 +33,7 @@ #include <sys/proc.h> #include <sys/cdefs.h> #include <sys/limits.h> +#include <sys/time.h> /* * Scheduler CPU information @@ -66,6 +67,7 @@ void sched_stat(struct sched_stat *statp); void sched_init(void); void sched_yield(void); +void sched_suspend(struct proc *td, const struct timeval *tv); void sched_detach(struct proc *td); __dead void sched_enter(void); diff --git a/sys/include/sys/socket.h b/sys/include/sys/socket.h index c82ae4e..1a33108 100644 --- a/sys/include/sys/socket.h +++ b/sys/include/sys/socket.h @@ -69,6 +69,10 @@ typedef uint32_t socklen_t; /* Socket types */ #define SOCK_STREAM 1 +/* Socket option names */ +#define SO_RCVTIMEO 0 /* Max time recv(2) waits */ +#define _SO_MAX 1 /* Max socket options */ + struct sockaddr_un { sa_family_t sun_family; char sun_path[108]; @@ -149,12 +153,22 @@ struct cmsg_list { uint8_t is_init : 1; }; +/* + * Socket option that may be applied to + * sockets on the system. + */ +struct sockopt { + socklen_t len; + char data[]; +}; + struct ksocket { int sockfd; union { struct sockaddr sockaddr; struct sockaddr_un un; }; + struct sockopt *opt[_SO_MAX]; struct proc *owner; struct cmsg_list cmsg_list; struct sockbuf buf; @@ -170,10 +184,13 @@ scret_t sys_send(struct syscall_args *scargs); scret_t sys_recvmsg(struct syscall_args *scargs); scret_t sys_sendmsg(struct syscall_args *scargs); +scret_t sys_setsockopt(struct syscall_args *scargs); #endif /* _KERNEL */ int socket(int domain, int type, int protocol); int bind(int sockfd, const struct sockaddr *addr, socklen_t len); + +int setsockopt(int sockfd, int level, int name, const void *v, socklen_t len); int connect(int sockfd, const struct sockaddr *addr, socklen_t len); ssize_t send(int sockfd, const void *buf, size_t size, int flags); diff --git a/sys/include/sys/syscall.h b/sys/include/sys/syscall.h index 9798d80..f53db8f 100644 --- a/sys/include/sys/syscall.h +++ b/sys/include/sys/syscall.h @@ -66,6 +66,7 @@ #define SYS_sendmsg 25 #define SYS_recvmsg 26 #define SYS_connect 27 +#define SYS_setsockopt 28 #if defined(_KERNEL) /* Syscall return value and arg type */ diff --git a/sys/include/sys/sysctl.h b/sys/include/sys/sysctl.h index 078135b..d13b0f8 100644 --- a/sys/include/sys/sysctl.h +++ b/sys/include/sys/sysctl.h @@ -30,9 +30,12 @@ #ifndef _SYS_SYSCTL_H_ #define _SYS_SYSCTL_H_ -#include <sys/types.h> #if defined(_KERNEL) +#include <sys/types.h> #include <sys/syscall.h> +#else +#include <stdint.h> +#include <stddef.h> #endif #include <sys/param.h> diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 5e351a8..e8255bd 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -119,6 +119,7 @@ main(void) md_inton(); /* Load all early drivers */ + driver_blacklist("ahci"); DRIVERS_INIT(); /* Only log to kmsg from here */ diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c new file mode 100644 index 0000000..2755ea0 --- /dev/null +++ b/sys/kern/kern_fork.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/proc.h> + + diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index 23a1ebb..9c5e215 100644 --- a/sys/kern/kern_sched.c +++ b/sys/kern/kern_sched.c @@ -315,6 +315,92 @@ proc_unpin(struct proc *td) td->flags &= ~PROC_PINNED; } +/* + * Suspend a process for a specified amount + * of time. This calling process will yield for + * the amount of time specified in 'tv' + * + * @td: Process to suspend (NULL for current) + * @tv: Time value to use + * + * XXX: 'tv' being NULL is equivalent to calling + * sched_detach() + */ +void +sched_suspend(struct proc *td, const struct timeval *tv) +{ + struct timer tmr; + const time_t USEC_PER_SEC = 1000000; + ssize_t usec; + time_t usec_cur, usec_tmp; + bool have_timer = true; + tmrr_status_t tmr_status; + + if (td == NULL) + td = this_td(); + if (__unlikely(td == NULL)) + return; + + if (tv == NULL) { + sched_detach(td); + return; + } + + /* + * Now, we need a generic timer so that we can compute + * how much time has elapsed since this process has + * requested to be suspended. However, we cannot assume + * that it would be present. If the lookup fails, all we + * can do is try to estimate how much time went by which + * works fine too, just not as accurate. + */ + tmr_status = req_timer(TIMER_GP, &tmr); + if (tmr_status != TMRR_SUCCESS) { + have_timer = false; + } + + /* We need microsecond precision */ + if (tmr.get_time_sec == NULL) { + have_timer = false; + } + + /* + * Compute the max time in microseconds that + * we will wait. We are using both tv->tv_sec + * and tv->tv_usec + */ + usec = tv->tv_usec; + usec += tv->tv_sec * USEC_PER_SEC; + usec_cur = (have_timer) ? tmr.get_time_usec() : 0; + + for (;;) { + sched_yield(); + + /* + * If we have a timer in our paws, compute how much + * time went by. Otherwise we estimate by subtracting + * the scheduler quantum. + * + * XXX: The timing here works decently as intended. However, + * it would be nice to smoothen out any jitter. Such can + * probably be done by subtracting 'usec' by the exponential + * moving average of 'usec_tmp' rather than the raw original + * value. + */ + if (have_timer) { + usec_tmp = (tmr.get_time_usec() - usec_cur); + } else { + usec_tmp = DEFAULT_TIMESLICE_USEC; + } + + /* We are done here! */ + usec -= usec_tmp; + if (usec <= 0) { + break; + } + } +} + void sched_init(void) { diff --git a/sys/kern/kern_socket.c b/sys/kern/kern_socket.c index 1717f5a..d0fbe19 100644 --- a/sys/kern/kern_socket.c +++ b/sys/kern/kern_socket.c @@ -31,6 +31,7 @@ #include <sys/sio.h> #include <sys/systm.h> #include <sys/proc.h> +#include <sys/time.h> #include <sys/namei.h> #include <sys/sched.h> #include <sys/errno.h> @@ -47,6 +48,17 @@ static struct vops socket_vops; /* + * This table maps socket option names to + * lengths of their underlying structure. + * + * This is used for bounds/length checking within + * setsockopt() + */ +static size_t sockopt_lentab[_SO_MAX] = { + [ SO_RCVTIMEO ] = sizeof(struct timeval) +}; + +/* * Get a kernel socket structure from a * file descriptor. * @@ -101,6 +113,7 @@ static int socket_reclaim(struct vnode *vp) { struct ksocket *ksock; + struct sockopt *opt; /* Is this even a socket? */ if (vp->type != VSOCK) { @@ -112,6 +125,15 @@ socket_reclaim(struct vnode *vp) return -EIO; } + /* Free up any used options */ + for (int i = 0; i < _SO_MAX; ++i) { + opt = ksock->opt[i]; + if (opt != NULL) { + dynfree(opt); + ksock->opt[i] = NULL; + } + } + fd_close(ksock->sockfd); mutex_free(ksock->mtx); dynfree(ksock); @@ -185,6 +207,46 @@ connect_domain(int sockfd, struct ksocket *ksock, struct sockaddr_un *un) } /* + * Wait until data is received for the + * recv() function. + * + * @sockfd: Socket we are waiting on + * + * Returns zero on success, otherwise a less + * than zero value is returned. + */ +static int +socket_rx_wait(int sockfd) +{ + struct ksocket *ksock; + struct sockopt *opt; + struct timeval tv; + int error; + + if (ksock == NULL) { + return -EINVAL; + } + + error = get_ksock(sockfd, &ksock); + if (error < 0) { + return error; + } + + /* + * If the socket does not have this option set, + * we will assume that there is no timeout value. + */ + opt = ksock->opt[SO_RCVTIMEO]; + if (opt == NULL) { + return 0; + } + + memcpy(&tv, opt->data, opt->len); + sched_suspend(NULL, &tv); + return 0; +} + +/* * Send data to socket - POSIX send(2) core * * @sockfd: File descriptor that backs this socket @@ -254,7 +316,7 @@ send(int sockfd, const void *buf, size_t size, int flags) * @size: Size of the buffer * @flags: Optional flags * - * Returns zero on success, otherwise a less + * Returns length on success, otherwise a less * than zero errno. */ ssize_t @@ -264,6 +326,7 @@ recv(int sockfd, void *buf, size_t len, int flags) struct sockbuf *sbuf; struct netbuf *netbuf; size_t head; + ssize_t retval = len; int error; /* Length cannot be zero */ @@ -283,7 +346,8 @@ recv(int sockfd, void *buf, size_t len, int flags) if (netbuf->len == 0) { sbuf->head = 0; sbuf->tail = 0; - return -EAGAIN; + retval = -EAGAIN; + goto done; } if (len > netbuf->len) { @@ -292,10 +356,10 @@ recv(int sockfd, void *buf, size_t len, int flags) head = sbuf->head; memcpy(buf, &netbuf->data[head], len); - sbuf->head = (sbuf->head + len) % NETBUF_LEN; +done: mutex_release(ksock->mtx); - return len; + return retval; } /* @@ -322,6 +386,7 @@ socket(int domain, int type, int protocol) goto fail; } + memset(ksock, 0, sizeof(*ksock)); sbuf = &ksock->buf; sbuf->head = 0; sbuf->tail = 0; @@ -394,6 +459,68 @@ bind(int sockfd, const struct sockaddr *addr, socklen_t len) } /* + * Set socket options - POSIX setsockopt(3) core + * + * @sockfd: File descriptor of socket + * @level: Protocol level + * @v: Options value + * @len: Length of data pointed to by 'v' + */ +int +setsockopt(int sockfd, int level, int name, const void *v, socklen_t len) +{ + struct ksocket *ksock; + struct sockopt *opt; + size_t exp_len; + int error; + + /* Must have a valid fd */ + if (sockfd < 0) { + return -EBADF; + } + + /* Ensure value and length are valid */ + if (v == NULL || len == 0) { + return -EINVAL; + } + + /* Verify the name */ + if (name >= _SO_MAX) { + return -EINVAL; + } + + /* Grab a new socket */ + if ((error = get_ksock(sockfd, &ksock)) < 0) { + return error; + } + + /* Clamp the input length as needed */ + exp_len = sockopt_lentab[name]; + if (len > exp_len) { + len = exp_len; + } + + /* + * Here we will grab the socket options. If it is + * NULL, we'll need to allocate one. + */ + if ((opt = ksock->opt[name]) == NULL) { + opt = dynalloc(sizeof(*opt) + len); + + if (opt == NULL) { + return -ENOMEM; + } + + opt->len = len; + ksock->opt[name] = opt; + } + + memcpy(opt->data, v, len); + opt->len = len; + return 0; +} + +/* * Connect to a socket * * @sockfd: File descriptor to connect @@ -614,10 +741,29 @@ sys_recv(struct syscall_args *scargs) return -ENOBUFS; } - do { + for (;;) { error = recv(sockfd, buf, len, flags); - sched_yield(); - } while (error == -EAGAIN); + if (error <= 0 && error != -EAGAIN) { + break; + } + + /* + * Wait for data to be ready on the socket. + * If a less than zero value is returned, don't + * handle timeouts. + */ + error = socket_rx_wait(sockfd); + if (error < 0) { + continue; + } + + /* Try one more time, obey timeout */ + error = recv(sockfd, buf, len, flags); + if (error == -EAGAIN) { + return error; + } + break; + } if (error < 0) { pr_error("sys_recv: recv() fail (fd=%d)\n", sockfd); @@ -776,7 +922,7 @@ sys_sendmsg(struct syscall_args *scargs) } /* - * connnect(3) syscall + * connect(3) syscall * * arg0: sockfd * arg1: address @@ -807,6 +953,55 @@ sys_connect(struct syscall_args *scargs) return connect(sockfd, sockaddr, len); } +/* + * POSIX setsockopt(3) syscall + * + * arg0: sockfd + * arg1: level + * arg2: name + * arg3: data + * arg4: len + */ +scret_t +sys_setsockopt(struct syscall_args *scargs) +{ + int sockfd = scargs->arg0; + int level = scargs->arg1; + int name = scargs->arg2; + void *u_data = (void *)scargs->arg3; + socklen_t len = scargs->arg4; + void *data; + size_t exp_len; + int retval; + + /* Verify that the name is correct */ + if (name >= _SO_MAX) { + return -EINVAL; + } + + /* Clamp length as needed */ + exp_len = sockopt_lentab[name]; + if (len > exp_len) { + len = exp_len; + } + + data = dynalloc(len); + if (data == NULL) { + return -ENOMEM; + } + + /* Grab data from userland */ + retval = copyin(u_data, data, len); + if (retval < 0) { + dynfree(data); + return retval; + } + + retval = setsockopt(sockfd, level, name, data, len); + dynfree(data); + return retval; +} + static struct vops socket_vops = { .read = NULL, .write = NULL, diff --git a/sys/kern/kern_syscall.c b/sys/kern/kern_syscall.c index 576b7aa..ba70b12 100644 --- a/sys/kern/kern_syscall.c +++ b/sys/kern/kern_syscall.c @@ -68,6 +68,7 @@ scret_t(*g_sctab[])(struct syscall_args *) = { sys_sendmsg, /* SYS_sendmsg */ sys_recvmsg, /* SYS_recvmsg */ sys_connect, /* SYS_connect */ + sys_setsockopt, /* SYS_setsockopt */ }; const size_t MAX_SYSCALLS = NELEM(g_sctab); diff --git a/usr.bin/cat/cat.c b/usr.bin/cat/cat.c index 35e9e95..4fb74d1 100644 --- a/usr.bin/cat/cat.c +++ b/usr.bin/cat/cat.c @@ -34,12 +34,27 @@ #include <stdio.h> #include <stdlib.h> +#define NUM_MODE_NONE 0 +#define NUM_MODE_ALL 1 +#define NUM_MODE_NONBLANK 2 + +static void +help(void) +{ + printf( + "usage: cat <flags> <file>\n" + "[-b] do not number blank lines\n" + "[-n] number all lines\n" + ); +} + static void -cat(const char *pathname) +cat(const char *pathname, int num_mode) { FILE *file; char buf[64]; int fd; + size_t lineno = 1; file = fopen(pathname, "r"); if (file == NULL) { @@ -47,7 +62,21 @@ cat(const char *pathname) } while (fgets(buf, sizeof(buf), file) != NULL) { + switch (num_mode) { + case NUM_MODE_NONE: + break; + case NUM_MODE_ALL: + printf("%d ", lineno); + break; + case NUM_MODE_NONBLANK: + if (buf[0] == '\n') { + break; + } + printf("%d ", lineno); + break; + } printf("%s", buf); + ++lineno; } fclose(file); @@ -56,8 +85,27 @@ cat(const char *pathname) int main(int argc, char **argv) { - for (size_t i = 1; i < argc; ++i) { - cat(argv[i]); + int num_mode = NUM_MODE_NONE; + int c; + + if (argc < 2) { + help(); + return -1; + } + + while ((c = getopt(argc, argv, "nb")) != -1) { + switch (c) { + case 'n': + num_mode = NUM_MODE_ALL; + break; + case 'b': + num_mode = NUM_MODE_NONBLANK; + break; + } + } + + for (size_t i = optind; i < argc; ++i) { + cat(argv[i], num_mode); } return 0; |