OS2021_Project1.Shell
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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