thruun
thruun

Reputation: 501

C Infinite loop in shell after piping commands into it

I am currently trying to build a custom shell for a class. I'm able to execute commands issued from this shell without problem, but if I pipe my command into my shell, I end up with an infinite-loop. I am out of ideas why this might happen. The example below would cause an infinite loop.

echo "ls" | ./myshell

Of course I have to redirect the output of programs, if a pipe within a command would occur, e.g. ls | grep test. Within my shell, this works flawlessly. I am using fork(), execv() and of course pipe + dup for redirecting streams between child processes.

It seems, as if fgets() would not clear the STDIN_FILENO from the last issued command.

For the command echo "ls" | ./myshell my program would do the following: (minimal working example)

EDIT: Minimal working example

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

int running = 1;
int exit_code = 0;

char cwd[256];
char command[256];
char args[10][256];
char buffer[256] __attribute__((aligned(4096)));

void handle_command(char* buffer, int buffer_size)
{
  int c = 0;
  int argsCount = -1;
  int lastIndex = 0;

  for (c = 0; c < buffer_size && buffer[c]; c++)
  {
    if (argsCount > 10)
    {
      argsCount = 10;
      printf("Argument Count is limited to 10 (no dynamic memory allocation) all other arguments will be ignored\n");
      break;
    }
    if (buffer[c] == '\r' || buffer[c] == '\n' || buffer[c] == ' ')
    {
      if (argsCount == -1)
      {
        memcpy(command, buffer + lastIndex, c - lastIndex);
        command[c - lastIndex] = 0;
      }
      else
      {
        memcpy(args[argsCount], buffer + lastIndex, c - lastIndex);
        args[argsCount][c - lastIndex] = 0;
      }
      argsCount++;
      lastIndex = c + 1;

    }
  }

  if (strcmp(command, "exit") == 0)
  {
    c = 4;
    while (buffer[c] == ' ')
      c++;
    exit_code = atoi(&buffer[c]);
    printf("Exiting Shell with exit_code %d\n", exit_code);
    running = 0;
  }
  else if (strcmp(command, "") != 0)
  {
    // -------------- Add structure to commands --------------------------------
    struct command_struct{
      char *options[10];
    } sub_commands[1];
    // Simplified code, there would be a dynamic amount of sub_commands
    // and further logic to handle pipes and < > >>

    // initialize first command, would work dynamically
    sub_commands[0].options[0] = command;
    sub_commands[0].options[1] = NULL;

    int status;
    int pid = fork();
    if (pid == 0) {
      execvp(sub_commands[0].options[0], sub_commands[0].options);
      perror("Error: Reached code after execvp\n");
    } else if (pid < 0) {
      perror("Cannot fork!\n");
    }
    wait(&status);
  }
}


int main(int argc, char *argv[])
{
  cwd[0] = '/';

  do
  {
    printf("\n%s %s%s", "SHELL:", cwd, "> ");
    fgets(buffer, 255, stdin);
    buffer[255] = 0;
    handle_command(buffer, 256);
    for (size_t a = 0; a < 256; a++)
      buffer[a] = 0;

  } while (running);

  return exit_code;
}

EDIT I have to point out, that parts of this code were given in this class.

Any help whatsoever would be greatly appreciated!

Upvotes: 1

Views: 1691

Answers (1)

chux
chux

Reputation: 153348

OP code will only quit the main() do loop when running == 0.

running = 0; only occurs when strcmp(command, "exit") == 0 and it is not clear that with string parsing that just "exit" is ever loaded in command. Note: command is not cleanly initialized per each handle_command() call and command does not need to be a global variable.

Adjust code to quit when fgets() returns NULL and review line processing. Suggest:

do {
  printf("\n%s %s%s", "SHELL:", cwd, "> ");
  if (fgets(buffer, sizeof buffer, stdin) == NULL) {
    break; 
  }
  // lop off potential end-of-line character(s)
  buffer[strcspn(buffer,"\r\n")] = '\0';
  handle_command(buffer, sizeof buffer);
} while (running);

Upvotes: 4

Related Questions