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.

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