1 #include "cmd.h"
2 #include "cmd_help.h"
3 #include "cmd_test.h"
4 #include <libc/src/dirent.h>
5 #include <libc/src/errno.h>
6 #include <libc/src/fcntl.h>
7 #include <libc/src/include/signal.h>
8 #include <libc/src/stddef.h>
9 #include <libc/src/stdio.h>
10 #include <libc/src/stdlib.h>
11 #include <libc/src/string.h>
12 #include <libc/src/sys/stat.h>
13 #include <libc/src/sys/wait.h>
14 #include <libc/src/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 = syscall_invoke(SYS_KILL, atoi(argv[1]), SIGKILL, 0, 0, 0, 0, 0, 0);
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 }