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.

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