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