OS2021_Project1.Shell

604 行
12 KiB

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