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