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     // 获取文件总大小
313     int file_size = lseek(fd, 0, SEEK_END);
314     // 将文件指针切换回文件起始位置
315     lseek(fd, 0, SEEK_SET);
316 
317     char *buf = (char *)malloc(512);
318 
319     while (file_size > 0)
320     {
321         memset(buf, 0, 512);
322         int l = read(fd, buf, 511);
323         buf[l] = '\0';
324 
325         file_size -= l;
326         printf("%s", buf);
327     }
328     close(fd);
329     free(buf);
330     free(file_path);
331     if (argv != NULL)
332         free(argv);
333     return 0;
334 }
335 
336 /**
337  * @brief 创建空文件的命令
338  *
339  * @param argc
340  * @param argv
341  * @return int
342  */
shell_cmd_touch(int argc,char ** argv)343 int shell_cmd_touch(int argc, char **argv)
344 {
345     int path_len = 0;
346     char *file_path;
347     bool alloc_full_path = false;
348     if (argv[1][0] == '/')
349         file_path = argv[1];
350     else
351     {
352         file_path = get_target_filepath(argv[1], &path_len);
353         alloc_full_path = true;
354     }
355 
356     // 打开文件
357     int fd = open(file_path, O_CREAT);
358     switch (fd)
359     {
360     case -ENOENT:
361         put_string("Parent dir not exists.\n", COLOR_RED, COLOR_BLACK);
362         break;
363 
364     default:
365         break;
366     }
367     close(fd);
368     if (argv != NULL)
369         free(argv);
370     if (alloc_full_path)
371         free(file_path);
372     return 0;
373 }
374 
375 /**
376  * @brief 创建文件夹的命令
377  *
378  * @param argc
379  * @param argv
380  * @return int
381  */
shell_cmd_mkdir(int argc,char ** argv)382 int shell_cmd_mkdir(int argc, char **argv)
383 {
384     int result_path_len = -1;
385     char *full_path = NULL;
386     bool alloc_full_path = false;
387     if (argv[1][0] == '/')
388         full_path = argv[1];
389     else
390     {
391         full_path = get_target_filepath(argv[1], &result_path_len);
392         alloc_full_path = true;
393     }
394     // printf("mkdir: full_path = %s\n", full_path);
395     int retval = mkdir(full_path, 0);
396 
397     if (argv != NULL)
398         free(argv);
399     if (alloc_full_path)
400         free(full_path);
401     return retval;
402 }
403 
404 /**
405  * @brief 删除文件夹的命令
406  *
407  * @param argc
408  * @param argv
409  * @return int
410  */
shell_cmd_rmdir(int argc,char ** argv)411 int shell_cmd_rmdir(int argc, char **argv)
412 {
413     char *full_path = NULL;
414     int result_path_len = -1;
415     bool alloc_full_path = false;
416 
417     if (argv[1][0] == '/')
418         full_path = argv[1];
419     else
420     {
421         full_path = get_target_filepath(argv[1], &result_path_len);
422         alloc_full_path = true;
423     }
424     int retval = rmdir(full_path);
425     if (retval != 0)
426         printf("Failed to remove %s, retval=%d\n", full_path, retval);
427     // printf("rmdir: path=%s, retval=%d\n", full_path, retval);
428     if (argv != NULL)
429         free(argv);
430     if (alloc_full_path)
431         free(full_path);
432     return retval;
433 }
434 
435 /**
436  * @brief 删除文件的命令
437  *
438  * @param argc
439  * @param argv
440  * @return int
441  */
shell_cmd_rm(int argc,char ** argv)442 int shell_cmd_rm(int argc, char **argv)
443 {
444     char *full_path = NULL;
445     int result_path_len = -1;
446     int retval = 0;
447     bool alloc_full_path = false;
448 
449     if (argv[1][0] == '/')
450         full_path = argv[1];
451     else
452     {
453         full_path = get_target_filepath(argv[1], &result_path_len);
454         alloc_full_path = true;
455     }
456 
457     retval = rm(full_path);
458     // printf("rmdir: path=%s, retval=%d\n", full_path, retval);
459     if (retval != 0)
460         printf("Failed to remove %s, retval=%d\n", full_path, retval);
461     if (alloc_full_path)
462         free(full_path);
463     if (argv != NULL)
464         free(argv);
465     return retval;
466 }
467 
468 /**
469  * @brief 执行新的程序的命令
470  *
471  * @param argc
472  * @param argv
473  * @return int
474  */
shell_cmd_exec(int argc,char ** argv)475 int shell_cmd_exec(int argc, char **argv)
476 {
477     pid_t pid = fork();
478     int retval = 0;
479     // printf("  pid=%d  \n",pid);
480 
481     if (pid == 0)
482     {
483 
484         // 子进程
485         int path_len = 0;
486         char *file_path = get_target_filepath(argv[1], &path_len);
487         // printf("before execv, path=%s, argc=%d\n", file_path, argc);
488         execv(file_path, argv);
489         free(argv);
490         free(file_path);
491 
492         exit(-1);
493     }
494     else
495     {
496         // 如果不指定后台运行,则等待退出
497         if (strcmp(argv[argc - 1], "&") != 0)
498             waitpid(pid, &retval, 0);
499         else
500         {
501             // 输出子进程的pid
502             printf("[1] %d\n", pid);
503         }
504         free(argv);
505     }
506 }
507 
shell_cmd_about(int argc,char ** argv)508 int shell_cmd_about(int argc, char **argv)
509 {
510     if (argv != NULL)
511         free(argv);
512     int aac = 0;
513     char **aav;
514 
515     unsigned char input_buffer[INPUT_BUFFER_SIZE] = {0};
516 
517     strcpy(input_buffer, "exec /bin/about.elf\0");
518 
519     parse_command(input_buffer, &aac, &aav);
520 
521     return shell_cmd_exec(aac, aav);
522 }
523 
shell_cmd_kill(int argc,char ** argv)524 int shell_cmd_kill(int argc, char **argv)
525 {
526     int retval = 0;
527     if (argc < 2)
528     {
529         printf("Usage: Kill <pid>\n");
530         retval = -EINVAL;
531         goto out;
532     }
533     retval = kill(atoi(argv[1]), SIGKILL);
534 out:;
535     free(argv);
536     return retval;
537 }
538 
539 /**
540  * @brief 重启命令
541  *
542  * @param argc
543  * @param argv
544  * @return int
545  */
shell_cmd_reboot(int argc,char ** argv)546 int shell_cmd_reboot(int argc, char **argv)
547 {
548     return syscall_invoke(SYS_REBOOT, 0, 0, 0, 0, 0, 0, 0, 0);
549 }
550 
shell_cmd_free(int argc,char ** argv)551 int shell_cmd_free(int argc, char **argv)
552 {
553     int retval = 0;
554     if (argc == 2 && strcmp("-m", argv[1]) != 0)
555     {
556         retval = -EINVAL;
557         printf("Invalid argument: %s\n", argv[1]);
558         goto done;
559     }
560 
561     struct mstat_t mst = {0};
562     retval = mstat(&mst);
563     if (retval != 0)
564     {
565         printf("Failed: retval=%d", retval);
566         goto done;
567     }
568 
569     printf("\ttotal\tused\tfree\tshared\tcache\tavailable\n");
570     printf("Mem:\t");
571     if (argc == 1) // 按照kb显示
572     {
573         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,
574                mst.cache_used >> 10, mst.available >> 10);
575     }
576     else // 按照MB显示
577     {
578         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,
579                mst.cache_used >> 20, mst.available >> 20);
580     }
581 
582 done:;
583     if (argv != NULL)
584         free(argv);
585     return retval;
586 }
587 
588 /**
589  * @brief 解析shell命令
590  *
591  * @param buf 输入缓冲区
592  * @param argc 返回值:参数数量
593  * @param argv 返回值:参数列表
594  * @return int 主命令的编号
595  */
parse_command(char * buf,int * argc,char *** argv)596 int parse_command(char *buf, int *argc, char ***argv)
597 {
598     // printf("parse command\n");
599     int index = 0; // 当前访问的是buf的第几位
600     // 去除命令前导的空格
601     while (index < INPUT_BUFFER_SIZE && buf[index] == ' ')
602         ++index;
603 
604     // 计算参数数量
605     for (int i = index; i < (INPUT_BUFFER_SIZE - 1); ++i)
606     {
607         // 到达了字符串末尾
608         if (!buf[i])
609             break;
610         if (buf[i] != ' ' && (buf[i + 1] == ' ' || buf[i + 1] == '\0'))
611             ++(*argc);
612     }
613 
614     // printf("\nargc=%d\n", *argc);
615 
616     // 为指向每个指令的指针分配空间
617     *argv = (char **)malloc(sizeof(char **) * (*argc));
618     memset(*argv, 0, sizeof(char **) * (*argc));
619     // 将每个命令都单独提取出来
620     for (int i = 0; i < *argc && index < INPUT_BUFFER_SIZE; ++i)
621     {
622         // 提取出命令,以空格作为分割
623         *((*argv) + i) = &buf[index];
624         while (index < (INPUT_BUFFER_SIZE - 1) && buf[index] && buf[index] != ' ')
625             ++index;
626         buf[index++] = '\0';
627 
628         // 删除命令间多余的空格
629         while (index < INPUT_BUFFER_SIZE && buf[index] == ' ')
630             ++index;
631 
632         // printf("%s\n", (*argv)[i]);
633     }
634     // 以第一个命令作为主命令,查找其在命令表中的编号
635     return shell_find_cmd(**argv);
636 }