OS2021_Project1.Shell
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

662 行
13 KiB

3 年前
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. #include <sys/stat.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. #include <dirent.h>
  9. #include <signal.h>
  10. #include <errno.h>
  11. #include <fcntl.h>
  12. #include "yeeshell.h"
  13. /* record cmdline history */
  14. char *history[CMDLINE_HISTORY_MAX_QUANTITY];
  15. int cmdline_amount = 0;
  16. /* if true, print additional output */
  17. int verbose = 0;
  18. /* next job ID to allocate */
  19. int nextjid = 1;
  20. /* environment variable */
  21. extern char **environ;
  22. /* The job list */
  23. struct job_t jobs[JOBS_MAX_QUANTITY];
  24. int main()
  25. {
  26. char *cmdline = NULL, *pwd = NULL;
  27. char *args[ARGS_MAX_QUANTITY];
  28. int status = 1;
  29. pwd = (char *)calloc(PATH_MAX_SIZE, sizeof(char));
  30. for (int i = 0; i < CMDLINE_HISTORY_MAX_QUANTITY; i++)
  31. {
  32. history[i] = (char *)calloc(CMDLINE_MAX_SIZE, sizeof(char));
  33. }
  34. /* Install the signal handlers */
  35. Signal(SIGINT, sigint_handler); /* ctrl-c */
  36. Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */
  37. Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
  38. Signal(SIGQUIT, sigquit_handler);
  39. /* initiate job list */
  40. initjobs(jobs);
  41. /* execute the shell's read, parse and execution loop */
  42. do
  43. {
  44. if (!getcwd(pwd, PATH_MAX_SIZE))
  45. {
  46. printf("yeeshell: The current path cannot be obtained!\n");
  47. exit(0);
  48. }
  49. printf("[root@yeeshell %s]# ", pwd);
  50. cmdline = readline();
  51. strcpy(history[cmdline_amount++], cmdline);
  52. status = execute(cmdline, args);
  53. free(cmdline);
  54. } while (status);
  55. for (int i = 0; i < CMDLINE_HISTORY_MAX_QUANTITY; i++)
  56. {
  57. free(history[i]);
  58. }
  59. exit(EXIT_SUCCESS);
  60. }
  61. char *readline()
  62. {
  63. char *cmdline = NULL;
  64. ssize_t bufsize = 0;
  65. getline(&cmdline, &bufsize, stdin);
  66. return cmdline;
  67. }
  68. int parseline(char *cmdline, char **args)
  69. {
  70. static char array[CMDLINE_MAX_SIZE]; /* holds local copy of command line */
  71. char *buf = array; /* ptr that traverses command line */
  72. char *delim; /* points to first space delimiter */
  73. int argc; /* number of args */
  74. int bg; /* background job? */
  75. strcpy(buf, cmdline);
  76. buf[strlen(buf) - 1] = ' '; /* replace trailing '\n' with space */
  77. while (*buf && (*buf == ' ')) /* ignore leading spaces */
  78. {
  79. buf++;
  80. }
  81. /* Build the argv list */
  82. argc = 0;
  83. if (*buf == '\'')
  84. {
  85. buf++;
  86. delim = strchr(buf, '\'');
  87. }
  88. else
  89. {
  90. delim = strchr(buf, ' ');
  91. }
  92. while (delim)
  93. {
  94. args[argc++] = buf;
  95. *delim = '\0';
  96. buf = delim + 1;
  97. while (*buf && (*buf == ' ')) /* ignore spaces */
  98. {
  99. buf++;
  100. }
  101. if (*buf == '\'')
  102. {
  103. buf++;
  104. delim = strchr(buf, '\'');
  105. }
  106. else
  107. {
  108. delim = strchr(buf, ' ');
  109. }
  110. }
  111. args[argc] = NULL;
  112. if (argc == 0) /* ignore blank line */
  113. {
  114. return 1;
  115. }
  116. /* should the job run in the background? */
  117. if ((bg = (*args[argc - 1] == '&')) != 0)
  118. {
  119. args[--argc] = NULL;
  120. }
  121. return bg;
  122. }
  123. int check_redirect(char **args, char *redirect_filename, char **redirect_args)
  124. {
  125. int i = 0, j = 0, redirect_flag = REDIRECT_NO;
  126. while (args[i] != NULL)
  127. {
  128. if (!strcmp(args[i], ">"))
  129. {
  130. redirect_flag = REDIRECT_OUT;
  131. break;
  132. }
  133. else if (!strcmp(args[i], "<"))
  134. {
  135. redirect_flag = REDIRECT_IN;
  136. break;
  137. }
  138. i++;
  139. }
  140. if (redirect_flag == 1) /* redirect output */
  141. {
  142. strcpy(redirect_filename, args[i + 1]);
  143. for (j = 0; j < i; j++)
  144. {
  145. redirect_args[j] = args[j];
  146. }
  147. }
  148. else if (redirect_flag == 2) /* redirect input */
  149. {
  150. redirect_filename = args[0];
  151. i++;
  152. while (args[i] != NULL)
  153. {
  154. redirect_args[j++] = args[i++];
  155. }
  156. }
  157. return redirect_flag;
  158. }
  159. int execute(char *cmdline, char **args)
  160. {
  161. int bg = 0, i = 0, redirect_flag = 0, fd = 1;
  162. pid_t pid;
  163. char *redirect_filename = NULL;
  164. redirect_filename = (char *)calloc(32, sizeof(char));
  165. char *redirect_args[ARGS_MAX_QUANTITY];
  166. sigset_t mask_all, mask_prev;
  167. sigprocmask(SIG_BLOCK, NULL, &mask_all);
  168. sigaddset(&mask_all, SIGCHLD);
  169. bg = parseline(cmdline, args);
  170. redirect_flag = check_redirect(args, redirect_filename, redirect_args);
  171. if (args[0] == NULL)
  172. {
  173. return 1;
  174. }
  175. if (!built_in(args))
  176. {
  177. /* Prevent child processes from ending between parent processes, that is, between addJob and deleteJob. */
  178. sigprocmask(SIG_BLOCK, &mask_all, &mask_prev); /* Shield SIGCHLD */
  179. if ((pid = fork()) == 0) /* Child process */
  180. {
  181. if (redirect_flag == 1)
  182. {
  183. fd = open(redirect_filename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
  184. close(1);
  185. dup2(1, fd);
  186. }
  187. else if (redirect_flag == 2)
  188. {
  189. fd = open(redirect_filename, O_RDONLY, S_IRUSR);
  190. close(0);
  191. dup2(0, fd);
  192. }
  193. /* The child process inherits the parent's signal mask and will inherit it after exec,
  194. so it needs to restore the signal mask before executing. */
  195. sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* Child process, unblock SIGCHLD */
  196. /* Set the pid of the current process to the group number of the process group it belongs to. */
  197. /* avoid being grouped with tsh */
  198. setpgid(0, 0);
  199. if (execvp(args[0], args) <= 0)
  200. {
  201. printf("%s: Command not found\n", args[0]);
  202. free(redirect_filename);
  203. free(redirect_args);
  204. exit(0);
  205. }
  206. if ((redirect_flag == 1) || (redirect_flag == 2))
  207. {
  208. close(fd);
  209. }
  210. }
  211. else
  212. {
  213. if (bg) /* bg task */
  214. {
  215. addjob(jobs, pid, BG, cmdline);
  216. }
  217. else /* fg task */
  218. {
  219. addjob(jobs, pid, FG, cmdline);
  220. }
  221. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  222. if (bg) /* Don't wait for background tasks to finish */
  223. {
  224. printf("[%d](%d)%s", pid2jid(pid), pid, cmdline);
  225. }
  226. else /* Wait for foreground tasks to finish */
  227. {
  228. waitfg(pid);
  229. }
  230. }
  231. }
  232. return 1;
  233. }
  234. int built_in(char **args)
  235. {
  236. if (!strcmp(args[0], "exit"))
  237. {
  238. exit(0);
  239. }
  240. else if (!strcmp(args[0], "cd"))
  241. {
  242. return builtin_cd(args);
  243. }
  244. else if (!strcmp(args[0], "history"))
  245. {
  246. return builtin_history(args);
  247. }
  248. else if (!strcmp(args[0], "mytop"))
  249. {
  250. return builtin_mytop(args);
  251. }
  252. else if (!strcmp(args[0], "jobs"))
  253. {
  254. return builtin_jobs(args);
  255. }
  256. else
  257. {
  258. return 0;
  259. }
  260. }
  261. int builtin_cd(char **args)
  262. {
  263. if (args[1] == NULL)
  264. {
  265. return 1;
  266. }
  267. else
  268. {
  269. if (chdir(args[1]) != 0)
  270. {
  271. perror("yeeshell");
  272. }
  273. return 1;
  274. }
  275. }
  276. int builtin_history(char **args)
  277. {
  278. int n = 0;
  279. if (args[1] == NULL)
  280. {
  281. n = cmdline_amount;
  282. }
  283. else
  284. {
  285. n = atoi(args[1]) < cmdline_amount ? atoi(args[1]) : cmdline_amount;
  286. }
  287. printf("ID\tCommandline\n");
  288. for (int i = 0; i < n; i++)
  289. {
  290. printf("%d\t%s\n", i + 1, history[i]);
  291. }
  292. return 1;
  293. }
  294. int builtin_jobs(char **args)
  295. {
  296. /* To prevent interruptions, block all signals. */
  297. sigset_t mask_all, mask_prev;
  298. sigfillset(&mask_all);
  299. sigprocmask(SIG_SETMASK, &mask_all, &mask_prev);
  300. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  301. {
  302. if (jobs[i].pid != 0)
  303. {
  304. printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
  305. switch (jobs[i].state)
  306. {
  307. case BG:
  308. printf("Running ");
  309. break;
  310. case FG:
  311. printf("Foreground ");
  312. break;
  313. case ST:
  314. printf("Stopped ");
  315. break;
  316. default:
  317. printf("listjobs: Internal error: job[%d].state=%d ",
  318. i, jobs[i].state);
  319. }
  320. printf("%s", jobs[i].cmdline);
  321. }
  322. }
  323. sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* unclock */
  324. return 1;
  325. }
  326. int builtin_mytop(char **args)
  327. {
  328. }
  329. int do_bgfg(char **args)
  330. {
  331. /* initialize variables */
  332. struct job_t *currentJob;
  333. int jid;
  334. pid_t pid;
  335. sigset_t mask_all, mask_prev;
  336. sigfillset(&mask_all);
  337. sigprocmask(SIG_SETMASK, &mask_all, &mask_prev);
  338. /* bg or fg has the argument? */
  339. if (args[1] == NULL)
  340. {
  341. printf("%s command requires PID or %%jobid argument\n", args[0]);
  342. return 1;
  343. }
  344. /* if process by jid, gets the corresponding Job structure*/
  345. else if (args[1][0] == '%')
  346. {
  347. jid = atoi(args[1][1]);
  348. currentJob = getjobjid(jobs, jid);
  349. if (currentJob == NULL)
  350. {
  351. printf("%%%d: No such job\n", jid);
  352. return 1;
  353. }
  354. pid = currentJob->pid;
  355. }
  356. /* if process by pid, gets the corresponding Job structure */
  357. else
  358. {
  359. pid = atoi(args[1]);
  360. currentJob = getjobpid(jobs, pid);
  361. if (pid <= 0)
  362. {
  363. printf("%s: argument must be a PID or %%jobid\n", args[0]);
  364. return 1;
  365. }
  366. currentJob = getjobpid(jobs, pid);
  367. if (currentJob == NULL)
  368. {
  369. printf("(%d): No such process\n", pid);
  370. return 1;
  371. }
  372. }
  373. /* bg or fg? */
  374. if (!strcmp(args[0], "bg")) /* if bg */
  375. {
  376. currentJob->state = BG;
  377. printf("[%d] (%d) %s", currentJob->jid, pid, currentJob->cmdline);
  378. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  379. kill(-pid, SIGCONT); /* send the SIGCONT to the pid */
  380. return 1;
  381. }
  382. else if (!strcmp(args[0], "fg")) /* if fg */
  383. {
  384. currentJob->state = FG;
  385. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  386. kill(-pid, SIGCONT); /* send the SIGCONT to the pid */
  387. waitfg(currentJob->pid); /* Child process switched to FG, so wait for it to finish */
  388. return 1;
  389. }
  390. else if (!strcmp(args[0], "kill"))
  391. {
  392. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  393. kill(-pid, SIGQUIT);
  394. return 1;
  395. }
  396. return 1;
  397. }
  398. void waitfg(pid_t pid)
  399. {
  400. sigset_t m;
  401. sigemptyset(&m);
  402. while (pid == fgpid(jobs))
  403. {
  404. /* Wake up when there is a signal to check whether the foreground process PID change, */
  405. /* change means that the foreground process is over. */
  406. sigsuspend(&m);
  407. }
  408. return;
  409. }
  410. void initjobs(struct job_t *jobs)
  411. {
  412. int i;
  413. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  414. clearjob(&jobs[i]);
  415. }
  416. int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
  417. {
  418. int i;
  419. if (pid < 1)
  420. return 0;
  421. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  422. {
  423. if (jobs[i].pid == 0)
  424. {
  425. jobs[i].pid = pid;
  426. jobs[i].state = state;
  427. jobs[i].jid = nextjid++;
  428. if (nextjid > JOBS_MAX_QUANTITY)
  429. nextjid = 1;
  430. strcpy(jobs[i].cmdline, cmdline);
  431. if (verbose)
  432. {
  433. printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
  434. }
  435. return 1;
  436. }
  437. }
  438. printf("Tried to create too many jobs\n");
  439. return 0;
  440. }
  441. void listjobs(struct job_t *jobs)
  442. {
  443. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  444. {
  445. if (jobs[i].pid != 0)
  446. {
  447. printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
  448. switch (jobs[i].state)
  449. {
  450. case BG:
  451. printf("Running ");
  452. break;
  453. case FG:
  454. printf("Foreground ");
  455. break;
  456. case ST:
  457. printf("Stopped ");
  458. break;
  459. default:
  460. printf("listjobs: Internal error: job[%d].state=%d ", i, jobs[i].state);
  461. }
  462. printf("%s", jobs[i].cmdline);
  463. }
  464. }
  465. }
  466. int deletejob(struct job_t *jobs, pid_t pid)
  467. {
  468. int i;
  469. if (pid < 1)
  470. return 0;
  471. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  472. {
  473. if (jobs[i].pid == pid)
  474. {
  475. clearjob(&jobs[i]);
  476. nextjid = maxjid(jobs) + 1;
  477. return 1;
  478. }
  479. }
  480. return 0;
  481. }
  482. void clearjob(struct job_t *job)
  483. {
  484. job->pid = 0;
  485. job->jid = 0;
  486. job->state = UNDF;
  487. job->cmdline[0] = '\0';
  488. }
  489. int pid2jid(pid_t pid)
  490. {
  491. int i;
  492. if (pid < 1)
  493. return 0;
  494. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  495. if (jobs[i].pid == pid)
  496. {
  497. return jobs[i].jid;
  498. }
  499. return 0;
  500. }
  501. pid_t fgpid(struct job_t *jobs)
  502. {
  503. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  504. if (jobs[i].state == FG)
  505. return jobs[i].pid;
  506. return 0;
  507. }
  508. struct job_t *getjobpid(struct job_t *jobs, pid_t pid)
  509. {
  510. int i;
  511. if (pid < 1)
  512. return NULL;
  513. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  514. if (jobs[i].pid == pid)
  515. return &jobs[i];
  516. return NULL;
  517. }
  518. struct job_t *getjobjid(struct job_t *jobs, int jid)
  519. {
  520. int i;
  521. if (jid < 1)
  522. return NULL;
  523. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  524. if (jobs[i].jid == jid)
  525. return &jobs[i];
  526. return NULL;
  527. }
  528. int maxjid(struct job_t *jobs)
  529. {
  530. int i, max = 0;
  531. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  532. {
  533. if (jobs[i].jid > max)
  534. {
  535. max = jobs[i].jid;
  536. }
  537. }
  538. return max;
  539. }
  540. handler_t *Signal(int signum, handler_t *handler)
  541. {
  542. struct sigaction action, old_action;
  543. action.sa_handler = handler;
  544. sigemptyset(&action.sa_mask); /* block sigs of type being handled */
  545. action.sa_flags = SA_RESTART; /* restart syscalls if possible */
  546. if (sigaction(signum, &action, &old_action) < 0)
  547. fprintf(stdout, "%s: %s\n", 'Signal Error', strerror(errno));
  548. return (old_action.sa_handler);
  549. }
  550. void sigchld_handler(int signal)
  551. {
  552. pid_t pid;
  553. int status;
  554. while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
  555. {
  556. if (WIFEXITED(status)) /* Normal termination */
  557. {
  558. deletejob(jobs, pid);
  559. }
  560. if (WIFSTOPPED(status)) /* Task suspension */
  561. {
  562. struct job_t *job = getjobpid(jobs, pid);
  563. int jid = pid2jid(pid);
  564. printf("Job [%d] (%d) stopped by signal %d\n", jid, pid, WSTOPSIG(status));
  565. job->state = ST;
  566. }
  567. if (WIFSIGNALED(status)) /* Task terminated */
  568. {
  569. int jid = pid2jid(pid);
  570. printf("Job [%d] (%d) terminated by signal %d\n", jid, pid, WTERMSIG(status));
  571. deletejob(jobs, pid);
  572. }
  573. }
  574. return;
  575. }
  576. void sigint_handler(int signal)
  577. {
  578. pid_t pid = fgpid(jobs);
  579. if (pid != 0)
  580. {
  581. kill(-pid, signal);
  582. }
  583. return;
  584. }
  585. void sigtstp_handler(int signal)
  586. {
  587. pid_t pid = fgpid(jobs);
  588. if (pid > 0)
  589. {
  590. kill(-pid, signal);
  591. }
  592. return;
  593. }
  594. void sigquit_handler(int signal)
  595. {
  596. printf("Terminating after receipt of SIGQUIT signal\n");
  597. exit(1);
  598. }