KAU/Linux
Shell Coading.
Angler.lee
2012. 5. 17. 11:03
간단한 쉘 동작입니다.
#include <stdio.h>
#include <string.h>
#include <stdlib.h> //exit 함수 사용하려면 추
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#define MAXARG 30 //command를 저장하기 위한
Structure struct COMMAND
{
int argv; //argument의 개수
char* arg[MAXARG]; //command 내용
char flag; //구분자(;, >, <, |, &)
};
main()
{
int i, j;
char buf[256];
char* arg[MAXARG];
char* s;
char* save;
int argv;
static const char delim[] = " \t\n"; // 공백, 탭, 개행으로 구분
int pid, status;
int cmdIdx; //명령어 개수
int syntax; //syntax error 판단
struct COMMAND cmd[MAXARG]; //command 저장
//리다이렉션에서 쓰이는 변수
int fd2, c;
//파이프에서 쓰이는 변수
int fd[2];
int bpipe = 0;
int Flag = 0;
//리다이렉션이나 파이프 실행 시 1
while (1) { // 무한 반복
printf("[shell]$ "); // 프롬프트 출력
fgets(buf, 256, stdin); // 오버플로우 안정을 위해
fgets argv = 0;
cmdIdx = 0;
syntax = 0; //초기화
for (i = 0; i < MAXARG; i++) {
cmd[i].argv = 0;
cmd[i].flag = 0;
for (j = 0; j < MAXARG; j++) {
cmd[i].arg[j] = NULL;
}
}
s = strtok_r(buf, delim, &save); // 문자열에서 구분자 기준으로 단어 분리
while (argv < MAXARG && s) {
arg[argv++] = s;
s = strtok_r(NULL, delim, &save);
}
arg[argv] = (char*)0; // 인수가 더 없음
if (!strcmp(arg[0], "quit")) // 단어가 ‘quit’이면 while 문 벗어남
break; //|, >, <, &, ; 로 구분하여 flag로 지정
for (i = 0; i < argv; i++) {
if (!strcmp(arg[i], "|"))
cmd[cmdIdx++].flag = '|';
else if (!strcmp(arg[i], ">"))
cmd[cmdIdx++].flag = '>';
else if (!strcmp(arg[i], "<"))
cmd[cmdIdx++].flag = '<';
else if (!strcmp(arg[i], "&")) {
if (i != argv - 1) {
syntax = -1;
break;
}
cmd[cmdIdx++].flag = '&';
}
else {
int lw = strlen(arg[i]) - 1; //LAST WORD
if (arg[i][lw] == ';') {
cmd[cmdIdx].flag = ';';
arg[i][lw] = '\0';
cmd[cmdIdx].arg[cmd[cmdIdx].argv++] = arg[i];
cmdIdx++;
}
else {
cmd[cmdIdx].arg[cmd[cmdIdx].argv++] = arg[i]; //마지막명령일 경우 if(i == argv-1) cmdIdx++;
}
}
}
//syntax error 판단
if (syntax == -1 || !strcmp(arg[argv - 1], "|") || !strcmp(arg[argv - 1], ">") || !strcmp(arg[argv - 1], "<")) {
printf("문법 에러 입니다!!\n");
continue;
} for (i = 0; i < cmdIdx; i++) { //제일 마지막 앞을 제외하고 '>' '<'가 있으면 문법오류
if (i != cmdIdx - 2 && cmd[i].flag == '>' || cmd[i].flag == '<') {
printf("문법 에러 입니다!!\n");
continue;
}
} /////////////////// fork 호출에 실패하면
if ((pid = fork()) == -1)
perror("fork failed");
else if (pid != 0) {
//백그라운드 실행이 아니면
if (cmd[argv - 1].flag != '&')
pid = wait(&status); // 자식 프로세스가 끝나기를 기다림.
////////////////////////////////// 자식: execvp를 이용하여 arg[0] 실행
}
else {
// Foreground, Background 프로세스 그룹 지정
for (i = 0; i < cmdIdx; i++) {
// |, >, < 일 경우, Falg 1로 설정
if (cmd[i].flag == '|' || cmd[i].flag == '>' || cmd[i].flag == '<')
Flag = 1;
else
Flag = 0;
if (cmd[cmdIdx - 1].flag == '&') {
if (Flag == 0) {
setpgid(getpid(), 0);
printf("Background 프로세스 그룹ID: %d \t\n", getpgrp());
}
}
else {
if (Flag == 0)
printf("Foreground 프로세스 그룹 ID: %d\t\n", getpgrp());
} // 리다이렉션 기능
if (cmd[i].flag == '>') {
fd2 = creat(cmd[i + 1].arg[0], 0600);
dup2(fd2, STDOUT_FILENO);
execvp(cmd[i].arg[0], cmd[i].arg);
}
else if (cmd[i].flag == '<') {
fd2 = open(cmd[i + 1].arg[0], O_RDONLY | O_CREAT, 0600);
dup2(fd2, STDIN_FILENO);
execvp(cmd[i].arg[0], cmd[i].arg);
} // 파이프 기능
else if (cmd[i].flag == '|') {
//처음파이프
if (bpipe == 0) pipe(fd);
if (bpipe == 0 && fork() == 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execvp(cmd[i].arg[0], cmd[i].arg);
perror("Connect");
exit(0);
}
else { //자식이 실행한걸 읽어오세요.
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]); //다음번에도 파이프 플래그가 있을 경우
if (cmd[i + 1].flag == '|') {
//새로운 파이프를 생성하여
pipe(fd);
//자식은 파이프에 다시 실행한 값을 쓰도록 하고 종료
if (fork() == 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execvp(cmd[i + 1].arg[0], cmd[i + 1].arg);
perror("Connect");
exit(0);
}
//부모의 경우에는 다음 루프에서 이 루트로 진행 할 수 있도록 플래그 pipe를 1
else
bpipe = 1;
//인다이렉션 플래그가 있을 경우 실행하지 않음
}
else if (cmd[i + 1].flag == '>' || cmd[i + 1].flag == '<') {
bpipe = 0;
//그외 플래그 일경우 그냥 실행
}
else {
execvp(cmd[i + 1].arg[0], cmd[i + 1].arg);
perror("Connect");
bpipe = 0;
}
}
}
else if (cmd[i].flag == ';') {
//자식은 실행하고
if (fork() == 0) {
execvp(cmd[i].arg[0], cmd[i].arg);
} //부모는 기다리고
else {
pid = wait(&status); // 자식 프로세스가 끝나기를 기다림.
}
}
// 그 외의 명령어 실행
else {
if (Flag == 0)
execvp(cmd[i].arg[0], cmd[i].arg);
else
exit(0);
}
}
}
}
exit(0);
}