I was given an assignment to write a mini-shell:To write your own shell, you will need to start with a C program that  will prompt the user for input and accept in a number of arguments  from a command line. The commands entered will be accepted into your  shell and then processed to understand if it is a built in command or  something that needs to be executed via fork/exec (NOTE: No use of  system function). Your shell should emulate the standard shells in  how it deals with background commands ( & ). The interaction with your  shell should be just like the standard shells. What I mean by this is  to have a good usage statement returned if the arguments passed to the  shell are not correct, and when there is an error you should send back  a useful error, and not exit the shell, just continue.Namespaces allow for virtualization and sharing of spaces between  parent and child processes. This is a part of the Linux operating  system since 2008 that allows for the creation of different models to  create containers for software applications. The most popular version  of Linux namespaces is Docker. Your task for this lab  is to create the option inside your shell through built in commands  to move your shell into a container. The options for different  containers can be added together in a clone or clone2 call. Here  are some of the options:CLONE_NEWIPC   - New namespace for IPCCLONE_NEWUTS   - Setup new hostname and domain CLONE_NEWUSER  - User and group changes CLONE_NEWNET   - New network namespace CLONE_NEWNS    - New mount namespaceWhen using a clone function, you will have the ability to run  another function. To test your clone call, you will need to be able  to demonstrate the change, and the best way to do that is to spawn  another shell to look around at what changed. The best way to do  this is to spawn another shell using the system function call.Here is my code implementing the above.  Basic testing has been performed and it seems to work as intended:#include <stdbool.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <sys/types.h>#include <sys/wait.h>#define _GNU_SOURCE#include <linux/sched.h>#ifndef streq#define streq(x, y) (strcmp((x), (y)) == 0)#endif#define MAX_COMMAND_LENGTH 100#define MAX_PARAMS 10typedef struct input{    char** params;    int numParams;    bool inBackground;} Command;// split input into array of parametersCommand* parseCmd(char* input){    // handle bad input    if (!input || strlen(input) == 0)        return NULL;    // allocate space on heap for command struct, and parameter list    Command *cmd = calloc(1, sizeof(Command));    cmd->params = calloc(MAX_PARAMS + 1, sizeof(char*));    // setup strtok_r    char *next = NULL;    char *part = strtok_r(input,  , &next);    // do-while so we can process first `part`, then rest of `input`    do    {        // check for background, if not found default setting is false        if (streq(part, &))        {            cmd->inBackground = true;            // don't store in parameter list for commands            continue;        }        cmd->params[cmd->numParams] = part;        ++cmd->numParams;    }    while ((part = strtok_r(NULL,  , &next)));    return cmd;}void freeCmd(Command* cmd){    free(cmd->params);    cmd->params = NULL;    free(cmd);    cmd = NULL;}int childFunct(void){    system(sh);    return 0;}int executeClone(Command *cmd){    const int STACK_SIZE = 1024;    unsigned long flags = 0;    if (cmd->numParams <= 1)    {        fprintf(stderr, clone command requires arguments\n);        return 0; // non-fatal error    }    if (streq(cmd->params[1], ipc))        flags |= CLONE_NEWIPC;    if (streq(cmd->params[1], uts))        flags |= CLONE_NEWUTS;    if (streq(cmd->params[1], user))        flags |= CLONE_NEWUSER;    if (streq(cmd->params[1], net))        flags |= CLONE_NEWNET;    if (streq(cmd->params[1], ns))        flags |= CLONE_NEWNS;    if (!flags)    {        fprintf(stderr, clone parameter not recognized\n);        return 0;  // non-fatal error    }    char *stack = malloc(STACK_SIZE);    pid_t pid = clone(childFunct, stack + STACK_SIZE, flags, NULL);    // error    if (pid == -1)    {        char* error = strerror(errno);        fprintf(stderr, clone: %s\n, error);    }    return 0;}int executeCmd(Command *cmd){    pid_t pid = fork();    // error    if (pid == -1)    {        char* error = strerror(errno);        fprintf(stderr, fork: %s\n, error);        return 1;    }    // child process    else if (pid == 0)    {        // will daemonize child if background        // if (cmd->inBackground)        //     setpgid(pid, 0);        // execute command        execvp(cmd->params[0], cmd->params);        // error occurred        char* error = strerror(errno);        fprintf(stderr, ERROR: %s: %s\n, cmd->params[0], error);        return 2;    }    // parent process    else    {        // wait for child process to finish        if (!cmd->inBackground)        {            int childStatus = 0;            waitpid(pid, &childStatus, 0);        }    }    return 0;}int main(void){    char input[MAX_COMMAND_LENGTH + 1] = ;    Command *cmd = NULL;    while(1)    {        // print command prompt        fputs(> , stdout);        // fgets returns NULL on Ctrl+D, so exit the loop on NULL        if (!fgets(input, sizeof(input), stdin))            break;        // remove trailing newline character, if any        if (input[strlen(input) - 1] == '\n')            input[strlen(input) - 1] = '\0';        // split cmd into array of parameters        cmd = parseCmd(input);        // handle bad command, continue running        if (!cmd)            continue;        // exit?        if (streq(cmd->params[0], exit) || streq(cmd->params[0], quit))            break;        // test for built-in command        if (streq(cmd->params[0], clone))        {            if (executeClone(cmd))            break;        }        else // executable        {            if (executeCmd(cmd))                break;        }        freeCmd(cmd);    }}Any suggestions for improvement?
mini-(Docker)-shell
c;homework;linux;shell;namespaces
Avoid a hacker exploit.  The null character is not special when fgets() reads it.  So if the first character read is the rare null character, then input[strlen(input) - 1] is UB.  strcspn() is a nice alternative.if (!fgets(input, sizeof(input), stdin)) break;//if (input[strlen(input) - 1] == '\n')//    input[strlen(input) - 1] = '\0';input[strcspn(input, \n)] = '\0';Memory leak in main().  Better to freeCmd(cmd) after the various breaks.        freeCmd(cmd);    }    // add     freeCmd(cmd);}Unclear why code is using long for flags.  linux.die.net implies the type should be int.  Check your prototype for clone() - also its return type.  Is it int or pid_t Ref?Although code nicely aligns, I'd expect an if()  else if() else  structure rather than if()  if()  if (streq(cmd->params[1], ipc))    flags |= CLONE_NEWIPC;else /* add */ if (streq(cmd->params[1], uts))    flags |= CLONE_NEWUTS;else /* add */ if (streq(cmd->params[1], user))    flags |= CLONE_NEWUSER;else {    fprintf(stderr, clone parameter not recognized\n);    return 0;  // non-fatal error}Why try to synchronize the type?  Instead reference the type.  Pedantic, avoid UB of signed overflow, use unsigned math with 1u.//                                      Is this the right type? // cmd->params = calloc(MAX_PARAMS + 1, sizeof(char*));//                                    Right size, regardless of type of *cmd->paramscmd->params = calloc(MAX_PARAMS + 1u, sizeof *(cmd->params));