KoizOS - Adding more shell commands

Introduction

KoizOS has a file system and memory management, but there's not an effective way to actually interact with the operating system. I put in two hastily coded test shell commands to test memory usage last post, but in this post, I'll expand the operating system shell to add a bit of interactivity to the operating system. Commands introduced will be mostly for reading and writing to the file system.

Implementation

The code for shell commands are pretty straight-forward: they consist of a function with a parameter for the number of arguments and an array of said arguments to the console command.

/* Structure for each command */
struct shell_cmd_entry {
    char* shell_cmd_name;
    int32_t (*shell_cmd_func)(int, char*[]);
};
typedef struct shell_cmd_entry shell_cmd_entry_t;

/* Array that holds all the kernel shell commands */
static shell_cmd_entry_t shell_cmds[] = {

    /* Filesystem commands */
    {"ls",              sh_ls},
    {"write",           sh_write},
    {"read",            sh_read},

    /* Memory commands */
    { "slabinfo",       sh_slabinfo },
    { "free",           sh_free}
    
};

Let's first take a look at the "free" command, which simply calls an internal function in "pmem.c" that lists the utilized memory.

#include "sh_free.h"

#include "../../drivers/memory/pmem.h"

int32_t sh_free(int argc, char* argv[])
{
    pmem_list_info();
    return 0;
}

Here one can see that the operating system is only using around 8mb of memory, which is to expected from an operating system that really doesn't do much of anything.

Now onto something a little more complicated: file management. Here's the shell functions for both reading from and writing to a file:

int32_t sh_write(int argc, char* argv[])
{
    /* argument check */
    if(argc < 3) {
        printf("write needs 2 arguments: filename and contents");
        return -1;
    }

    /* attempt to write the file to the filesystem */
    int file_size = strlen((uint8_t*)argv[2]);
    fs_file_write((uint8_t*)argv[1], argv[2], file_size);

    printf("Wrote file %s with %d bytes\n", argv[1], file_size);
    return 0;
}

int32_t sh_read(int argc, char* argv[])
{
    /* argument check */
    if(argc < 2) {
        printf("read needs 1 argument: filename");
        return -1;
    }

    uint8_t* file_buffer = (uint8_t*)malloc(4096);

    /* make sure the file exists */
    if(!fs_file_exists((uint8_t*)argv[1])) {
        printf("file does not exist\n");
        return -1;
    }

    /* attempt to write the read in the filesystem */
    fs_file_read((uint8_t*)argv[1], file_buffer, 0);
    printf("%s\n", file_buffer);

    free(file_buffer);
    return 0;
}

We can now read and write to the filesystem on our operating system.

Conclusion

Everything from the past few posts starts to come together as a user can freely interact with all the systems we built up from earlier posts. As cool as managing files and ram is, we're still missing a very important function that operating systems typically aim to fulfill: running applications. We'll explore exactly that in the next post.