1 #include "cmd.h"
2 #include "cmd_help.h"
3 #include "cmd_test.h"
4 #include <dirent.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <libsystem/syscall.h>
8 #include <signal.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 
17 // 当前工作目录(在main_loop中初始化)
18 char *shell_current_path = NULL;
19 /**
20  * @brief shell 内建函数的主命令与处理函数的映射表
21  *
22  */
23 struct built_in_cmd_t shell_cmds[] = {
24     {"cd", shell_cmd_cd},
25     {"cat", shell_cmd_cat},
26     {"exec", shell_cmd_exec},
27     {"ls", shell_cmd_ls},
28     {"mkdir", shell_cmd_mkdir},
29     {"pwd", shell_cmd_pwd},
30     {"rm", shell_cmd_rm},
31     {"rmdir", shell_cmd_rmdir},
32     {"reboot", shell_cmd_reboot},
33     {"touch", shell_cmd_touch},
34     {"about", shell_cmd_about},
35     {"free", shell_cmd_free},
36     {"help", shell_help},
37     {"pipe", shell_pipe_test},
38     {"kill", shell_cmd_kill},
39 
40 };
41 // 总共的内建命令数量
42 const static int total_built_in_cmd_num = sizeof(shell_cmds) / sizeof(struct built_in_cmd_t);
43 
44 /**
45  * @brief 将cwd与文件名进行拼接,得到最终的文件绝对路径
46  *
47  * @param filename 文件名
48  * @param result_path_len 结果字符串的大小
49  * @return char* 结果字符串
50  */
get_target_filepath(const char * filename,int * result_path_len)51 static char *get_target_filepath(const char *filename, int *result_path_len)
52 {
53     char *file_path = NULL;
54     if (filename[0] != '/')
55     {
56         int cwd_len = strlen(shell_current_path);
57 
58         // 计算文件完整路径的长度
59         *result_path_len = cwd_len + strlen(filename);
60 
61         file_path = (char *)malloc(*result_path_len + 2);
62 
63         memset(file_path, 0, *result_path_len + 2);
64 
65         strncpy(file_path, shell_current_path, cwd_len);
66 
67         // 在文件路径中加入斜杠
68         if (cwd_len > 1)
69             file_path[cwd_len] = '/';
70 
71         // 拼接完整路径
72         strcat(file_path, filename);
73     }
74     else
75     {
76         *result_path_len = strlen(filename);
77         file_path = (char *)malloc(*result_path_len + 2);
78 
79         memset(file_path, 0, *result_path_len + 2);
80 
81         strncpy(file_path, filename, *result_path_len);
82         if (filename[(*result_path_len) - 1] != '/')
83             file_path[*result_path_len] = '/';
84     }
85 
86     return file_path;
87 }
88 
89 /**
90  * @brief 寻找对应的主命令编号
91  *
92  * @param main_cmd 主命令
93  * @return int 成功:主命令编号
94  *              失败: -1
95  */
shell_find_cmd(char * main_cmd)96 int shell_find_cmd(char *main_cmd)
97 {
98 
99     for (int i = 0; i < total_built_in_cmd_num; ++i)
100     {
101         if (strcmp(main_cmd, shell_cmds[i].name) == 0) // 找到对应的命令号
102             return i;
103     }
104     // 找不到该命令
105     return -1;
106 }
107 
108 /**
109  * @brief 运行shell内建的命令
110  *
111  * @param index 主命令编号
112  * @param argc 参数数量
113  * @param argv 参数列表
114  */
shell_run_built_in_command(int index,int argc,char ** argv)115 void shell_run_built_in_command(int index, int argc, char **argv)
116 {
117     if (index >= total_built_in_cmd_num)
118         return;
119     // printf("run built-in command : %s\n", shell_cmds[index].name);
120 
121     shell_cmds[index].func(argc, argv);
122 }
123 
124 /**
125  * @brief cd命令:进入文件夹
126  *
127  * @param argc
128  * @param argv
129  * @return int
130  */
131 
shell_cmd_cd(int argc,char ** argv)132 int shell_cmd_cd(int argc, char **argv)
133 {
134 
135     int current_dir_len = strlen(shell_current_path);
136     if (argc < 2)
137     {
138         shell_help_cd();
139         goto done;
140     }
141     // 进入当前文件夹
142     if (!strcmp(".", argv[1]))
143         goto done;
144 
145     // 进入父目录
146     if (!strcmp("..", argv[1]))
147     {
148 
149         // 当前已经是根目录
150         if (!strcmp("/", shell_current_path))
151             goto done;
152 
153         // 返回到父目录
154         int index = current_dir_len - 1;
155         for (; index > 1; --index)
156         {
157             if (shell_current_path[index] == '/')
158                 break;
159         }
160         shell_current_path[index] = '\0';
161 
162         // printf("switch to \" %s \"\n", shell_current_path);
163         goto done;
164     }
165 
166     int dest_len = strlen(argv[1]);
167     // 路径过长
168     if (dest_len >= SHELL_CWD_MAX_SIZE - 1)
169     {
170         printf("ERROR: Path too long!\n");
171         goto fail;
172     }
173 
174     if (argv[1][0] == '/')
175     {
176         // ======进入绝对路径=====
177         int ec = chdir(argv[1]);
178         if (ec == -1)
179             ec = errno;
180         if (ec == 0)
181         {
182             // 获取新的路径字符串
183             char *new_path = (char *)malloc(dest_len + 2);
184             memset(new_path, 0, dest_len + 2);
185             strncpy(new_path, argv[1], dest_len);
186 
187             // 释放原有的路径字符串的内存空间
188             free(shell_current_path);
189 
190             shell_current_path = new_path;
191 
192             shell_current_path[dest_len] = '\0';
193             return 0;
194         }
195         else
196             goto fail;
197         ; // 出错则直接忽略
198     }
199     else // ======进入相对路径=====
200     {
201         int dest_offset = 0;
202         if (dest_len > 2)
203         {
204             if (argv[1][0] == '.' && argv[1][1] == '/') // 相对路径
205                 dest_offset = 2;
206         }
207 
208         int new_len = current_dir_len + dest_len - dest_offset;
209 
210         if (new_len >= SHELL_CWD_MAX_SIZE - 1)
211         {
212             printf("ERROR: Path too long!\n");
213             goto fail;
214         }
215 
216         // 拼接出新的字符串
217         char *new_path = (char *)malloc(new_len + 2);
218         memset(new_path, 0, sizeof(new_path));
219         strncpy(new_path, shell_current_path, current_dir_len);
220 
221         if (current_dir_len > 1)
222             new_path[current_dir_len] = '/';
223         strcat(new_path, argv[1] + dest_offset);
224         int x = chdir(new_path);
225         if (x == 0) // 成功切换目录
226         {
227             free(shell_current_path);
228             // printf("new_path=%s, newlen= %d\n", new_path, new_len);
229             new_path[new_len + 1] = '\0';
230             shell_current_path = new_path;
231             goto done;
232         }
233         else
234         {
235             free(new_path);
236             printf("ERROR: Cannot switch to directory: %s\n", new_path);
237             goto fail;
238         }
239     }
240 
241 fail:;
242 done:;
243     // 释放参数所占的内存
244     free(argv);
245     return 0;
246 }
247 
248 /**
249  * @brief 查看文件夹下的文件列表
250  *
251  * @param argc
252  * @param argv
253  * @return int
254  */
shell_cmd_ls(int argc,char ** argv)255 int shell_cmd_ls(int argc, char **argv)
256 {
257     struct DIR *dir = opendir(shell_current_path);
258 
259     if (dir == NULL)
260         return -1;
261 
262     struct dirent *buf = NULL;
263     // printf("dir=%#018lx\n", dir);
264 
265     while (1)
266     {
267         buf = readdir(dir);
268         if (buf == NULL)
269             break;
270 
271         int color = COLOR_WHITE;
272         if (buf->d_type == DT_DIR)
273             color = COLOR_YELLOW;
274         else if (buf->d_type == DT_REG)
275             color = COLOR_INDIGO;
276         else if (buf->d_type == DT_BLK || buf->d_type == DT_CHR)
277             color = COLOR_GREEN;
278 
279         char output_buf[256] = {0};
280 
281         sprintf(output_buf, "%s   ", buf->d_name);
282         put_string(output_buf, color, COLOR_BLACK);
283     }
284     printf("\n");
285     closedir(dir);
286 
287     if (argv != NULL)
288         free(argv);
289 
290     return 0;
291 }
292 
293 /**
294  * @brief 显示当前工作目录的命令
295  *
296  * @param argc
297  * @param argv
298  * @return int
299  */
shell_cmd_pwd(int argc,char ** argv)300 int shell_cmd_pwd(int argc, char **argv)
301 {
302     if (shell_current_path)
303         printf("%s\n", shell_current_path);
304     if (argv != NULL)
305         free(argv);
306     return 0;
307 }
308 
309 /**
310  * @brief 查看文件内容的命令
311  *
312  * @param argc
313  * @param argv
314  * @return int
315  */
shell_cmd_cat(int argc,char ** argv)316 int shell_cmd_cat(int argc, char **argv)
317 {
318     int path_len = 0;
319     char *file_path = get_target_filepath(argv[1], &path_len);
320 
321     // 打开文件
322     int fd = open(file_path, 0);
323     if (fd <= 0)
324     {
325         printf("ERROR: Cannot open file: %s, fd=%d\n", file_path, fd);
326         return -1;
327     }
328     // 获取文件总大小
329     int file_size = lseek(fd, 0, SEEK_END);
330     // 将文件指针切换回文件起始位置
331     lseek(fd, 0, SEEK_SET);
332 
333     char *buf = (char *)malloc(512);
334 
335     while (file_size > 0)
336     {
337         memset(buf, 0, 512);
338         int l = read(fd, buf, 511);
339         if (l < 0)
340         {
341             printf("ERROR: Cannot read file: %s\n", file_path);
342             return -1;
343         }
344         buf[l] = '\0';
345 
346         file_size -= l;
347         printf("%s", buf);
348     }
349     close(fd);
350     free(buf);
351     free(file_path);
352     if (argv != NULL)
353         free(argv);
354     return 0;
355 }
356 
357 /**
358  * @brief 创建空文件的命令
359  *
360  * @param argc
361  * @param argv
362  * @return int
363  */
shell_cmd_touch(int argc,char ** argv)364 int shell_cmd_touch(int argc, char **argv)
365 {
366     int path_len = 0;
367     char *file_path;
368     bool alloc_full_path = false;
369     if (argv[1][0] == '/')
370         file_path = argv[1];
371     else
372     {
373         file_path = get_target_filepath(argv[1], &path_len);
374         alloc_full_path = true;
375     }
376 
377     // 打开文件
378     int fd = open(file_path, O_CREAT);
379     switch (fd)
380     {
381     case -ENOENT:
382         put_string("Parent dir not exists.\n", COLOR_RED, COLOR_BLACK);
383         break;
384 
385     default:
386         break;
387     }
388     close(fd);
389     if (argv != NULL)
390         free(argv);
391     if (alloc_full_path)
392         free(file_path);
393     return 0;
394 }
395 
396 /**
397  * @brief 创建文件夹的命令
398  *
399  * @param argc
400  * @param argv
401  * @return int
402  */
shell_cmd_mkdir(int argc,char ** argv)403 int shell_cmd_mkdir(int argc, char **argv)
404 {
405     int result_path_len = -1;
406     char *full_path = NULL;
407     bool alloc_full_path = false;
408     if (argv[1][0] == '/')
409         full_path = argv[1];
410     else
411     {
412         full_path = get_target_filepath(argv[1], &result_path_len);
413         alloc_full_path = true;
414     }
415     // printf("mkdir: full_path = %s\n", full_path);
416     int retval = mkdir(full_path, 0);
417 
418     if (argv != NULL)
419         free(argv);
420     if (alloc_full_path)
421         free(full_path);
422     return retval;
423 }
424 
425 /**
426  * @brief 删除文件夹的命令
427  *
428  * @param argc
429  * @param argv
430  * @return int
431  */
shell_cmd_rmdir(int argc,char ** argv)432 int shell_cmd_rmdir(int argc, char **argv)
433 {
434     char *full_path = NULL;
435     int result_path_len = -1;
436     bool alloc_full_path = false;
437 
438     if (argv[1][0] == '/')
439         full_path = argv[1];
440     else
441     {
442         full_path = get_target_filepath(argv[1], &result_path_len);
443         alloc_full_path = true;
444     }
445     int retval = rmdir(full_path);
446     if (retval != 0)
447         printf("Failed to remove %s, retval=%d\n", full_path, retval);
448     // printf("rmdir: path=%s, retval=%d\n", full_path, retval);
449     if (argv != NULL)
450         free(argv);
451     if (alloc_full_path)
452         free(full_path);
453     return retval;
454 }
455 
456 /**
457  * @brief 删除文件的命令
458  *
459  * @param argc
460  * @param argv
461  * @return int
462  */
shell_cmd_rm(int argc,char ** argv)463 int shell_cmd_rm(int argc, char **argv)
464 {
465     char *full_path = NULL;
466     int result_path_len = -1;
467     int retval = 0;
468     bool alloc_full_path = false;
469 
470     if (argv[1][0] == '/')
471         full_path = argv[1];
472     else
473     {
474         full_path = get_target_filepath(argv[1], &result_path_len);
475         alloc_full_path = true;
476     }
477 
478     retval = rm(full_path);
479     // printf("rmdir: path=%s, retval=%d\n", full_path, retval);
480     if (retval != 0)
481         printf("Failed to remove %s, retval=%d\n", full_path, retval);
482     if (alloc_full_path)
483         free(full_path);
484     if (argv != NULL)
485         free(argv);
486     return retval;
487 }
488 
489 /**
490  * @brief 执行新的程序的命令
491  *
492  * @param argc
493  * @param argv
494  * @return int
495  */
shell_cmd_exec(int argc,char ** argv)496 int shell_cmd_exec(int argc, char **argv)
497 {
498     pid_t pid = fork();
499     int retval = 0;
500     // printf("  pid=%d  \n",pid);
501 
502     if (pid == 0)
503     {
504 
505         // 子进程
506         int path_len = 0;
507         char *file_path = get_target_filepath(argv[1], &path_len);
508         // printf("before execv, path=%s, argc=%d\n", file_path, argc);
509         execv(file_path, argv);
510         // printf("after execv, path=%s, argc=%d\n", file_path, argc);
511         free(argv);
512         free(file_path);
513 
514         exit(-1);
515     }
516     else
517     {
518         // 如果不指定后台运行,则等待退出
519         if (strcmp(argv[argc - 1], "&") != 0)
520             waitpid(pid, &retval, 0);
521         else
522             printf("[1] %d\n", pid); // 输出子进程的pid
523 
524         free(argv);
525     }
526 }
527 
shell_cmd_about(int argc,char ** argv)528 int shell_cmd_about(int argc, char **argv)
529 {
530     if (argv != NULL)
531         free(argv);
532     int aac = 0;
533     char **aav;
534 
535     unsigned char input_buffer[INPUT_BUFFER_SIZE] = {0};
536 
537     strcpy(input_buffer, "exec /bin/about.elf\0");
538 
539     parse_command(input_buffer, &aac, &aav);
540 
541     return shell_cmd_exec(aac, aav);
542 }
543 
shell_cmd_kill(int argc,char ** argv)544 int shell_cmd_kill(int argc, char **argv)
545 {
546     int retval = 0;
547     if (argc < 2)
548     {
549         printf("Usage: Kill <pid>\n");
550         retval = -EINVAL;
551         goto out;
552     }
553     retval = kill(atoi(argv[1]), SIGKILL);
554 out:;
555     free(argv);
556     return retval;
557 }
558 
559 /**
560  * @brief 重启命令
561  *
562  * @param argc
563  * @param argv
564  * @return int
565  */
shell_cmd_reboot(int argc,char ** argv)566 int shell_cmd_reboot(int argc, char **argv)
567 {
568     return syscall_invoke(SYS_REBOOT, 0, 0, 0, 0, 0, 0, 0, 0);
569 }
570 
shell_cmd_free(int argc,char ** argv)571 int shell_cmd_free(int argc, char **argv)
572 {
573     int retval = 0;
574     if (argc == 2 && strcmp("-m", argv[1]) != 0)
575     {
576         retval = -EINVAL;
577         printf("Invalid argument: %s\n", argv[1]);
578         goto done;
579     }
580 
581     struct mstat_t mst = {0};
582     retval = mstat(&mst);
583     if (retval != 0)
584     {
585         printf("Failed: retval=%d", retval);
586         goto done;
587     }
588 
589     printf("\ttotal\tused\tfree\tshared\tcache\tavailable\n");
590     printf("Mem:\t");
591     if (argc == 1) // 按照kb显示
592     {
593         printf("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t\n", mst.total >> 10, mst.used >> 10, mst.free >> 10, mst.shared >> 10,
594                mst.cache_used >> 10, mst.available >> 10);
595     }
596     else // 按照MB显示
597     {
598         printf("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t\n", mst.total >> 20, mst.used >> 20, mst.free >> 20, mst.shared >> 20,
599                mst.cache_used >> 20, mst.available >> 20);
600     }
601 
602 done:;
603     if (argv != NULL)
604         free(argv);
605     return retval;
606 }
607 
608 /**
609  * @brief 解析shell命令
610  *
611  * @param buf 输入缓冲区
612  * @param argc 返回值:参数数量
613  * @param argv 返回值:参数列表
614  * @return int 主命令的编号,小于零为无效命令
615  */
parse_command(char * buf,int * argc,char *** argv)616 int parse_command(char *buf, int *argc, char ***argv)
617 {
618     // printf("parse command\n");
619     int index = 0; // 当前访问的是buf的第几位
620     // 去除命令前导的空格
621     while (index < INPUT_BUFFER_SIZE && buf[index] == ' ')
622         ++index;
623     // 如果去除前导空格后第一项为0x00,则归为空命令
624     if (!buf[index])
625         return -1;
626 
627     // 计算参数数量
628     for (int i = index; i < (INPUT_BUFFER_SIZE - 1); ++i)
629     {
630         // 到达了字符串末尾
631         if (!buf[i])
632             break;
633         if (buf[i] != ' ' && (buf[i + 1] == ' ' || buf[i + 1] == '\0'))
634             ++(*argc);
635     }
636 
637     // printf("\nargc=%d\n", *argc);
638 
639     // 为指向每个指令的指针分配空间
640     *argv = (char **)malloc(sizeof(char **) * (*argc + 1));
641     memset(*argv, 0, sizeof(char **) * (*argc + 1));
642     // 将每个命令都单独提取出来
643     for (int i = 0; i < *argc && index < INPUT_BUFFER_SIZE; ++i)
644     {
645         // 提取出命令,以空格作为分割
646         *((*argv) + i) = &buf[index];
647         while (index < (INPUT_BUFFER_SIZE - 1) && buf[index] && buf[index] != ' ')
648             ++index;
649         buf[index++] = '\0';
650 
651         // 删除命令间多余的空格
652         while (index < INPUT_BUFFER_SIZE && buf[index] == ' ')
653             ++index;
654 
655         // printf("%s\n", (*argv)[i]);
656     }
657     // 以第一个命令作为主命令,查找其在命令表中的编号
658     return shell_find_cmd(**argv);
659 }