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.

661 lines
13 KiB

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