2014-02-09 80 views
0

我正在編寫一個C程序來模擬一個簡單的shell。這個shell基本上像其他shell(ls,cat等)一樣評估命令,以及處理流水線和重定向。C程序中的shell程序有奇怪的行爲

目前,我試圖通過獲取用戶輸入令牌化它展開,取出,並執行提供的命令(例如,只執行「LS」,而不是「ls -l命令」)。但是,我在分叉方面遇到很多困難。似乎每次我分叉時,出現了一些錯誤,並創建了數百個相同的進程,導致我的計算機被凍結,我不得​​不重新啓動。該代碼似乎是正確的,但我不知道是什麼導致此行爲。以下是我的代碼(主要方法和輸入標記器方法)的相關部分。

int main() { 
    char inputLine[512]; //user input 
    char *args[10];   //arguments 
    char* pathVar = "/bin/";//path for argument 
    char programPath[512]; //pathVar + args[0] 
    int n;     //count variable 

    //loop 
    while (1) { 
     //print prompt, get input 
     printf("input> "); 
     fgets(inputLine, 512, stdin); 
     n = tokenizer(inputLine, args); 

     //fork process 
     pid_t pid = fork(); 

     if (pid != 0) {  //if parent 
      wait(NULL); 
     } else {   //if child 
      //format input for execution 
      strcpy(programPath, pathVar); 
      strcat(programPath, args[0]); 

      //execute user command 
      int returnVal = execv(programPath, args); 
     } 
    } 

    return 0; 
} 

int tokenizer(char *input, char *args[]) { 
    char *line;    //current line 
    int i = 0;    //count variable 

    line = input; 
    args[i] = strtok(line, " "); 
    do { 
     i++; 
     line = NULL; 
     args[i] = strtok(line, " "); 
    } while (args[i] != NULL); 

    return i; 
} 
+0

什麼是 「出錯」 是什麼意思?你有沒有嘗試過在exec中更簡單一些 - 或者更換exec--來隔離問題?你有沒有嘗試打印出programPath,以確保它是你認爲它應該是什麼? – keshlam

+0

我已經確定在嘗試執行exec之前,令牌化等工作。用戶輸入被正確標記(參數用空格分隔)並且程序路徑正確地形成(例如「/ bin/ls」)。大問題似乎與分叉有關。 – user41419

+0

您在子進程中執行的命令不會在控制檯上寫入 –

回答

2

把它放在一起: 您需要檢查forkexecv的失敗。 你應該exitexecv失敗後(或許還有fork失敗後)。 你需要添加\nstrtok分隔符(或刪除輸入線換行以其他方式)。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 

#define MAXARGS 10 
#define PATH "/bin/" 

int main() { 
    char inputLine[BUFSIZ]; 
    char *args[MAXARGS]; 
    char programPath[BUFSIZ + sizeof(PATH) + 10]; 

    while (1) { 
     printf(":-> "); 
     if (fgets(inputLine, BUFSIZ, stdin) == NULL) /* ctrl-D entered */ 
      break; 

     tokenize(inputLine, args); 

     pid_t pid = fork(); 
     if (pid == -1) { 
      perror("fork"); 
      exit(EXIT_FAILURE); 
     } 

     if (pid != 0) { /* parent */ 
      wait(NULL); 

     } else {   /* child */ 
      strcpy(programPath, PATH); 
      strcat(programPath, args[0]); 

      execv(programPath, args); /* will not return unless it fails */ 

      perror("execv"); 
      exit(EXIT_FAILURE); 
     } 
    } 

    return 0; 
} 

int tokenize(char *input, char *args[]) { 
    int i = 0; 

    args[0] = strtok(input, " \n"); 
    for (i = 0; args[i] && i < MAXARGS-1; ++i) 
     args[++i] = strtok(NULL, " \n"); 

    return i; 
} 
+0

謝謝,這工作非常好!沒有更多的挫折感或重新啓動。 – user41419

1

你應該檢查execv並沒有失敗,也一定要退出()處子塊的結尾。

  //execute user command 
      int returnVal = execv(programPath, args); 

      // check return from execv 
      if (returnVal < 0) { 
       perror("execv"); 
       exit(1); 
      } 

此外,當心使用如在此上下文中的strcpy功能,因爲它們可能會導致緩衝區溢出。如果一個不可信任的攻擊者類型正在與你的shell交談,這種類型的安全問題可能會讓他們脫離「沙箱」。

+0

請記住,fork()本身也可能失敗! – jduck

+0

捕獲'execv()'的返回值沒有意義。如果成功,它不會返回;如果它返回,則失敗。在失敗的'execv()'之後放置錯誤消息和退出是一個非常好的主意。它只是不需要在條件代碼中。 –

+0

喬納森公平。另外,嗨!希望你一切都好。 – jduck