本次作業主要目標為使用 socket, pipe, fork, exec 建立我們自訂規範的 network shell
支援:
我們的 netshell 會要求在特定資料夾下執行(ras/),支援下面四個 command 以及 ls, cat, setenv, printenv
int main(void) {return 0;}
#include <stdio.h> #include <iostream> #include <string> #include <stdlib.h> #include <cstring> FILE *fd; using namespace std; int main(int argc, char *argv[]) { char c; int counter = 1; if(argc == 1) fd = stdin; else if(argc == 2) fd = fopen(argv[1], "r"); else { fprintf(stderr,"Usage"); exit(1); } string str=""; while((c = fgetc(fd))!=EOF) { if (c == -1) break; str+=c; if (c == '\n'){ fprintf(stdout, "%4d %s", counter++, str.c_str()); str=""; } } if( strcmp( str.c_str(), "") !=0 ) { cout << " " << counter++ << " " << str; cout << endl; } fclose(fd); return 0; }
#include <stdio.h> #include <stdlib.h> int main(int argc,char **argv){ FILE *fp; char c; int inTag=0; if(argc == 1) fp = stdin; else if (argc == 2) fp = fopen(argv[1],"r"); else{ fprintf(stderr,"Usage:%s <file>\n",argv[1]); exit(1); } while((c = fgetc(fp))!=EOF){ if(c == '<'){ inTag = 1; continue; } if(c == '>'){ inTag = 0; continue; } if(!inTag) fputc(c, stdout); } // fputc('\n' , stdout) ; fflush(stdout); fclose(fp); return(0); }
#include <stdio.h> #include <stdlib.h> #include <memory.h> #include <string.h> #include <ctype.h> int main(int argc,char **argv){ FILE *fp; char c; int inTag=0; bool errTag = false; char TagMsg[1024]; int iCount = 0; if(argc == 1) fp = stdin; else if (argc == 2) fp = fopen(argv[1],"r"); else{ fprintf(stderr,"Usage:%s <file>\n",argv[1]); exit(1); } while((c = fgetc(fp))!=EOF) { if(c == '<'){ inTag = 1; continue; } if(c == '>'){ inTag = 0; continue; } if(inTag) { TagMsg[iCount++] = c; if(!isalpha(c) && c!='/') errTag = true; } if(!inTag) { if(errTag) { TagMsg[iCount] = '\0'; fprintf (stderr, "Error: illegal tag \"%s\"\n",TagMsg); errTag = false ; } iCount = 0; memset( TagMsg, '\0', strlen(TagMsg)); fputc(c, stdout); } } fflush(stdout); fclose(fp); return(0); }
機器上有 max process per user = 512 限制,exec 執行完 process 後將資料寫出,然後必須結束
cat 指令要收到 EOF 才會結束,需要適時的關掉前面 writein 到 pipe 的那端,此時 EOF 才會被送進 pipe,cat 從 readout 這端讀取 input 時才有辦法結束
我一開始做法是全部 process 跑完才一口氣用 for loop 將 fd 關閉,但是這樣 cat 其實在關閉 fd 之前都沒有結束,導致 ls | cat | cat …| cat 這種指令會生出一堆的 cat process,到達 512 上限便會出現 can not fork 的錯誤
file descriptor 在使用完畢時都必須正確關閉,linux 上有 max fd per process = 1024 限制
解決上面第二點後發現 freebsd 沒問題,但 linux 還是跑不過,error 是 can not create pipe,原因是我執行結束後關了 pipe 的 writein 端,但 readout 忘記了,所以就會留一個 fd 在那,當 pipe 越來越多,fd 量到達上限便會出現 can not create pipe 錯誤
在關 fd 時要注意避免關到 0,1,2
pipes_fd 存在陣列中,在關閉時要注意 index,若陣列位置尚未指配 pipe 指令產生的 fd,則陣列初始為 0,這時很可能誤關 stdin,產生 bad file descriptor 的錯誤
讀取網路 socket 資料時要確認是否已經讀到換行字元,讀到才能開始處理
在網路傳遞中,即使 server 程式中用的 buffer 夠大,但讀進來也可能讀不滿,然後沒有讀到 \n
這是因為過長的字串可能在 switch 被切割傳送,不會一次就送到
所以一開始在測試就發生 cmd string 不長的測資都沒問題,cmd string 很長的測資,若 client 和 server 跑在同一台機器,不管用 IP, localhost, domain name連線有都沒問題,但當 client 和 server 跑在不同機器,過長字串就會被切斷,導致讀取到的不如預期而後續執行錯誤
切割位置基本上差不多,應該是由 switch 決定,偶爾會有一兩個 char 差異,所以 ls | cat | cat … cat | cat 指令我發現大部分被切斷會是 cat | 結尾,然後後段會變成另一次讀取 cat | cat … | cat,結果會變成第一段 cmd 正常輸出,但第二段開頭是 cat,stdin 為 0 ,沒有人送 EOF 給他,程式會停在這邊
但偶爾會切成 ls | cat | cat | c 與 at | cat | … cat | cat 然後我的程式就會出現 unknown cammand [c]. , unknown cammand [at].