Skip to content

Commit d592251

Browse files
authored
shell実装 (#8)
* ✨ implement fork exec routine * 👷 add makefile * 🐛 update makefile * ✨ implement read fork exec loop * 💚 enable debug build * 💚 add make clean * 💚 add warning flag * ♻️ 関数に分割したら最適化オプションをつけないと動かないコードができた * ✨ 解決 * ♻️ ゴミファイル削除 * Delete .idea directory * ✨ bg実行機能を実装 * ✨ [pwd] サンプルのpwdを実装 * 👷 add pwd makefile * 🙈 add pwd .gitignore * ✨ 組み込みコマンド検索を実装 * ✨ 組み込みコマンド実行機能を実装 * 💚 fix pwd Makefile * 👷 update Makefile for building internal commands * ♻️ fix duplicate tab * ♻️ ファイル分割 * 🐛 ^DでのSEGVを解決 * ✨ 親プロセス側は^Cを無視するように設定 * ♻️ rename function get_internal_cmd to get_inner_cmd
1 parent 7a61fc4 commit d592251

File tree

8 files changed

+255
-0
lines changed

8 files changed

+255
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rcsh
2+

Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
subdirs := $(shell find . -maxdepth 2 -path "./commands/*")
2+
3+
.PHONY: all $(subdirs) clean
4+
all: $(subdirs) rcsh
5+
6+
$(subdirs):
7+
$(MAKE) -C $@ $(MAKECMDGOALS)
8+
9+
rcsh: rcsh.c
10+
gcc rcsh.c -Wall -Wextra -g -o rcsh
11+
12+
clean: $(subdirs)
13+
rm rcsh
14+

commands/pwd/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pwd

commands/pwd/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
all: pwd.c
2+
gcc pwd.c -o pwd
3+
4+
clean:
5+
rm pwd
6+

commands/pwd/pwd.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <stdio.h>
2+
#include <unistd.h>
3+
#include <stdlib.h>
4+
5+
int main(void) {
6+
const int BUFSIZE = 1024;
7+
char* dirname = getcwd(NULL, BUFSIZE);
8+
puts(dirname);
9+
}

inner_cmds.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <dirent.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
6+
char **get_inner_cmd(int *cmd_num) {
7+
const int MAX_CMD_COUNT = 32;
8+
const int MAX_CMD_LENGTH = 128;
9+
char **commands = calloc(MAX_CMD_COUNT, sizeof(char *));
10+
int cmd_cnt = 0;
11+
12+
DIR *dir = opendir("./commands");
13+
if (dir == NULL) {
14+
fprintf(stderr, "unable to opendir ./commands\n");
15+
exit(1);
16+
}
17+
18+
struct dirent *entry;
19+
while ((entry = readdir(dir)) != NULL) {
20+
if (entry->d_name[0] == '.') {
21+
continue;
22+
}
23+
int len = strlen(entry->d_name);
24+
if (len > MAX_CMD_LENGTH) {
25+
len = MAX_CMD_LENGTH;
26+
}
27+
char *cmd = calloc(len, sizeof(char));
28+
29+
strncpy(cmd, entry->d_name, len);
30+
commands[cmd_cnt++] = cmd;
31+
printf("found: %s\n", entry->d_name);
32+
}
33+
34+
closedir(dir);
35+
*cmd_num = cmd_cnt;
36+
return commands;
37+
}
38+
39+
char *check_inner_cmd(int cmd_cnt, char **cmds, char **argv) {
40+
if (cmds == NULL) {
41+
return argv[0];
42+
}
43+
44+
const int MAX_CMD_LENGTH = 128;
45+
char *cmd = malloc(sizeof(char) * MAX_CMD_LENGTH);
46+
memset(cmd, 0, sizeof(char) * MAX_CMD_LENGTH);
47+
48+
for (int i = 0; i < cmd_cnt; i++) {
49+
if (strcmp(argv[0], cmds[i])) {
50+
continue;
51+
}
52+
snprintf(cmd, MAX_CMD_LENGTH, "./commands/%s/%s", cmds[i], cmds[i]);
53+
break;
54+
}
55+
if (cmd[0] == '\0') {
56+
free(cmd);
57+
cmd = argv[0];
58+
}
59+
60+
return cmd;
61+
}
62+
63+
void free_inner_cmds(int cnt, char **cmds) {
64+
for (int i = 0; i < cnt; i++) {
65+
free(cmds[i]);
66+
}
67+
free(cmds);
68+
}

rcsh.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#include <ctype.h>
2+
#include <signal.h>
3+
#include <stdio.h>
4+
#include <sys/wait.h>
5+
#include <unistd.h>
6+
7+
#include "inner_cmds.c"
8+
#include "read_cmd.c"
9+
10+
pid_t execute(char *, char **);
11+
void sigchld_wait(int);
12+
void sigint_ignore(int sig) { return; }
13+
14+
int main(void) {
15+
// bg実行終了後のリソース回収処理
16+
if (signal(SIGCHLD, sigchld_wait) == SIG_ERR) {
17+
fprintf(stderr, "rcsh: signal(SIGCHLD) failed\n");
18+
exit(1);
19+
}
20+
21+
int argc = 0, status;
22+
bool exec_bg = false;
23+
char **argv;
24+
pid_t pid;
25+
26+
// 組み込みコマンドの取得
27+
int inner_cmds_cnt;
28+
char **inner_cmds = get_inner_cmd(&inner_cmds_cnt);
29+
30+
while (1) {
31+
// ^Cを無視
32+
if (signal(SIGINT, SIG_IGN) == SIG_ERR) {
33+
fprintf(stderr, "rcsh: signal(SIGINT) failed\n");
34+
exit(1);
35+
}
36+
37+
// read
38+
if ((argv = read_cmd(&argc, &exec_bg)) == NULL) {
39+
break;
40+
}
41+
42+
// no input
43+
if (argc < 1) {
44+
free_argv(argv);
45+
continue;
46+
}
47+
48+
// exit
49+
if (strcmp(argv[0], "exit") == 0) {
50+
free_argv(argv);
51+
break;
52+
}
53+
54+
// update cmdpath if internal command
55+
char *cmd = check_inner_cmd(inner_cmds_cnt, inner_cmds, argv);
56+
57+
// execute
58+
pid = execute(cmd, argv);
59+
60+
// wait
61+
if (!exec_bg) {
62+
wait(&status);
63+
}
64+
65+
free_argv(argv);
66+
}
67+
68+
free_inner_cmds(inner_cmds_cnt, inner_cmds);
69+
return 0;
70+
}
71+
72+
pid_t execute(char *cmd, char *argv[]) {
73+
pid_t pid = fork();
74+
if (pid != 0) return pid;
75+
76+
signal(SIGINT, SIG_DFL);
77+
execvp(cmd, argv);
78+
perror(argv[0]);
79+
exit(1);
80+
}
81+
82+
void sigchld_wait(int sig) {
83+
int status;
84+
waitpid(-1, &status, WNOHANG);
85+
printf("process exited with status %d\n", WEXITSTATUS(status));
86+
}

read_cmd.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include <stdbool.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
6+
char **read_cmd(int *argc, bool *exec_bg) {
7+
const int BUFSIZE = 1024;
8+
const char TOK_DELIM[] = " \t\r\n\a";
9+
10+
int arg_cnt = 0, max_argc = 10;
11+
// char input[BUFSIZE];
12+
char *input = malloc(sizeof(char) * BUFSIZE);
13+
char **argv = malloc(max_argc * sizeof(char *));
14+
15+
// input
16+
printf("rcsh> ");
17+
if (fgets(input, BUFSIZE, stdin) == NULL) {
18+
return NULL;
19+
}
20+
input[strlen(input) - 1] = '\0';
21+
22+
// バックグラウンド実行のフラグを設定
23+
// 同時に引数から&記号を削除
24+
char *pos_and;
25+
if ((pos_and = strchr(input, '&')) != NULL) {
26+
*exec_bg = true;
27+
*pos_and = '\0';
28+
}
29+
30+
// strtokは2回目の呼び出しではTOK_DELIMを一切含まない最初の部分をinputから切り出す
31+
//この際word直後のTOK_DELIM文字は\0に置き換えられる
32+
char *word = strtok(input, TOK_DELIM);
33+
argv[arg_cnt++] = word;
34+
35+
while ((word = strtok(NULL, TOK_DELIM))) {
36+
argv[arg_cnt++] = word;
37+
38+
//引数の個数が配列の要素数より多かった時の対処
39+
if (arg_cnt >= max_argc) {
40+
max_argc *= 2;
41+
42+
// reallocは第1引数の内容をコピーした新しい領域を返す.
43+
argv = realloc(argv, max_argc * sizeof(char *));
44+
if (argv == NULL) {
45+
fprintf(stderr, "rcsh: allocation error\n");
46+
exit(1);
47+
}
48+
}
49+
}
50+
51+
// execvは最後にNULLが入っていないと動作しないので末尾にNULLを追加
52+
if (argv[arg_cnt] != NULL) {
53+
argv = realloc(argv, (arg_cnt + 1) * sizeof(char *));
54+
argv[arg_cnt] = NULL;
55+
}
56+
57+
// 入力が\nなど空白だった場合の処理
58+
if (argv[0] == NULL) {
59+
arg_cnt--;
60+
}
61+
62+
*argc = arg_cnt;
63+
return argv;
64+
}
65+
66+
void free_argv(char **argv) {
67+
free(*argv);
68+
free(argv);
69+
}

0 commit comments

Comments
 (0)