OS2021_Project1.Shell

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