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 }