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 }