OS2021_Project1.Shell
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

672 lines
13 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  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. printf("%s %s %s %s", redirect_args[0], redirect_args[1], redirect_args[2], redirect_filename);
  172. if (args[0] == NULL)
  173. {
  174. return 1;
  175. }
  176. if (!built_in(args))
  177. {
  178. /* Prevent child processes from ending between parent processes, that is, between addJob and deleteJob. */
  179. sigprocmask(SIG_BLOCK, &mask_all, &mask_prev); /* Shield SIGCHLD */
  180. if ((pid = fork()) == 0) /* Child process */
  181. {
  182. if (redirect_flag == 1)
  183. {
  184. fd = open(redirect_filename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
  185. close(1);
  186. dup(fd);
  187. }
  188. else if (redirect_flag == 2)
  189. {
  190. fd = open(redirect_filename, O_RDONLY, S_IRUSR);
  191. close(0);
  192. dup(fd);
  193. }
  194. /* The child process inherits the parent's signal mask and will inherit it after exec,
  195. so it needs to restore the signal mask before executing. */
  196. sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* Child process, unblock SIGCHLD */
  197. /* Set the pid of the current process to the group number of the process group it belongs to. */
  198. /* avoid being grouped with tsh */
  199. setpgid(0, 0);
  200. if ((redirect_flag == 1) || (redirect_flag == 2))
  201. {
  202. if (execvp(redirect_args[0], redirect_args) <= 0)
  203. {
  204. printf("%s: Command not found\n", args[0]);
  205. exit(0);
  206. }
  207. close(fd);
  208. }
  209. else
  210. {
  211. if (execvp(args[0], args) <= 0)
  212. {
  213. printf("%s: Command not found\n", args[0]);
  214. exit(0);
  215. }
  216. }
  217. }
  218. else
  219. {
  220. if (bg) /* bg task */
  221. {
  222. addjob(jobs, pid, BG, cmdline);
  223. }
  224. else /* fg task */
  225. {
  226. addjob(jobs, pid, FG, cmdline);
  227. }
  228. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  229. if (bg) /* Don't wait for background tasks to finish */
  230. {
  231. printf("[%d](%d)%s", pid2jid(pid), pid, cmdline);
  232. }
  233. else /* Wait for foreground tasks to finish */
  234. {
  235. waitfg(pid);
  236. }
  237. }
  238. }
  239. return 1;
  240. }
  241. int built_in(char **args)
  242. {
  243. if (!strcmp(args[0], "exit"))
  244. {
  245. exit(0);
  246. }
  247. else if (!strcmp(args[0], "cd"))
  248. {
  249. return builtin_cd(args);
  250. }
  251. else if (!strcmp(args[0], "history"))
  252. {
  253. return builtin_history(args);
  254. }
  255. else if (!strcmp(args[0], "mytop"))
  256. {
  257. return builtin_mytop(args);
  258. }
  259. else if (!strcmp(args[0], "jobs"))
  260. {
  261. return builtin_jobs(args);
  262. }
  263. else
  264. {
  265. return 0;
  266. }
  267. }
  268. int builtin_cd(char **args)
  269. {
  270. if (args[1] == NULL)
  271. {
  272. return 1;
  273. }
  274. else
  275. {
  276. if (chdir(args[1]) != 0)
  277. {
  278. perror("yeeshell");
  279. }
  280. return 1;
  281. }
  282. }
  283. int builtin_history(char **args)
  284. {
  285. int n = 0;
  286. if (args[1] == NULL)
  287. {
  288. n = cmdline_amount;
  289. }
  290. else
  291. {
  292. n = atoi(args[1]) < cmdline_amount ? atoi(args[1]) : cmdline_amount;
  293. }
  294. printf("ID\tCommandline\n");
  295. for (int i = 0; i < n; i++)
  296. {
  297. printf("%d\t%s\n", i + 1, history[i]);
  298. }
  299. return 1;
  300. }
  301. int builtin_jobs(char **args)
  302. {
  303. /* To prevent interruptions, block all signals. */
  304. sigset_t mask_all, mask_prev;
  305. sigfillset(&mask_all);
  306. sigprocmask(SIG_SETMASK, &mask_all, &mask_prev);
  307. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  308. {
  309. if (jobs[i].pid != 0)
  310. {
  311. printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
  312. switch (jobs[i].state)
  313. {
  314. case BG:
  315. printf("Running ");
  316. break;
  317. case FG:
  318. printf("Foreground ");
  319. break;
  320. case ST:
  321. printf("Stopped ");
  322. break;
  323. default:
  324. printf("listjobs: Internal error: job[%d].state=%d ",
  325. i, jobs[i].state);
  326. }
  327. printf("%s", jobs[i].cmdline);
  328. }
  329. }
  330. sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* unclock */
  331. return 1;
  332. }
  333. int builtin_mytop(char **args)
  334. {
  335. }
  336. int do_bgfg(char **args)
  337. {
  338. /* initialize variables */
  339. struct job_t *currentJob;
  340. int jid;
  341. pid_t pid;
  342. sigset_t mask_all, mask_prev;
  343. sigfillset(&mask_all);
  344. sigprocmask(SIG_SETMASK, &mask_all, &mask_prev);
  345. /* bg or fg has the argument? */
  346. if (args[1] == NULL)
  347. {
  348. printf("%s command requires PID or %%jobid argument\n", args[0]);
  349. return 1;
  350. }
  351. /* if process by jid, gets the corresponding Job structure*/
  352. else if (args[1][0] == '%')
  353. {
  354. jid = atoi(args[1][1]);
  355. currentJob = getjobjid(jobs, jid);
  356. if (currentJob == NULL)
  357. {
  358. printf("%%%d: No such job\n", jid);
  359. return 1;
  360. }
  361. pid = currentJob->pid;
  362. }
  363. /* if process by pid, gets the corresponding Job structure */
  364. else
  365. {
  366. pid = atoi(args[1]);
  367. currentJob = getjobpid(jobs, pid);
  368. if (pid <= 0)
  369. {
  370. printf("%s: argument must be a PID or %%jobid\n", args[0]);
  371. return 1;
  372. }
  373. currentJob = getjobpid(jobs, pid);
  374. if (currentJob == NULL)
  375. {
  376. printf("(%d): No such process\n", pid);
  377. return 1;
  378. }
  379. }
  380. /* bg or fg? */
  381. if (!strcmp(args[0], "bg")) /* if bg */
  382. {
  383. currentJob->state = BG;
  384. printf("[%d] (%d) %s", currentJob->jid, pid, currentJob->cmdline);
  385. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  386. kill(-pid, SIGCONT); /* send the SIGCONT to the pid */
  387. return 1;
  388. }
  389. else if (!strcmp(args[0], "fg")) /* if fg */
  390. {
  391. currentJob->state = FG;
  392. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  393. kill(-pid, SIGCONT); /* send the SIGCONT to the pid */
  394. waitfg(currentJob->pid); /* Child process switched to FG, so wait for it to finish */
  395. return 1;
  396. }
  397. else if (!strcmp(args[0], "kill"))
  398. {
  399. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  400. kill(-pid, SIGQUIT);
  401. return 1;
  402. }
  403. return 1;
  404. }
  405. void waitfg(pid_t pid)
  406. {
  407. sigset_t m;
  408. sigemptyset(&m);
  409. while (pid == fgpid(jobs))
  410. {
  411. /* Wake up when there is a signal to check whether the foreground process PID change, */
  412. /* change means that the foreground process is over. */
  413. sigsuspend(&m);
  414. }
  415. return;
  416. }
  417. void initjobs(struct job_t *jobs)
  418. {
  419. int i;
  420. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  421. clearjob(&jobs[i]);
  422. }
  423. int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
  424. {
  425. int i;
  426. if (pid < 1)
  427. return 0;
  428. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  429. {
  430. if (jobs[i].pid == 0)
  431. {
  432. jobs[i].pid = pid;
  433. jobs[i].state = state;
  434. jobs[i].jid = nextjid++;
  435. if (nextjid > JOBS_MAX_QUANTITY)
  436. nextjid = 1;
  437. strcpy(jobs[i].cmdline, cmdline);
  438. if (verbose)
  439. {
  440. printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
  441. }
  442. return 1;
  443. }
  444. }
  445. printf("Tried to create too many jobs\n");
  446. return 0;
  447. }
  448. void listjobs(struct job_t *jobs)
  449. {
  450. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  451. {
  452. if (jobs[i].pid != 0)
  453. {
  454. printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
  455. switch (jobs[i].state)
  456. {
  457. case BG:
  458. printf("Running ");
  459. break;
  460. case FG:
  461. printf("Foreground ");
  462. break;
  463. case ST:
  464. printf("Stopped ");
  465. break;
  466. default:
  467. printf("listjobs: Internal error: job[%d].state=%d ", i, jobs[i].state);
  468. }
  469. printf("%s", jobs[i].cmdline);
  470. }
  471. }
  472. }
  473. int deletejob(struct job_t *jobs, pid_t pid)
  474. {
  475. int i;
  476. if (pid < 1)
  477. return 0;
  478. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  479. {
  480. if (jobs[i].pid == pid)
  481. {
  482. clearjob(&jobs[i]);
  483. nextjid = maxjid(jobs) + 1;
  484. return 1;
  485. }
  486. }
  487. return 0;
  488. }
  489. void clearjob(struct job_t *job)
  490. {
  491. job->pid = 0;
  492. job->jid = 0;
  493. job->state = UNDF;
  494. job->cmdline[0] = '\0';
  495. }
  496. int pid2jid(pid_t pid)
  497. {
  498. int i;
  499. if (pid < 1)
  500. return 0;
  501. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  502. if (jobs[i].pid == pid)
  503. {
  504. return jobs[i].jid;
  505. }
  506. return 0;
  507. }
  508. pid_t fgpid(struct job_t *jobs)
  509. {
  510. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  511. if (jobs[i].state == FG)
  512. return jobs[i].pid;
  513. return 0;
  514. }
  515. struct job_t *getjobpid(struct job_t *jobs, pid_t pid)
  516. {
  517. int i;
  518. if (pid < 1)
  519. return NULL;
  520. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  521. if (jobs[i].pid == pid)
  522. return &jobs[i];
  523. return NULL;
  524. }
  525. struct job_t *getjobjid(struct job_t *jobs, int jid)
  526. {
  527. int i;
  528. if (jid < 1)
  529. return NULL;
  530. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  531. if (jobs[i].jid == jid)
  532. return &jobs[i];
  533. return NULL;
  534. }
  535. int maxjid(struct job_t *jobs)
  536. {
  537. int i, max = 0;
  538. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  539. {
  540. if (jobs[i].jid > max)
  541. {
  542. max = jobs[i].jid;
  543. }
  544. }
  545. return max;
  546. }
  547. handler_t *Signal(int signum, handler_t *handler)
  548. {
  549. struct sigaction action, old_action;
  550. action.sa_handler = handler;
  551. sigemptyset(&action.sa_mask); /* block sigs of type being handled */
  552. action.sa_flags = SA_RESTART; /* restart syscalls if possible */
  553. if (sigaction(signum, &action, &old_action) < 0)
  554. fprintf(stdout, "%s: %s\n", 'Signal Error', strerror(errno));
  555. return (old_action.sa_handler);
  556. }
  557. void sigchld_handler(int signal)
  558. {
  559. pid_t pid;
  560. int status;
  561. while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
  562. {
  563. if (WIFEXITED(status)) /* Normal termination */
  564. {
  565. deletejob(jobs, pid);
  566. }
  567. if (WIFSTOPPED(status)) /* Task suspension */
  568. {
  569. struct job_t *job = getjobpid(jobs, pid);
  570. int jid = pid2jid(pid);
  571. printf("Job [%d] (%d) stopped by signal %d\n", jid, pid, WSTOPSIG(status));
  572. job->state = ST;
  573. }
  574. if (WIFSIGNALED(status)) /* Task terminated */
  575. {
  576. int jid = pid2jid(pid);
  577. printf("Job [%d] (%d) terminated by signal %d\n", jid, pid, WTERMSIG(status));
  578. deletejob(jobs, pid);
  579. }
  580. }
  581. return;
  582. }
  583. void sigint_handler(int signal)
  584. {
  585. pid_t pid = fgpid(jobs);
  586. if (pid != 0)
  587. {
  588. kill(-pid, signal);
  589. }
  590. return;
  591. }
  592. void sigtstp_handler(int signal)
  593. {
  594. pid_t pid = fgpid(jobs);
  595. if (pid > 0)
  596. {
  597. kill(-pid, signal);
  598. }
  599. return;
  600. }
  601. void sigquit_handler(int signal)
  602. {
  603. printf("Terminating after receipt of SIGQUIT signal\n");
  604. exit(1);
  605. }