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.

414 lines
7.8 KiB

3 years ago
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <dirent.h>
  8. #include "yeeshell.h"
  9. /* record cmdline history */
  10. char **history = NULL;
  11. int cmdline_amount = 0;
  12. /* next job ID to allocate */
  13. int nextjid = 1;
  14. /* environment variable */
  15. extern char **environ;
  16. /* The job list */
  17. struct job_t jobs[JOBS_MAX_QUANTITY];
  18. int main()
  19. {
  20. char *cmdline = NULL, *pwd = NULL;
  21. char *args[ARGS_MAX_QUANTITY];
  22. int status = 1;
  23. pwd = (char *)calloc(PATH_MAX, sizeof(char));
  24. if (!(history = (char **)calloc(CMDLINE_HISTORY_MAX_QUANTITY, CMDLINE_MAX_SIZE * sizeof(char *))))
  25. {
  26. printf("yeeshell: allocation error!\n");
  27. exit(EXIT_FAILURE);
  28. }
  29. /* execute the shell's read, parse and execution loop */
  30. do
  31. {
  32. if (!getcwd(pwd, PATH_MAX))
  33. {
  34. printf("yeeshell: The current path cannot be obtained!\n");
  35. exit(0);
  36. }
  37. printf("[yeeshell %s]# ", pwd);
  38. cmdline = readline();
  39. history[cmdline_amount++] = cmdline;
  40. status = execute(cmdline, args);
  41. free(cmdline);
  42. } while (status);
  43. free(history);
  44. exit(EXIT_SUCCESS);
  45. }
  46. char *readline()
  47. {
  48. char *cmdline = NULL;
  49. ssize_t bufsize = 0;
  50. getline(&cmdline, &bufsize, stdin);
  51. return cmdline;
  52. }
  53. int parseline(char *cmdline, char **args)
  54. {
  55. static char array[CMDLINE_MAX_SIZE]; /* holds local copy of command line */
  56. char *buf = array; /* ptr that traverses command line */
  57. char *delim; /* points to first space delimiter */
  58. int argc; /* number of args */
  59. int bg; /* background job? */
  60. strcpy(buf, cmdline);
  61. buf[strlen(buf) - 1] = ' '; /* replace trailing '\n' with space */
  62. while (*buf && (*buf == ' ')) /* ignore leading spaces */
  63. {
  64. buf++;
  65. }
  66. /* Build the argv list */
  67. argc = 0;
  68. if (*buf == '\'')
  69. {
  70. buf++;
  71. delim = strchr(buf, '\'');
  72. }
  73. else
  74. {
  75. delim = strchr(buf, ' ');
  76. }
  77. while (delim)
  78. {
  79. args[argc++] = buf;
  80. *delim = '\0';
  81. buf = delim + 1;
  82. while (*buf && (*buf == ' ')) /* ignore spaces */
  83. {
  84. buf++;
  85. }
  86. if (*buf == '\'')
  87. {
  88. buf++;
  89. delim = strchr(buf, '\'');
  90. }
  91. else
  92. {
  93. delim = strchr(buf, ' ');
  94. }
  95. }
  96. args[argc] = NULL;
  97. if (argc == 0) /* ignore blank line */
  98. {
  99. return 1;
  100. }
  101. /* should the job run in the background? */
  102. if ((bg = (*args[argc - 1] == '&')) != 0)
  103. {
  104. args[--argc] = NULL;
  105. }
  106. return bg;
  107. }
  108. int execute(char *cmdline, char **args)
  109. {
  110. int bg = 0, i = 0;
  111. pid_t pid;
  112. sigset_t mask_all, mask_prev;
  113. sigprocmask(SIG_BLOCK, NULL, &mask_all);
  114. sigaddset(&mask_all, SIGCHLD);
  115. bg = parseline(cmdline, args);
  116. if (args[0] == NULL)
  117. {
  118. return 1;
  119. }
  120. if (!built_in(args))
  121. {
  122. /* Prevent child processes from ending between parent processes, that is, between addJob and deleteJob. */
  123. sigprocmask(SIG_BLOCK, &mask_all, &mask_prev); /* Shield SIGCHLD */
  124. if ((pid = fork()) == 0) /* Child process */
  125. {
  126. /* The child process inherits the parent's signal mask and will inherit it after exec,
  127. so it needs to restore the signal mask before executing. */
  128. sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* Child process, unblock SIGCHLD */
  129. /* Set the pid of the current process to the group number of the process group it belongs to. */
  130. /* avoid being grouped with tsh */
  131. setpgid(0, 0);
  132. if (execve(args[0], args, environ) <= 0)
  133. {
  134. printf("%s: Command not found\n", args[0]);
  135. exit(0);
  136. }
  137. }
  138. else
  139. {
  140. do
  141. {
  142. } while (!args[i++]);
  143. if (bg) /* bg task */
  144. {
  145. addjob(jobs, pid, BG, cmdline);
  146. }
  147. else /* fg task */
  148. {
  149. addjob(jobs, pid, FG, cmdline);
  150. }
  151. sigprocmask(SIG_SETMASK, &mask_prev, NULL);
  152. if (bg) /* Don't wait for background tasks to finish */
  153. {
  154. printf("[%d](%d)%s", pid2jid(pid), pid, cmdline);
  155. }
  156. else /* Wait for foreground tasks to finish */
  157. {
  158. waitfg(pid);
  159. }
  160. }
  161. }
  162. return 1;
  163. }
  164. int built_in(char **args)
  165. {
  166. if (!strcmp(args[0], "exit"))
  167. {
  168. exit(0);
  169. }
  170. else if (!strcmp(args[0], "cd"))
  171. {
  172. return builtin_cd(args);
  173. }
  174. else if (!strcmp(args[0], "history"))
  175. {
  176. return builtin_history(args);
  177. }
  178. else if (!strcmp(args[0], "mytop"))
  179. {
  180. return builtin_mytop(args);
  181. }
  182. else if (!strcmp(args[0], "ls"))
  183. {
  184. return builtin_ls(args);
  185. }
  186. else if (!strcmp(args[0], "jobs"))
  187. {
  188. return builtin_jobs(args);
  189. }
  190. else
  191. {
  192. return -1;
  193. }
  194. }
  195. int builtin_cd(char **args)
  196. {
  197. if (args[1] == NULL)
  198. {
  199. return 1;
  200. }
  201. else
  202. {
  203. if (chdir(args[1]) != 0)
  204. {
  205. perror("yeeshell");
  206. }
  207. return 1;
  208. }
  209. }
  210. int builtin_ls(char **args)
  211. {
  212. DIR *dp;
  213. struct dirent *dirp;
  214. char *path = NULL;
  215. if (args[1] == NULL)
  216. {
  217. path = (char *)calloc(PATH_MAX, sizeof(char));
  218. getcwd(path, PATH_MAX)
  219. }
  220. else if (args[1])
  221. if ((dp = opendir(args[1])) == NULL)
  222. {
  223. perror("yeeshell");
  224. }
  225. while ((dirp = readdir(dp)) != NULL)
  226. {
  227. printf("%s\n", dirp->d_name);
  228. }
  229. closedir(dp);
  230. return 0;
  231. }
  232. int builtin_history(char **args)
  233. {
  234. int n = atoi(args[1]);
  235. printf("ID\tCommandline\n");
  236. for (int i = 0; i < ((n < cmdline_amount) ? n : cmdline_amount); i++)
  237. {
  238. printf("%d\t%s\n", i, history[i]);
  239. }
  240. return 1;
  241. }
  242. int builtin_jobs(char **args)
  243. {
  244. /* To prevent interruptions, block all signals. */
  245. sigset_t mask_all, mask_prev;
  246. sigfillset(&mask_all);
  247. sigprocmask(SIG_SETMASK, &mask_all, &mask_prev);
  248. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  249. {
  250. if (jobs[i].pid != 0)
  251. {
  252. printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
  253. switch (jobs[i].state)
  254. {
  255. case BG:
  256. printf("Running ");
  257. break;
  258. case FG:
  259. printf("Foreground ");
  260. break;
  261. case ST:
  262. printf("Stopped ");
  263. break;
  264. default:
  265. printf("listjobs: Internal error: job[%d].state=%d ",
  266. i, jobs[i].state);
  267. }
  268. printf("%s", jobs[i].cmdline);
  269. }
  270. }
  271. sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* unclock */
  272. return 1;
  273. }
  274. int builtin_mytop(char **args)
  275. {
  276. }
  277. int do_bgfg(char **args)
  278. {
  279. }
  280. /************************
  281. * manipulate job list
  282. ************************/
  283. int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
  284. {
  285. int i;
  286. if (pid < 1)
  287. return 0;
  288. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  289. {
  290. if (jobs[i].pid == 0)
  291. {
  292. jobs[i].pid = pid;
  293. jobs[i].state = state;
  294. jobs[i].jid = nextjid++;
  295. if (nextjid > JOBS_MAX_QUANTITY)
  296. nextjid = 1;
  297. strcpy(jobs[i].cmdline, cmdline);
  298. printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
  299. return 1;
  300. }
  301. }
  302. printf("Tried to create too many jobs\n");
  303. return 0;
  304. }
  305. int pid2jid(pid_t pid)
  306. {
  307. int i;
  308. if (pid < 1)
  309. return 0;
  310. for (i = 0; i < JOBS_MAX_QUANTITY; i++)
  311. if (jobs[i].pid == pid)
  312. {
  313. return jobs[i].jid;
  314. }
  315. return 0;
  316. }
  317. void listjobs(struct job_t *jobs)
  318. {
  319. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  320. {
  321. if (jobs[i].pid != 0)
  322. {
  323. printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
  324. switch (jobs[i].state)
  325. {
  326. case BG:
  327. printf("Running ");
  328. break;
  329. case FG:
  330. printf("Foreground ");
  331. break;
  332. case ST:
  333. printf("Stopped ");
  334. break;
  335. default:
  336. printf("listjobs: Internal error: job[%d].state=%d ", i, jobs[i].state);
  337. }
  338. printf("%s", jobs[i].cmdline);
  339. }
  340. }
  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. pid_t fgpid(struct job_t *jobs)
  355. {
  356. for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
  357. if (jobs[i].state == FG)
  358. return jobs[i].pid;
  359. return 0;
  360. }
  361. int hide(const char *path)
  362. {
  363. if (*path == '.')
  364. {
  365. return 1;
  366. }
  367. else
  368. {
  369. return 0;
  370. }
  371. }