OS2021_Project1.Shell
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

742 řádky
15 KiB

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