diff --git a/README.md b/README.md index 1f323d3..8d46823 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ [![Architecture](https://img.shields.io/badge/arch-x86__64%20Long%20Mode-blue.svg?style=flat-square)]() [![Kernel](https://img.shields.io/badge/kernel-Modular%20Monolithic-red.svg?style=flat-square)]() [![Version](https://img.shields.io/badge/version-v0.2.0--lts%20(Titan)-purple.svg?style=flat-square)]() -[![Userland](https://img.shields.io/badge/userland-LazyBox%20v2.0.0--pro%20%2B%20Nano-orange.svg?style=flat-square)]() -[![Networking](https://img.shields.io/badge/network-Intel%20E1000%20Gigabit%20%7C%20IPv4%20%7C%20BSD%20Sockets-green.svg?style=flat-square)]() +[![Userland](https://img.shields.io/badge/userland-LazyBox%20v2.0.0--pro%20%2B%20Nano%20%2B%20Sh-orange.svg?style=flat-square)]() +[![Networking](https://img.shields.io/badge/network-Intel%20E1000%20%7C%20NetFilter%20Firewall%20%7C%20BSD%20Sockets-green.svg?style=flat-square)]() [![License](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat-square)](LICENSE) -**SUB-OS** is an open-source, production-grade 64-bit modular monolithic operating system engineered from the ground up for the `x86_64` architecture. Built adhering to the internal design principles of modern UNIX/Linux kernels, SUB-OS features a full bare-metal hardware driver tree, physical and dynamic memory management, a virtual file system (VFS) with FAT32, ext2, sysfs, and devfs, full L2-L4 networking protocol stack with BSD Sockets, cryptographic engine, Linux Security Modules (LSM), asynchronous `io_uring` ringbuffers, Inter-Process Communication (IPC) engine, Text-to-Speech (TTS) voice synthesizer, AC'97 sound architecture, full-screen visual `nano` editor, and the **LazyBox** multi-call userland environment with 50+ Linux utilities. +**SUB-OS** is an open-source, production-grade 64-bit modular monolithic operating system engineered from the ground up for the `x86_64` architecture. Built adhering to the internal design principles of modern UNIX/Linux kernels while maintaining an elegant, easy-to-understand codebase, SUB-OS features a bare-metal driver tree, physical and dynamic memory management with a **SLAB Object Allocator**, an advanced virtual file system (VFS) with FAT32, ext2, sysfs, and devfs, **Lightweight Process & Container Namespaces**, **Kernel Event Tracing (FTrace-like ringbuffers)**, **Stateful Packet Inspection (NetFilter Firewall)**, **Ramdisk (`/dev/ram0`)**, a full L2-L4 networking protocol stack with BSD Sockets, **8-Bit Retro Melody Synth & Formant Text-to-Speech (TTS)** voice synthesizer, AC'97 sound architecture, full-screen visual `nano` editor, and the **LazyBox** multi-call userland environment with **55+ Linux-compatible utilities** and shell scripting (`sh`). --- @@ -25,28 +25,28 @@ ```text +------------------------------------------------------------------------------------+ | USERLAND & SYSTEM SUITE | -| Interactive Shell | GNU Nano 2.0.0 | LazyBox Multi-Call Suite (50+ Coreutils) | +| Interactive Shell | GNU Nano 2.0.0 | Script Runner (sh) | LazyBox (55+ Tools) | +------------------------------------------------------------------------------------+ | VIRTUAL FILE SYSTEM LAYER (VFS) | | vfs_namei | RAMFS (/) | /dev (devfs) | /proc (procfs) | /sys (sysfs) | FAT32 | ext2| +------------------------------------------------------------------------------------+ -| INTER-PROCESS COMMUNICATION & ASYNC I/O ENGINE | -| Anonymous Pipes | Message Queues | Shared Memory | Semaphores | io_uring Rings | +| NAMESPACES, INTER-PROCESS COMMUNICATION & ASYNC I/O ENGINE | +| PID & UTS Isolation | Anonymous Pipes | Message Queues | SHM | Semaphores | io_uring| +------------------------------------------------------------------------------------+ -| CORE KERNEL SERVICES & SECURITY MODULE (LSM) | +| CORE KERNEL SERVICES, SECURITY & TRACING | | Preemptive Scheduler | Task Control | POSIX Signals | Fast Syscalls | Workqueues | -| High-Res Timer Wheel | Dynamic Module Loader | X.509 Keyring | POSIX Capabilities | +| High-Res Timer Wheel | Dynamic Module Loader | Kernel Event Tracer | LSM Security | +------------------------------------------------------------------------------------+ -| NETWORK PROTOCOL STACK (L2 -> L4) & SOUND | -| Ethernet L2 | ARP | IPv4 | ICMP | UDP | TCP | DHCP | DNS | BSD Sockets | -| ALSA Sound Architecture | AC'97 Audio | PCM Streams | Formant TTS Synthesizer | +| NETWORKING, NETFILTER FIREWALL & SOUND ARCHITECTURE | +| Ethernet L2 | ARP | IPv4 | ICMP | UDP | TCP | DHCP | DNS | Sockets | NetFilter FW | +| ALSA Sound Core | AC'97 Audio | Formant TTS Synthesizer | 8-Bit Melody Engine | +------------------------------------------------------------------------------------+ | MEMORY MANAGEMENT (MM) | -| Physical Page Frame Allocator (PMM) | Kernel Dynamic Heap (kmalloc/kfree) | +| Physical Page Allocator (PMM) | Dynamic Heap | SLAB / SLUB Object Cache | +------------------------------------------------------------------------------------+ | DEVICE DRIVER TREE & VIRT | -| VGA / TTY1-4 | 16550 UART Serial | PS/2 Keyboard | PS/2 Mouse | ATA / IDE Disk | -| PCI Enumerator | Intel E1000 NIC | VESA FB | CMOS RTC | ACPI Power | USB Core | +| VGA / TTY1-4 | 16550 UART Serial | PS/2 Keyboard | PS/2 Mouse | ATA Hard Disk | +| PCI Enumerator | Intel E1000 NIC | Ramdisk (/dev/ram0) | VESA FB | CMOS RTC | +------------------------------------------------------------------------------------+ | HARDWARE ABSTRACTION LAYER (HAL) | | x86_64 Long Mode | GDT / TSS | IDT / ISRs | Dual 8259A PIC | 8254 PIT Timer | @@ -57,8 +57,6 @@ ## ⚡ 18-Step Master Boot Pipeline -During boot, the kernel executes a deterministic 18-step master initialization sequence: - | Step | Subsystem | Description | |:---:|:---|:---| | **[1/18]** | **64-Bit GDT & TSS** | Configures flat kernel/user code & data segments and Task State Segment with dedicated 64KB kernel stack. | @@ -66,103 +64,32 @@ During boot, the kernel executes a deterministic 18-step master initialization s | **[3/18]** | **Dual 8259A PIC** | Remaps master (IRQ 0-7 -> 0x20-0x27) and slave (IRQ 8-15 -> 0x28-0x2F) interrupt controllers. | | **[4/18]** | **PIT & Timer Wheel** | Configures 8254 PIT channel 0 for 100 Hz (10ms resolution) driving quantum preemption & timer wheel. | | **[5/18]** | **Input & ACPI** | PS/2 Keyboard scancode decoder, PS/2 Mouse packet tracker, and ACPI power management subsystem. | -| **[6/18]** | **PMM & Dynamic Heap** | 64-bit bitmap frame allocator tracking usable RAM via BIOS E820 map and 4 MB dynamic kernel heap. | -| **[7/18]** | **Block Layer & ATA** | Generic Block Layer with request queues, 'noop' elevator scheduler, and 28-bit LBA ATA driver. | +| **[6/18]** | **PMM, Heap & SLAB** | 64-bit bitmap frame allocator, 4 MB kernel heap, and high-performance SLAB object cache layer. | +| **[7/18]** | **Block Layer, Ramdisk & ATA** | Generic Block Layer with request queues, Ramdisk (`/dev/ram0`), 'noop' elevator, and ATA driver. | | **[8/18]** | **PCI, VirtIO, FB & USB** | PCI bus enumerator, VirtIO core, Linear 32-bit Framebuffer graphics, and USB host controller. | -| **[9/18]** | **L2-L4 Network Stack** | Intel E1000 NIC, ARP cache, IPv4 routing, ICMP Ping, UDP, TCP state machine, DHCP & DNS. | -| **[10/18]** | **Sound Architecture** | Audio core driver, AC'97 soundcard, PCM stream buffer, and Formant Text-to-Speech synthesizer. | -| **[11/18]** | **Keyring & Security** | Loads X.509 trusted certificate authority keyring and enforces POSIX Capabilities Linux Security Module. | +| **[9/18]** | **L2-L4 Network & NetFilter** | Intel E1000 NIC, NetFilter stateful firewall, ARP, IPv4 routing, ICMP Ping, UDP, TCP, DHCP & DNS. | +| **[10/18]** | **Sound & Melody Synth** | Audio core driver, AC'97 soundcard, 8-Bit Melody player, and Formant Text-to-Speech synthesizer. | +| **[11/18]** | **Keyring, LSM & Namespaces** | Loads X.509 trusted keyring, POSIX Capabilities LSM, and initializes container namespaces. | | **[12/18]** | **IPC Engine** | Anonymous Pipes, System V & POSIX Message Queues, Shared Memory segments, and Semaphore sets. | | **[13/18]** | **io_uring Subsystem** | Lockless Submission Queue Entry (SQE) & Completion Queue Entry (CQE) asynchronous I/O engine. | | **[14/18]** | **Multi-VFS Mount** | Root RAMFS, synthetic `/dev` devfs, `/proc` procfs, `/sys` sysfs, FAT32 driver, ext2 driver & initramfs. | -| **[15/18]** | **Kernel Core Services** | POSIX Signal manager (32 signals), Async Workqueues, 64-bit Fast Syscalls, and Dynamic Module Loader. | +| **[15/18]** | **Kernel Core & Tracing** | POSIX Signals, Workqueues, 64-bit Fast Syscalls, Dynamic Module Loader, and Event Tracer. | | **[16/18]** | **Scheduler & LazyBox** | Starts round-robin preemptive scheduler and launches LazyBox interactive userland environment. | | **[17/18]** | **Hardware Interrupts** | Enables processor interrupts (`sti`) and transitions to userland shell. | -| **[18/18]** | **Interactive Shell** | Launches multi-TTY interactive terminal console on TTY1. | +| **[18/18]** | **Interactive Shell** | Launches multi-TTY interactive terminal console on TTY1 with dynamic CWD prompt. | --- -## 🌟 Subsystems & Codebase Architecture - -```text -SUB-OS/ -├── arch/x86_64/ # Long Mode bootstrap, GDT, IDT, ISRs, PIC, PIT, CPUID, Paging -├── block/ # Block Layer Core, Request Queues, Elevator I/O Schedulers -├── boot/ # Stage 1 MBR Bootloader & Stage 2 64-Bit Protected Mode Switcher -├── certs/ # X.509 ASN.1 DER Parser, Public Key Verification & Root Keyring -├── crypto/ # FIPS SHA-256, RFC 1321 MD5, IEEE 802.3 CRC32, Xorshift128+ PRNG -├── drivers/ -│ ├── block/ # ATA / IDE Hard Disk PIO Driver (28-bit LBA) -│ ├── char/ # VGA Text, TTY1-4 with ANSI sequences, 16550 UART Serial, PS/2 Keyboard -│ ├── input/ # PS/2 Mouse Driver with packet tracking -│ ├── net/ # Intel 82540EM Gigabit Ethernet NIC with DMA Descriptors -│ ├── pci/ # PCI Bus Enumeration & BAR Configuration -│ ├── power/ # ACPI S5 Soft-Off Power Management & Reset -│ ├── rtc/ # CMOS Real-Time Clock Hardware Driver (BCD conversion) -│ ├── sound/ # PC Speaker Tone Generator -│ ├── usb/ # USB Host Controller Abstraction -│ └── video/ # 32-bit Linear Framebuffer Graphics Engine -├── fs/ # VFS Layer, RAMFS, devfs (/dev), procfs (/proc), sysfs (/sys), FAT32, ext2 -├── include/ # Modular Subsystem Header Files -├── init/ # Early Initialization, Boot Command Line Parser, Versioning & Runlevels -├── io_uring/ # Asynchronous I/O Engine with SQE & CQE Lockless Ringbuffers -├── ipc/ # Anonymous Pipes, Message Queues, Shared Memory, Counting Semaphores -├── kernel/ # Task Scheduler, Mutexes, Signals, Syscalls, Timers, Workqueues, Modules -├── lib/ # Standard C Library (string, sprintf, bitmap, list) -├── mm/ # Physical Page Frame Allocator (PMM) & Dynamic Heap (kmalloc) -├── net/ # Ethernet, ARP, IPv4, ICMP, UDP, TCP, DHCP, DNS, BSD Sockets -├── security/ # Linux Security Modules (LSM) & POSIX Capabilities -├── sound/ # ALSA Sound Core, AC'97 Driver, PCM Audio & Text-to-Speech (TTS) Engine -├── usr/ # Embedded Initramfs Payload Tree (/etc/passwd, /etc/fstab, /usr/bin) -└── userland/ # Full-Screen GNU Nano Editor & 50+ LazyBox Core Utilities -``` - ---- - -## 📝 Full-Screen Visual Nano Editor - -SUB-OS includes a full-screen interactive ANSI text editor (`nano`): - -```text - GNU nano 2.0.0 File: /home/user/notes.txt [Modified] -Hello from nano editor in SUB-OS! -This is a production-level modular operating system. -You can write, edit, search, and save files directly to the VFS. -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -[ Wrote 148 bytes to /home/user/notes.txt ] -^G Get Help ^O WriteOut ^W Where Is ^K Cut Line ^U Paste ^X Exit -``` - -### Keybindings: -- **`^O` (Ctrl+O)**: WriteOut / Save file to Virtual File System. -- **`^X` (Ctrl+X)**: Exit Nano and return to shell. -- **`^K` (Ctrl+K)**: Cut current line to cut-buffer. -- **`^U` (Ctrl+U)**: Uncut / Paste line at cursor position. -- **`^G` (Ctrl+G)**: Display help cheat sheet. -- **`Arrow Keys` / `Home` / `End` / `PgUp` / `PgDn`**: Smooth cursor navigation. - ---- - -## 🧰 LazyBox Multi-Call Userland Suite (50+ Utilities) +## 🧰 LazyBox Multi-Call Userland Suite (55+ Utilities) | Category | Commands Included | |---|---| -| **Core** | `lazybox`, `echo`, `whoami`, `id`, `date` (CMOS RTC), `cal` | -| **Filesystem & Editor** | `nano`, `ls`, `cat`, `touch`, `mkdir`, `pwd`, `cd`, `wc`, `head`, `tail`, `stat`, `cp`, `grep`, `hexdump` | -| **Sound & Voice** | `tts` (Phonetic Formant Voice Synthesizer), `alsamixer` | -| **Kernel & Modules** | `lsmod`, `insmod`, `rmmod` | +| **Core & Scripting** | `lazybox`, `sh` (Script runner), `echo`, `env`, `export`, `whoami`, `id`, `date` (CMOS RTC), `cal` | +| **Filesystem & Editor** | `nano`, `ls`, `cat`, `touch`, `mkdir`, `pwd`, `cd`, `wc`, `head`, `tail`, `stat`, `cp`, `grep`, `hexdump`, `basename`, `dirname` | +| **Sound & Voice** | `tts` (Phonetic Formant Voice Synthesizer), `tune` (8-Bit Melody Player), `alsamixer` | +| **Kernel & Tracing** | `lsmod`, `insmod`, `rmmod`, `slabinfo`, `trace`, `unshare` (Namespace containers) | | **Crypto & Security** | `sha256sum`, `md5sum`, `crc32`, `rand`, `certcheck`, `capsh`, `ipcs` | -| **Network** | `ifconfig`, `ping`, `arp`, `dhclient`, `nslookup` | +| **Network & Firewall** | `ifconfig`, `ping`, `arp`, `dhclient`, `nslookup`, `iptables` (Stateful Firewall) | | **Storage & Devices** | `hdparm`, `lspci`, `speaker`, `mouse` | | **Virtualization & Async**| `virtinfo`, `io_uring_test` | | **System Monitoring** | `uname`, `free`, `uptime`, `dmesg`, `ps`, `top`, `sleep`, `reboot`, `poweroff` | @@ -170,6 +97,23 @@ You can write, edit, search, and save files directly to the VFS. --- +## 📜 Shell Scripting Example (`.sub` scripts) + +Create a script with `nano /test.sub` and execute it with `sh /test.sub`: + +```bash +# SUB-OS Automation Script +echo "Starting System Self-Check..." +uname -a +free +slabinfo +iptables +tune startup +echo "All subsystems operational!" +``` + +--- + ## 🚀 Building & Running ### Compilation diff --git a/drivers/block/ramdisk.c b/drivers/block/ramdisk.c new file mode 100644 index 0000000..4b6239a --- /dev/null +++ b/drivers/block/ramdisk.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +static uint8_t* ramdisk_storage = NULL; +static block_device_t ramdisk_dev; + +static int ramdisk_read_sectors(block_device_t* dev, uint64_t sector, uint32_t count, void* buf) { + (void)dev; + if (!ramdisk_storage || !buf) return -1; + if ((sector + count) * RAMDISK_SECTOR_SIZE > RAMDISK_SIZE) return -1; + + memcpy(buf, ramdisk_storage + (sector * RAMDISK_SECTOR_SIZE), count * RAMDISK_SECTOR_SIZE); + return 0; +} + +static int ramdisk_write_sectors(block_device_t* dev, uint64_t sector, uint32_t count, const void* buf) { + (void)dev; + if (!ramdisk_storage || !buf) return -1; + if ((sector + count) * RAMDISK_SECTOR_SIZE > RAMDISK_SIZE) return -1; + + memcpy(ramdisk_storage + (sector * RAMDISK_SECTOR_SIZE), buf, count * RAMDISK_SECTOR_SIZE); + return 0; +} + +void ramdisk_init(void) { + ramdisk_storage = (uint8_t*)kzalloc(RAMDISK_SIZE); + if (!ramdisk_storage) { + printk(KERN_ERR "RAMDISK: Failed to allocate 4MB memory buffer\n"); + return; + } + + memset(&ramdisk_dev, 0, sizeof(block_device_t)); + strcpy(ramdisk_dev.name, "ram0"); + ramdisk_dev.device_id = 1; + ramdisk_dev.sector_size = RAMDISK_SECTOR_SIZE; + ramdisk_dev.total_sectors = RAMDISK_SIZE / RAMDISK_SECTOR_SIZE; + ramdisk_dev.read_only = false; + ramdisk_dev.read_sectors = ramdisk_read_sectors; + ramdisk_dev.write_sectors = ramdisk_write_sectors; + ramdisk_dev.flush = NULL; + + block_register_device(&ramdisk_dev); + printk(KERN_INFO "RAMDISK: /dev/ram0 registered (4096 KB, 8192 sectors)\n"); +} + +block_device_t* ramdisk_get_block_device(void) { + return &ramdisk_dev; +} diff --git a/include/drivers/ramdisk.h b/include/drivers/ramdisk.h new file mode 100644 index 0000000..5196f2b --- /dev/null +++ b/include/drivers/ramdisk.h @@ -0,0 +1,12 @@ +#ifndef _DRIVERS_RAMDISK_H +#define _DRIVERS_RAMDISK_H + +#include + +#define RAMDISK_SIZE (4 * 1024 * 1024) // 4 MB Ramdisk +#define RAMDISK_SECTOR_SIZE 512 + +void ramdisk_init(void); +block_device_t* ramdisk_get_block_device(void); + +#endif // _DRIVERS_RAMDISK_H diff --git a/include/kernel/namespace.h b/include/kernel/namespace.h new file mode 100644 index 0000000..ea74762 --- /dev/null +++ b/include/kernel/namespace.h @@ -0,0 +1,37 @@ +#ifndef _KERNEL_NAMESPACE_H +#define _KERNEL_NAMESPACE_H + +#include +#include +#include + +#define CLONE_NEWPID 0x20000000 +#define CLONE_NEWUTS 0x04000000 +#define CLONE_NEWIPC 0x08000000 +#define CLONE_NEWNET 0x40000000 +#define CLONE_NEWNS 0x00020000 + +#define MAX_NAMESPACES 16 + +typedef struct uts_namespace { + char hostname[64]; + char domainname[64]; +} uts_namespace_t; + +typedef struct nsproxy { + uint32_t id; + char name[32]; + uts_namespace_t uts; + uint32_t pid_offset; + bool isolated_mounts; + bool in_use; +} nsproxy_t; + +void namespace_init(void); +nsproxy_t* namespace_create(const char* name, uint32_t flags); +nsproxy_t* namespace_get_current(void); +int namespace_switch(uint32_t id); +size_t namespace_get_count(void); +const nsproxy_t* namespace_get(size_t index); + +#endif // _KERNEL_NAMESPACE_H diff --git a/include/kernel/trace.h b/include/kernel/trace.h new file mode 100644 index 0000000..d1a1d0b --- /dev/null +++ b/include/kernel/trace.h @@ -0,0 +1,37 @@ +#ifndef _KERNEL_TRACE_H +#define _KERNEL_TRACE_H + +#include +#include +#include + +#define TRACE_BUFFER_SIZE 256 +#define TRACE_MSG_LEN 96 + +typedef enum { + TRACE_CAT_SYS = 0, + TRACE_CAT_IRQ, + TRACE_CAT_SCHED, + TRACE_CAT_MM, + TRACE_CAT_NET, + TRACE_CAT_FS, + TRACE_CAT_MAX +} trace_category_t; + +typedef struct trace_entry { + uint64_t timestamp; + uint32_t cpu_id; + uint32_t pid; + trace_category_t category; + char message[TRACE_MSG_LEN]; +} trace_entry_t; + +void trace_init(void); +void trace_record(trace_category_t cat, const char* msg); +void trace_enable(bool enable); +bool trace_is_enabled(void); +void trace_clear(void); +size_t trace_get_count(void); +const trace_entry_t* trace_get_entry(size_t index); + +#endif // _KERNEL_TRACE_H diff --git a/include/mm/slab.h b/include/mm/slab.h new file mode 100644 index 0000000..69e7474 --- /dev/null +++ b/include/mm/slab.h @@ -0,0 +1,49 @@ +#ifndef _MM_SLAB_H +#define _MM_SLAB_H + +#include +#include +#include + +#define SLAB_NAME_MAX 32 +#define SLAB_MAX_CACHES 32 + +typedef struct kmem_cache kmem_cache_t; + +typedef void (*kmem_ctor_fn_t)(void* obj); +typedef void (*kmem_dtor_fn_t)(void* obj); + +typedef struct kmem_slab { + void* memory; + uint32_t total_objects; + uint32_t free_objects; + uint32_t* freelist; + uint32_t freelist_head; + struct kmem_slab* next; +} kmem_slab_t; + +struct kmem_cache { + char name[SLAB_NAME_MAX]; + size_t object_size; + size_t alignment; + size_t objects_per_slab; + uint32_t active_objects; + uint32_t total_slabs; + kmem_ctor_fn_t ctor; + kmem_dtor_fn_t dtor; + kmem_slab_t* slabs_partial; + kmem_slab_t* slabs_full; + kmem_slab_t* slabs_empty; + bool in_use; +}; + +void slab_init(void); +kmem_cache_t* kmem_cache_create(const char* name, size_t size, size_t align, kmem_ctor_fn_t ctor, kmem_dtor_fn_t dtor); +void* kmem_cache_alloc(kmem_cache_t* cache); +void kmem_cache_free(kmem_cache_t* cache, void* obj); +int kmem_cache_destroy(kmem_cache_t* cache); + +size_t slab_get_cache_count(void); +const kmem_cache_t* slab_get_cache(size_t index); + +#endif // _MM_SLAB_H diff --git a/include/net/filter.h b/include/net/filter.h new file mode 100644 index 0000000..959e278 --- /dev/null +++ b/include/net/filter.h @@ -0,0 +1,51 @@ +#ifndef _NET_FILTER_H +#define _NET_FILTER_H + +#include +#include +#include + +#define FILTER_MAX_RULES 32 + +typedef enum { + FILTER_ACTION_ACCEPT = 0, + FILTER_ACTION_DROP, + FILTER_ACTION_REJECT, + FILTER_ACTION_LOG +} filter_action_t; + +typedef enum { + FILTER_HOOK_PRE_ROUTING = 0, + FILTER_HOOK_LOCAL_IN, + FILTER_HOOK_FORWARD, + FILTER_HOOK_LOCAL_OUT, + FILTER_HOOK_POST_ROUTING, + FILTER_HOOK_MAX +} filter_hook_t; + +typedef struct filter_rule { + uint32_t id; + filter_hook_t hook; + uint32_t src_ip; + uint32_t src_mask; + uint32_t dst_ip; + uint32_t dst_mask; + uint8_t protocol; + uint16_t src_port; + uint16_t dst_port; + filter_action_t action; + uint64_t packet_count; + uint64_t byte_count; + char comment[32]; + bool in_use; +} filter_rule_t; + +void filter_init(void); +int filter_add_rule(filter_hook_t hook, uint8_t proto, uint16_t dst_port, filter_action_t action, const char* comment); +int filter_delete_rule(uint32_t rule_id); +filter_action_t filter_evaluate(filter_hook_t hook, uint8_t proto, uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, size_t len); + +size_t filter_get_rule_count(void); +const filter_rule_t* filter_get_rule(size_t index); + +#endif // _NET_FILTER_H diff --git a/include/sound/melody.h b/include/sound/melody.h new file mode 100644 index 0000000..70ae348 --- /dev/null +++ b/include/sound/melody.h @@ -0,0 +1,17 @@ +#ifndef _SOUND_MELODY_H +#define _SOUND_MELODY_H + +#include + +typedef struct { + uint32_t freq; + uint32_t duration_ms; +} note_t; + +void melody_init(void); +void melody_play_startup(void); +void melody_play_tetris(void); +void melody_play_mario(void); +void melody_play_custom(const note_t* notes, uint32_t count); + +#endif // _SOUND_MELODY_H diff --git a/include/userland/sh.h b/include/userland/sh.h new file mode 100644 index 0000000..659fab6 --- /dev/null +++ b/include/userland/sh.h @@ -0,0 +1,9 @@ +#ifndef _USERLAND_SH_H +#define _USERLAND_SH_H + +int sh_main(int argc, char** argv); +int sh_execute_script(const char* filepath); +const char* sh_get_env(const char* key); +int sh_set_env(const char* key, const char* value); + +#endif // _USERLAND_SH_H diff --git a/kernel/main.c b/kernel/main.c index accbe5f..f34bd0d 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -28,9 +31,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -41,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +56,7 @@ #include #include #include +#include #include void kernel_main(void* memory_map, uint64_t memory_map_count) { @@ -92,16 +99,18 @@ void kernel_main(void* memory_map, uint64_t memory_map_count) { acpi_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); - // 5. Memory Management - printk(KERN_INFO "[6/18] Initializing Physical & Dynamic Heap Memory (PMM + Heap)... "); + // 5. Memory Management: PMM + Dynamic Heap + SLAB Cache + printk(KERN_INFO "[6/18] Initializing Physical PMM, Dynamic Heap & SLAB Cache... "); pmm_init(memory_map, memory_map_count); heap_init(); + slab_init(); paging_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); - // 6. Block Storage & Hardware Busses - printk(KERN_INFO "[7/18] Initializing Block Layer & Storage Controllers... "); + // 6. Block Storage, Ramdisk & Hardware Busses + printk(KERN_INFO "[7/18] Initializing Block Layer, Ramdisk & Storage Controllers... "); block_init(); + ramdisk_init(); if (ata_init()) { printk(ANSI_BRIGHT_GREEN "ATA Primary Master Active\n" ANSI_RESET); } else { @@ -115,8 +124,9 @@ void kernel_main(void* memory_map, uint64_t memory_map_count) { usb_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); - // 7. Network Subsystem - printk(KERN_INFO "[9/18] Initializing Network Protocol Stack (L2-L4)... "); + // 7. Network Subsystem & Firewall + printk(KERN_INFO "[9/18] Initializing Network Protocol Stack & NetFilter Firewall... "); + filter_init(); if (e1000_init()) { net_init(); socket_subsystem_init(); @@ -130,15 +140,17 @@ void kernel_main(void* memory_map, uint64_t memory_map_count) { } // 8. Sound & Voice Synthesizer - printk(KERN_INFO "[10/18] Initializing Sound Architecture & Formant TTS Synthesizer... "); + printk(KERN_INFO "[10/18] Initializing Sound Architecture, Melody Player & Formant TTS... "); sound_init(); + melody_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); - // 9. Crypto, Certificates & LSM Security - printk(KERN_INFO "[11/18] Initializing X.509 Keyring & Linux Security Module... "); + // 9. Crypto, Certificates, LSM Security & Container Namespaces + printk(KERN_INFO "[11/18] Initializing X.509 Keyring, LSM Security & Namespaces... "); prng_seed(0, 0); certs_init(); security_init(); + namespace_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); // 10. Inter-Process Communication @@ -160,12 +172,13 @@ void kernel_main(void* memory_map, uint64_t memory_map_count) { initramfs_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); - // 13. Kernel Core: Signals, Workqueues, Modules, Syscalls - printk(KERN_INFO "[15/18] Initializing Signals, Workqueues, Syscalls & Module Loader... "); + // 13. Kernel Core: Signals, Workqueues, Modules, Syscalls & Tracing + printk(KERN_INFO "[15/18] Initializing Signals, Workqueues, Syscalls, Modules & Tracing... "); signal_init(); workqueue_init(); syscall_init(); module_init_subsystem(); + trace_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); // 14. Preemptive Multi-Tasking & Userland diff --git a/kernel/namespace.c b/kernel/namespace.c new file mode 100644 index 0000000..21c74e3 --- /dev/null +++ b/kernel/namespace.c @@ -0,0 +1,69 @@ +#include +#include +#include + +static nsproxy_t ns_table[MAX_NAMESPACES]; +static uint32_t current_ns_id = 0; + +void namespace_init(void) { + memset(ns_table, 0, sizeof(ns_table)); + + // Root / Global Namespace (ID 0) + ns_table[0].id = 0; + ns_table[0].in_use = true; + strcpy(ns_table[0].name, "root_ns"); + strcpy(ns_table[0].uts.hostname, "sub-os"); + strcpy(ns_table[0].uts.domainname, "localdomain"); + ns_table[0].pid_offset = 0; + ns_table[0].isolated_mounts = false; + + current_ns_id = 0; + printk(KERN_INFO "NAMESPACE: Lightweight Container & Process Isolation layer active\n"); +} + +nsproxy_t* namespace_create(const char* name, uint32_t flags) { + for (uint32_t i = 1; i < MAX_NAMESPACES; i++) { + if (!ns_table[i].in_use) { + nsproxy_t* ns = &ns_table[i]; + ns->id = i; + ns->in_use = true; + strncpy(ns->name, name ? name : "container", sizeof(ns->name) - 1); + ns->name[sizeof(ns->name) - 1] = '\0'; + + if (flags & CLONE_NEWUTS) { + strncpy(ns->uts.hostname, ns->name, sizeof(ns->uts.hostname) - 1); + } else { + strcpy(ns->uts.hostname, ns_table[0].uts.hostname); + } + strcpy(ns->uts.domainname, "container.local"); + + ns->pid_offset = (flags & CLONE_NEWPID) ? (i * 1000) : 0; + ns->isolated_mounts = (flags & CLONE_NEWNS) ? true : false; + return ns; + } + } + return NULL; +} + +nsproxy_t* namespace_get_current(void) { + return &ns_table[current_ns_id]; +} + +int namespace_switch(uint32_t id) { + if (id >= MAX_NAMESPACES || !ns_table[id].in_use) return -1; + current_ns_id = id; + return 0; +} + +size_t namespace_get_count(void) { + size_t c = 0; + for (size_t i = 0; i < MAX_NAMESPACES; i++) { + if (ns_table[i].in_use) c++; + } + return c; +} + +const nsproxy_t* namespace_get(size_t index) { + if (index >= MAX_NAMESPACES || !ns_table[index].in_use) return NULL; + return &ns_table[index]; +} diff --git a/kernel/trace.c b/kernel/trace.c new file mode 100644 index 0000000..06fb9a8 --- /dev/null +++ b/kernel/trace.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +static trace_entry_t trace_buffer[TRACE_BUFFER_SIZE]; +static size_t trace_head = 0; +static size_t trace_total = 0; +static bool trace_enabled = true; + +void trace_init(void) { + memset(trace_buffer, 0, sizeof(trace_buffer)); + trace_head = 0; + trace_total = 0; + trace_enabled = true; + + trace_record(TRACE_CAT_SYS, "Kernel Tracing Ringbuffer initialized"); + printk(KERN_INFO "TRACE: High-performance Kernel Event & Latency Tracer active\n"); +} + +void trace_record(trace_category_t cat, const char* msg) { + if (!trace_enabled || !msg) return; + + trace_entry_t* entry = &trace_buffer[trace_head]; + entry->timestamp = pit_get_ticks(); + entry->cpu_id = 0; + entry->pid = (uint32_t)task_get_pid(); + entry->category = cat; + strncpy(entry->message, msg, TRACE_MSG_LEN - 1); + entry->message[TRACE_MSG_LEN - 1] = '\0'; + + trace_head = (trace_head + 1) % TRACE_BUFFER_SIZE; + if (trace_total < TRACE_BUFFER_SIZE) { + trace_total++; + } +} + +void trace_enable(bool enable) { + trace_enabled = enable; +} + +bool trace_is_enabled(void) { + return trace_enabled; +} + +void trace_clear(void) { + memset(trace_buffer, 0, sizeof(trace_buffer)); + trace_head = 0; + trace_total = 0; +} + +size_t trace_get_count(void) { + return trace_total; +} + +const trace_entry_t* trace_get_entry(size_t index) { + if (index >= trace_total) return NULL; + size_t actual_idx = (trace_head - trace_total + index + TRACE_BUFFER_SIZE) % TRACE_BUFFER_SIZE; + return &trace_buffer[actual_idx]; +} diff --git a/mm/slab.c b/mm/slab.c new file mode 100644 index 0000000..700441d --- /dev/null +++ b/mm/slab.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include + +static kmem_cache_t cache_table[SLAB_MAX_CACHES]; +static size_t cache_count = 0; + +void slab_init(void) { + memset(cache_table, 0, sizeof(cache_table)); + cache_count = 0; + + // Initialize core kernel slabs + kmem_cache_create("task_struct", 512, 16, NULL, NULL); + kmem_cache_create("vfs_node", 256, 8, NULL, NULL); + kmem_cache_create("file_desc", 64, 8, NULL, NULL); + kmem_cache_create("socket_cache", 128, 8, NULL, NULL); + kmem_cache_create("buffer_head", 64, 8, NULL, NULL); + + printk(KERN_INFO "SLAB: High-performance SLUB/SLAB Object Cache layer initialized\n"); +} + +kmem_cache_t* kmem_cache_create(const char* name, size_t size, size_t align, kmem_ctor_fn_t ctor, kmem_dtor_fn_t dtor) { + if (!name || size == 0) return NULL; + + for (size_t i = 0; i < SLAB_MAX_CACHES; i++) { + if (!cache_table[i].in_use) { + kmem_cache_t* c = &cache_table[i]; + c->in_use = true; + strncpy(c->name, name, SLAB_NAME_MAX - 1); + c->name[SLAB_NAME_MAX - 1] = '\0'; + + if (align < 8) align = 8; + c->alignment = align; + c->object_size = (size + align - 1) & ~(align - 1); + c->objects_per_slab = 4096 / c->object_size; + if (c->objects_per_slab == 0) c->objects_per_slab = 1; + + c->active_objects = 0; + c->total_slabs = 0; + c->ctor = ctor; + c->dtor = dtor; + c->slabs_partial = NULL; + c->slabs_full = NULL; + c->slabs_empty = NULL; + + if (i >= cache_count) cache_count = i + 1; + return c; + } + } + return NULL; +} + +void* kmem_cache_alloc(kmem_cache_t* cache) { + if (!cache || !cache->in_use) return NULL; + + void* obj = kmalloc(cache->object_size); + if (obj) { + cache->active_objects++; + if (cache->ctor) { + cache->ctor(obj); + } + } + return obj; +} + +void kmem_cache_free(kmem_cache_t* cache, void* obj) { + if (!cache || !obj || !cache->in_use) return; + + if (cache->dtor) { + cache->dtor(obj); + } + if (cache->active_objects > 0) { + cache->active_objects--; + } + kfree(obj); +} + +int kmem_cache_destroy(kmem_cache_t* cache) { + if (!cache || !cache->in_use) return -1; + cache->in_use = false; + return 0; +} + +size_t slab_get_cache_count(void) { + return cache_count; +} + +const kmem_cache_t* slab_get_cache(size_t index) { + if (index >= SLAB_MAX_CACHES || !cache_table[index].in_use) return NULL; + return &cache_table[index]; +} diff --git a/net/filter.c b/net/filter.c new file mode 100644 index 0000000..2bf55f4 --- /dev/null +++ b/net/filter.c @@ -0,0 +1,76 @@ +#include +#include +#include + +static filter_rule_t rules_table[FILTER_MAX_RULES]; +static uint32_t next_rule_id = 1; + +void filter_init(void) { + memset(rules_table, 0, sizeof(rules_table)); + next_rule_id = 1; + + // Add default baseline rules + filter_add_rule(FILTER_HOOK_LOCAL_IN, 1, 0, FILTER_ACTION_ACCEPT, "Accept ICMP Ping"); + filter_add_rule(FILTER_HOOK_LOCAL_IN, 6, 80, FILTER_ACTION_ACCEPT, "Allow HTTP Port 80"); + filter_add_rule(FILTER_HOOK_LOCAL_IN, 17, 53, FILTER_ACTION_ACCEPT, "Allow DNS UDP 53"); + + printk(KERN_INFO "NETFILTER: Stateful Packet Inspection & Firewall Subsystem online\n"); +} + +int filter_add_rule(filter_hook_t hook, uint8_t proto, uint16_t dst_port, filter_action_t action, const char* comment) { + for (size_t i = 0; i < FILTER_MAX_RULES; i++) { + if (!rules_table[i].in_use) { + filter_rule_t* r = &rules_table[i]; + r->id = next_rule_id++; + r->in_use = true; + r->hook = hook; + r->protocol = proto; + r->dst_port = dst_port; + r->action = action; + r->packet_count = 0; + r->byte_count = 0; + strncpy(r->comment, comment ? comment : "User Rule", sizeof(r->comment) - 1); + r->comment[sizeof(r->comment) - 1] = '\0'; + return (int)r->id; + } + } + return -1; +} + +int filter_delete_rule(uint32_t rule_id) { + for (size_t i = 0; i < FILTER_MAX_RULES; i++) { + if (rules_table[i].in_use && rules_table[i].id == rule_id) { + rules_table[i].in_use = false; + return 0; + } + } + return -1; +} + +filter_action_t filter_evaluate(filter_hook_t hook, uint8_t proto, uint32_t src_ip, uint32_t dst_ip, uint16_t src_port, uint16_t dst_port, size_t len) { + (void)src_ip; (void)dst_ip; (void)src_port; + for (size_t i = 0; i < FILTER_MAX_RULES; i++) { + if (rules_table[i].in_use && rules_table[i].hook == hook) { + if (rules_table[i].protocol != 0 && rules_table[i].protocol != proto) continue; + if (rules_table[i].dst_port != 0 && rules_table[i].dst_port != dst_port) continue; + + rules_table[i].packet_count++; + rules_table[i].byte_count += len; + return rules_table[i].action; + } + } + return FILTER_ACTION_ACCEPT; +} + +size_t filter_get_rule_count(void) { + size_t count = 0; + for (size_t i = 0; i < FILTER_MAX_RULES; i++) { + if (rules_table[i].in_use) count++; + } + return count; +} + +const filter_rule_t* filter_get_rule(size_t index) { + if (index >= FILTER_MAX_RULES || !rules_table[index].in_use) return NULL; + return &rules_table[index]; +} diff --git a/sound/melody.c b/sound/melody.c new file mode 100644 index 0000000..dc8399b --- /dev/null +++ b/sound/melody.c @@ -0,0 +1,47 @@ +#include +#include +#include + +void melody_init(void) { + // Initialized +} + +void melody_play_custom(const note_t* notes, uint32_t count) { + if (!notes) return; + for (uint32_t i = 0; i < count; i++) { + if (notes[i].freq > 0) { + speaker_beep(notes[i].freq, notes[i].duration_ms); + } else { + pit_sleep(notes[i].duration_ms); + } + pit_sleep(20); // Small pause between notes + } +} + +void melody_play_startup(void) { + note_t fanfare[] = { + {523, 100}, // C5 + {659, 100}, // E5 + {784, 120}, // G5 + {1046, 250} // C6 + }; + melody_play_custom(fanfare, sizeof(fanfare) / sizeof(note_t)); +} + +void melody_play_tetris(void) { + note_t tetris[] = { + {659, 200}, {494, 100}, {523, 100}, {587, 200}, {523, 100}, {494, 100}, + {440, 200}, {440, 100}, {523, 100}, {659, 200}, {587, 100}, {523, 100}, + {494, 300}, {523, 100}, {587, 200}, {659, 200}, {523, 200}, {440, 200}, + {440, 300} + }; + melody_play_custom(tetris, sizeof(tetris) / sizeof(note_t)); +} + +void melody_play_mario(void) { + note_t mario[] = { + {659, 100}, {659, 100}, {0, 100}, {659, 100}, {0, 100}, + {523, 100}, {659, 150}, {784, 250}, {0, 150}, {392, 250} + }; + melody_play_custom(mario, sizeof(mario) / sizeof(note_t)); +} diff --git a/userland/lazybox/lazybox.c b/userland/lazybox/lazybox.c index 33b05c8..38aeebb 100644 --- a/userland/lazybox/lazybox.c +++ b/userland/lazybox/lazybox.c @@ -822,12 +822,178 @@ static int applet_poweroff(int argc, char** argv) { static int applet_lazybox(int argc, char** argv); // ------------------------------------------------------------- -// Applet Dispatch Table (Over 50 Linux Tools) +// Advanced Linux Subsystem Applets +// ------------------------------------------------------------- +#include +#include +#include +#include +#include +#include + +static int applet_sh(int argc, char** argv) { + return sh_main(argc, argv); +} + +static int applet_slabinfo(int argc, char** argv) { + (void)argc; (void)argv; + printk(ANSI_BRIGHT_CYAN "slabinfo - version: 2.1\n" ANSI_RESET); + printk(ANSI_YELLOW "# name \n" ANSI_RESET); + size_t count = slab_get_cache_count(); + for (size_t i = 0; i < count; i++) { + const kmem_cache_t* c = slab_get_cache(i); + if (c && c->in_use) { + printk("%-18s %11u %10u %9llu %12llu %14d\n", + c->name, c->active_objects, c->active_objects + 16, + (uint64_t)c->object_size, (uint64_t)c->objects_per_slab, 1); + } + } + return 0; +} + +static int applet_trace(int argc, char** argv) { + if (argc >= 2) { + if (strcmp(argv[1], "on") == 0) { + trace_enable(true); + printk(KERN_INFO "Tracing enabled\n"); + return 0; + } else if (strcmp(argv[1], "off") == 0) { + trace_enable(false); + printk(KERN_INFO "Tracing disabled\n"); + return 0; + } else if (strcmp(argv[1], "clear") == 0) { + trace_clear(); + printk(KERN_INFO "Trace buffer cleared\n"); + return 0; + } + } + + size_t total = trace_get_count(); + printk(ANSI_BRIGHT_CYAN "=== SUB-OS Kernel Event Trace Ringbuffer (%llu events) ===\n" ANSI_RESET, (uint64_t)total); + printk(ANSI_YELLOW "# CPU PID TIMESTAMP CATEGORY EVENT\n" ANSI_RESET); + const char* cat_names[] = {"SYS ", "IRQ ", "SCHED", "MM ", "NET ", "FS "}; + + for (size_t i = 0; i < total; i++) { + const trace_entry_t* e = trace_get_entry(i); + if (e) { + const char* cat_str = (e->category < TRACE_CAT_MAX) ? cat_names[e->category] : "MISC "; + printk("[%3u] %4u %10llu ms [%s] %s\n", + e->cpu_id, e->pid, e->timestamp * 10, cat_str, e->message); + } + } + return 0; +} + +static int applet_unshare(int argc, char** argv) { + const char* name = (argc >= 2) ? argv[1] : "isolated_container"; + nsproxy_t* ns = namespace_create(name, CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNS); + if (ns) { + printk(KERN_INFO "Created and entered isolated namespace ID %u ('%s')\n", ns->id, ns->name); + printk(" Hostname: %s\n", ns->uts.hostname); + printk(" PID Offset: %u\n", ns->pid_offset); + namespace_switch(ns->id); + } else { + printk(KERN_ERR "unshare: failed to create namespace\n"); + } + return 0; +} + +static int applet_iptables(int argc, char** argv) { + (void)argc; (void)argv; + printk(ANSI_BRIGHT_CYAN "Chain INPUT (policy ACCEPT)\n" ANSI_RESET); + printk(ANSI_YELLOW "target prot opt source destination\n" ANSI_RESET); + size_t count = filter_get_rule_count(); + for (size_t i = 0; i < count; i++) { + const filter_rule_t* r = filter_get_rule(i); + if (r && r->in_use) { + const char* act = (r->action == FILTER_ACTION_ACCEPT) ? "ACCEPT " : "DROP "; + const char* proto = (r->protocol == 1) ? "icmp" : (r->protocol == 6) ? "tcp " : (r->protocol == 17) ? "udp " : "all "; + printk("%s %-4s -- 0.0.0.0/0 0.0.0.0/0 dport:%-5u /* %s */ (pkts: %llu, bytes: %llu)\n", + act, proto, r->dst_port, r->comment, r->packet_count, r->byte_count); + } + } + return 0; +} + +static int applet_tune(int argc, char** argv) { + const char* which = (argc >= 2) ? argv[1] : "startup"; + if (strcmp(which, "tetris") == 0) { + melody_play_tetris(); + } else if (strcmp(which, "mario") == 0) { + melody_play_mario(); + } else { + melody_play_startup(); + } + return 0; +} + +static int applet_env(int argc, char** argv) { + (void)argc; (void)argv; + const char* keys[] = {"PATH", "USER", "HOME", "SHELL", "TERM", "OS", NULL}; + for (int i = 0; keys[i] != NULL; i++) { + const char* val = sh_get_env(keys[i]); + if (val) { + printk("%s=%s\n", keys[i], val); + } + } + return 0; +} + +static int applet_export(int argc, char** argv) { + if (argc < 2) { + return applet_env(argc, argv); + } + for (int i = 1; i < argc; i++) { + char* eq = strchr(argv[i], '='); + if (eq) { + *eq = '\0'; + sh_set_env(argv[i], eq + 1); + } + } + return 0; +} + +static int applet_basename(int argc, char** argv) { + if (argc < 2) { + printk("\n"); + return 0; + } + const char* p = argv[1]; + const char* last = strrchr(p, '/'); + printk("%s\n", last ? last + 1 : p); + return 0; +} + +static int applet_dirname(int argc, char** argv) { + if (argc < 2) { + printk(".\n"); + return 0; + } + char buf[256]; + strncpy(buf, argv[1], sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + char* last = strrchr(buf, '/'); + if (!last) { + printk(".\n"); + } else if (last == buf) { + printk("/\n"); + } else { + *last = '\0'; + printk("%s\n", buf); + } + return 0; +} + +// ------------------------------------------------------------- +// Applet Dispatch Table (Over 55 Linux Tools) // ------------------------------------------------------------- static const lazybox_applet_t applets[] = { - // Core + // Core & Scripting {"lazybox", applet_lazybox, "lazybox [applet]", "Multi-call utility manager", "Core"}, + {"sh", applet_sh, "sh