0

I found the same question but there was no answer.

In building my own unix shell, my output redirection is working fine, but when I try the input it does not do anything. If you could help me figure out the problem that would be great. This is my exec function code:

void execute (char **args)
{
   int pid, status;
   pid = fork ();

   if (pid < 0) 
   {
      perror ("Error forking!");
      return;
   }


   else if (pid > 0) 
   {
      fflush(0);
      while (wait (&status) != pid)
         continue;
   }


   else if (pid == 0) 
   {

      int i,in=0,out=0;
      char input[BUF_SIZE],output[BUF_SIZE];

      for(i=0;args[i]!=NULL;i++)
      {
         if(strcmp(args[i],"<")==0)
         {        
            args[i]=NULL;
            strcpy(input,args[i+1]);
            in=2;           
         }               

         if(strcmp(args[i],">")==0)
         {      
            args[i]=NULL;
            strcpy(output,args[i+1]);
            out=2;
         }         
      }


      if(in)
      {   
         int fd0;
         if ((fd0 = open(input, O_RDONLY, 0)) < 0) 
         {
            perror("Couldn't open input file");
            exit(0);
         }           

         dup2(fd0, 0); 

         close(fd0); 
      }


      if (out)
      {
         int fd1;
         if ((fd1 = creat(output , 0644)) < 0) 
         {
            perror("Couldn't open the output file");
            exit(0);
         }           

         dup2(fd1, 1); 
         close(fd1);
      }

      execvp (*args, args);
      perror("execvp");
      _exit(1);
   }

Here is my whole code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#define ARGSIZE 20
#define BUF_SIZE 1024

void execute (char **args);
void cd (char *directory);
int killpid (char *pitstr, int sig);

int main (void)
{
    char line[BUF_SIZE] = {0};
    char *args[ARGSIZE] = {NULL};
    char *token;
    int i, argIndex = 0;

    while (1) 
    {

        argIndex = 0; 

        for (i = 0; i < ARGSIZE; i++)
            args[i] = NULL;

        printf ("shell> "); 

        if (fgets (line, BUF_SIZE, stdin) == NULL) 
        {
            printf ("EOF received\n");
            return 0;
        }

        if (*line == '\n') 
        continue;

        token = strtok (line, " \n"); 

        while (token != NULL) 
        {
            args[argIndex] = token;
            token = strtok (NULL, " \n");
            argIndex++;
        }

        if (!argIndex)
            continue;  

        if (strcmp (args[0], "quit") == 0 || strcmp (args[0], "exit") == 0)
            break;

        if ((strcmp (args[0], "cd") == 0))
            cd (args[1]);
        else if ((strcmp (args[0], "kill") == 0)) 
        {
            if (args[1])
                killpid (args[1], SIGTERM);
        }


        else
            execute (args);

    }
    return 0;
}

void execute (char **args)
{
    int pid, status;
    pid = fork ();

    if (pid < 0) 
    {
        perror ("Error forking!");
        return;
    }


    else if (pid > 0) 
    {
        fflush(0);
        while (wait (&status) != pid)
            continue;
    }


    else if (pid == 0) 
    {

        int i,in=0,out=0;
        char input[BUF_SIZE],output[BUF_SIZE];

        for(i=0;args[i]!=NULL;i++)
        {
            if(strcmp(args[i],"<")==0)
            {        
                args[i]=NULL;
                strcpy(input,args[i+1]);
                in=2;           
            }               

            if(strcmp(args[i],">")==0)
            {      
                args[i]=NULL;
                strcpy(output,args[i+1]);
                out=2;
            }         
        }


        if(in)
        {   
            int fd0;
            if ((fd0 = open(input, O_RDONLY, 0)) < 0) 
            {
                perror("Couldn't open input file");
                exit(0);
            }           

            dup2(fd0, 0); 
            close(fd0); 
        }


        if (out)
        {
            int fd1;
            if ((fd1 = creat(output , 0644)) < 0) 
            {
                perror("Couldn't open the output file");
                exit(0);
            }           

            dup2(fd1, 1); 
            close(fd1);
        }

        execvp (*args, args);
        perror("execvp");
        _exit(1);
        }

}

void cd (char *directory)
{
    char dir[BUF_SIZE] = {0};

    if (!directory) 
    {  
        directory = getenv ("HOME");

        if (chdir (directory))
            fprintf (stderr, "Failed to enter directory: %s\n", directory);
        else
            printf ("%s\n", directory);

        return;
    }

    if (*directory == '~') 
    { 
        strcpy (dir, getenv ("HOME"));
        strcat (dir, "/");
        strcat (dir, directory + 2);

        if (chdir (dir))
            fprintf (stderr, "Failed to enter directory: %s\n", dir);
        else
            printf ("%s\n", dir);

        return;
    }

    if (chdir (directory)) 
        fprintf (stderr, "Failed to enter directory: %s\n", directory);
    else
        printf ("%s\n", directory);
}

int killpid (char *pidstr, int sig)
{
    pid_t pid = (pid_t)atoi (pidstr);

    if (pid < 1) 
    {
        fprintf (stderr, "warning: requested pid < 1, ignoring\n");
        return (int)pid;
    }

    printf (" killing pid '%d' with signal '%d'\n", (int)pid, sig);
    return 0;
}
8
  • Is this C or C++ or something else? Commented Feb 20, 2016 at 14:25
  • Replace the execvp(...) with int c; while((c = getchar()) != EOF) putchar(c); and edit your question with the result. Commented Feb 20, 2016 at 14:29
  • It is C, I'm sorry msw my college server is down atm so I cannot I run it, but how would that work? It would print the files content but would not execute commands..Than my output wouldn't work. I have to be able to do "ls -al > out.txt" which works, but "sort < out.txt" does not give any outcome. Commented Feb 20, 2016 at 14:52
  • When setting up the args[] array, the last entry must contain NULL, However, in the posted code, that only happens if the line read from the user contains either a < or a >. Commented Feb 23, 2016 at 2:38
  • the posted code contains special functionality for cd, kill, and dir however there is no check in the code for dir, so that functionality will never be accessible to the user. Commented Feb 23, 2016 at 2:40

1 Answer 1

1

When you see a < in the args array here

if(strcmp(args[i],"<")==0)

you set args[i] to NULL

    args[i]=NULL;

But then, you pass it to strcmp()

if(strcmp(args[i],">")==0)

and your child process will happily segfault. Use an if-else-construct here:

if(strcmp(args[i],"<")==0) {        
    args[i]=NULL;
    strcpy(input,args[i+1]);
    in=2;           
} else if(strcmp(args[i],">")==0) {      
    args[i]=NULL;
    strcpy(output,args[i+1]);
    out=2;
}

This should fix the error.

Furthermore, this might come in handy to detect such situations:

...
while (wait (&status) != pid)
     continue;
if (WIFSIGNALED(status))
     printf("Killed by signal %d%s\n",
            WTERMSIG(status), WCOREDUMP(status)?" (Core dumped)":"");
Sign up to request clarification or add additional context in comments.

4 Comments

You've hit on a problem (not necessarily the only problem), but the 'set argument to null' means that the shell command will ignore any subsequent arguments. In POSIX shells, you can write "< input > output command-name arg1 arg2" with the I/O redirection at arbitrary points in the input line (so "> output command-name 2>/dev/null arg1 < input arg2" works too). Unless all the I/O redirection is after the last command argument, zapping the argument pointer loses arguments to the command (in extreme cases, loses the command). The redirection args need to be removed and others shuffled down.
@JonathanLeffler Since the loop is not terminated, it will find a subsequent output redirection as far as I can see. Ok, you mean that regular arguments after a redirection are ignored. This is true, but I would say that this behaviour is at the shell designers disposition.
Yes, the shell will find it, but the execvp() will stop processing the command line at the first null pointer; it won't look for extra arguments after that. So, if you have command-name < input arg1 arg2, the kernel won't pass arg1 or arg2 to the command.
Thank you so much!!! Finally..I feel like a retard now haha. Oh I see, so I can only use one redirection per line and one argument.. That's okay for a start.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.