0

I am trying to create a microshell. It reads commands in, parses this and splits this, then executes. To parse, first I separate by the delimiter || to get up to two commands if there is a pipe. The split each command into an array of strings.

I thought this is how execlp works, but it only runs the command even though the C string "cmd1" does contain the arguments. Can someone please help me understand how I am passing the parameters wrong to the execlp function?

shell.h

   /****************************************************************
   PROGRAM:   MicroShell(assignment 4)
   FILE:      shell.h

   AUTHOR:    Nick Schuck

   FUNCTION:  This contains the header for the shell class   
   ****************************************************************/
   #ifndef _shell_h
   #define _shell_h

   #include <sys/types.h>
   #include <unistd.h>  
   #include <cstdio>
   #include <pwd.h>
   #include <cstring>
   #include <sys/wait.h>
   #include <cstdlib>
   #include <vector>


   class Shell
   {
        private:
        char buffer[1024];
        const char *cmd1[10];
        const char *cmd2[10];

        public:
        Shell();  //default constructor
        void askForCommand(); 
        void readCommandLine();
        void parseBuffer();
        void invokeCommand();
        void executeOneCommand();
        void executeTwoCommands();
        };

        #endif 

shell.cc

     /***************************************************************
         PROGRAM:   MicroShell(assignment 4)
         FILE:      shell.c

   AUTHOR:    Nick Schuck

   FUNCTION:  This file contains the implementation of 
              class shell from file "shell.h"
****************************************************************/
#include "shell.h"
#include <iostream>


Shell::Shell()
{
    /**Get current user*/
    struct passwd *p = getpwuid(getuid());
    if (!p) //Error handling
        puts("Welcome to Nick Schuck's MicroShell, Anonymous");

    /**Welcome message for my shell*/
    printf("\n\nWelcome to Nick Schuck's Microshell, user %s!\n\n", p->pw_name);
}


void Shell::askForCommand()
{
    /**Command Prompt*/
    printf("myshell>");
}


void Shell::readCommandLine()
{
    /**Read stdin into buffer array IF no*/
    /**errors occur                      */
    if (fgets(this->buffer, 1024, stdin) != NULL)
    {
        this->buffer[strlen(this->buffer) - 1] = 0;
    }
}


void Shell::parseBuffer()
{
    /**Variables*/
    int i = 0, u = 0,
        t = 0;
    char *ptr;  
    char parsingBuffer[2][512];


    /**Parse buffer for multiple commands*/
    strcpy(parsingBuffer[0], strtok(this->buffer, "||"));
    while ((ptr = strtok(NULL, "||")) != NULL)
    {
        i++;
        strcpy(parsingBuffer[i], ptr);
    }


    //**Get first command*/
    this->cmd1[0] = strtok(parsingBuffer[0], " ");
    while ((ptr = strtok(NULL, " ")) != NULL)
    {
        u++;
        this->cmd1[u] = ptr;
        this->cmd1[u+1] = '\0';
    }

    //!!!TESTING TO SEE COMMAND ARE IN CMD1
    int b = 0;
    while(cmd1[b] != '\0')
    {
        std::cout << cmd1[b] << "\n";
        b++;
    }


    /**Get second command*/
    this->cmd2[0] = strtok(parsingBuffer[1], " ");
    while ((ptr = strtok(NULL, " ")) != NULL)
    {
        t++;
        this->cmd2[t] = ptr;
    }
}


void Shell::invokeCommand()
{
    if (this->cmd1[0] == NULL)
    {
        //do nothing
    }
    else if(this->cmd1[0] != NULL && this->cmd2[0] == NULL)
    {
        executeOneCommand();
    }
    else if(this->cmd1[0] != NULL && cmd2[0] !=NULL)
    {
        executeTwoCommands();
    }
}


void Shell::executeOneCommand()
{
    pid_t pid; //pid for fork
    int status;
    char args[512];

    if ((pid = fork()) < 0)
    {
        printf("fork error\n");
        exit(-1);
    }
    else if(pid == 0)  //Child Process
        {
            execlp(cmd1[0], *cmd1);     
        }
        else  //Parent Process
        {
            if ((pid = waitpid(pid, &status, 0)) < 0)
            {
                printf("waitpid error in main\n");
                exit(-1);
            }
        }

    }

main.cc

#include "shell.h"
#include <iostream>
#include <vector>

int main()
{
    const int BUFFER_SIZE = 1024;
    const int MAX_COMMANDS_IN_BUFFER = 2;

    /**Initialize a new shell object*/
    Shell shell;

    /**Print command prompt to screen*/
    shell.askForCommand();

    /**Read users command*/
    shell.readCommandLine();

    /**parse buffer to find individual*/
    /**commands                        */
    shell.parseBuffer();

    /**Invoke command*/
    shell.invokeCommand();
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278

1 Answers1

1

You can't use execlp() — you must use execvp().

execvp(cmd1[0], cmd1);

To use execlp(), you must know at compile time the fixed list of arguments for the command — you must be able to write:

execlp(cmd_name, arg0, arg1, …, argN, (char *)0);

Your call to execlp() is also faulty because you don't provide the (char *)0 argument to indicate the end of the argument list.

Your code also needs to handle exec*() returning, which means the command failed. Usually that means it should print an error message (that the command was not found, or permission denied, or whatever), and then exit with an appropriate non-zero error status.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • When i try Execvp(), it says cannot change from const char* to char *const. I understand pointers( as far as i know) but I do not understand how to get this to work. I have looked all over forums but when to do something that works for thhose questions, it never works. How would I write the arguments inside execvp() with my array of c strings "cmd1" –  Mar 24 '16 at 15:33
  • Also, thank you with the suggestion of handling null pointer error. Have not done to much in error handling while invoking command or executing, just looking to get it to work first. Do you know if I can call Execvp with the current format of "cmd1" –  Mar 24 '16 at 15:38
  • Remove the const-ness from the types in the class. They are modifiable strings, even if you won't ordinarily modify them. – Jonathan Leffler Mar 24 '16 at 15:38
  • Also ensure you've got a null pointer after the last valid argument in cmd1. – Jonathan Leffler Mar 24 '16 at 15:45
  • Thanks for the help. I guess I have been working on so much stuff my mind is jumbled right now. –  Mar 24 '16 at 15:51