Advertisement
Guest User

Untitled

a guest
Oct 16th, 2019
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.30 KB | None | 0 0
  1. //
  2. // main.c
  3. // Shell
  4. //
  5. // Created by Zeyad Osama on 2019-10-12.
  6. // Copyright © 2019 Zeyad Osama. All rights reserved.
  7. //
  8.  
  9. #include <unistd.h>
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <limits.h>
  14. #include <time.h>
  15.  
  16. /**
  17. * definitions.
  18. */
  19. #define BUFFER_SIZE 256
  20. #define TOKEN_BUFFER_SIZE 64
  21. #define TOKEN_DELIMITERS " \t\r\n\a"
  22.  
  23. typedef enum {
  24. false = 0, true = 1
  25. } bool;
  26.  
  27.  
  28. /**
  29. * constants.
  30. */
  31. char *SHELL_TITLE = "Shell > ";
  32. char *ERROR = "error";
  33.  
  34. /**
  35. * current working directory path for logging usages.
  36. */
  37. char cwd[PATH_MAX];
  38.  
  39. /**
  40. * @return number of pre-implemented commands.
  41. */
  42. int get_commands_num();
  43.  
  44. /**
  45. * @brief reads a line from the terminal.
  46. * @return read line as string.
  47. */
  48. char *read_string(void);
  49.  
  50. /**
  51. * @brief split a string into tokens.
  52. * @param string to be splitted.
  53. * @return tokens.
  54. */
  55. char **tokenize_string(char *string);
  56.  
  57. /**
  58. * @brief checks if a given line has '&' at it's end.
  59. * @param tokens to be checked.
  60. * @return boolean upon the check.
  61. */
  62. bool has_ampersand(char **tokens);
  63.  
  64. /**
  65. * @param tokens to know it's length.
  66. * @return int value representing length.
  67. */
  68. int get_tokens_length(char **tokens);
  69.  
  70. /**
  71. * @param args list of args.
  72. * args[0] "cd".
  73. * args[1] directory to be changed to.
  74. * @return running status, 1 to continue executing.
  75. */
  76. int command_cd(char **args);
  77.  
  78. /**
  79. * @param args list of args.
  80. * @return running status, 0 to terminate execution.
  81. */
  82. int command_exit(char **args);
  83.  
  84. /**
  85. * @brief Launch a program and wait for it to terminate.
  86. * @param args Null terminated list of arguments (including program).
  87. * @return Always returns 1, to continue execution.
  88. */
  89. int execute(char **args, bool background);
  90.  
  91. /**
  92. * @brief Execute shell built-in or launch program.
  93. * @param args Null terminated list of arguments.
  94. * @return 1 if the shell should continue running, 0 if it should terminate
  95. */
  96. int detect_command(char **args, bool background);
  97.  
  98. /**
  99. * @brief constantly running the shell.
  100. */
  101. void run(void);
  102.  
  103. /**
  104. * @brief logs the process id and it's exit status in .txt file included in the same directory.
  105. * @param pid process id.
  106. * @param status exit status.
  107. * @return boolean upon file writing success.
  108. */
  109. bool write_to_file(pid_t pid, int status);
  110.  
  111. /**
  112. *
  113. * @param buffer to be checked if allocated.
  114. * @return boolean upon the check.
  115. */
  116. bool allocation_check(const char *buffer);
  117.  
  118. /**
  119. * @brief commands string.
  120. */
  121. char *commands_name[] = {
  122. "exit",
  123. "cd"
  124. };
  125.  
  126. /**
  127. * @brief commands function.
  128. */
  129. int (*commands_function[])(char **args) = {
  130. &command_exit,
  131. &command_cd
  132. };
  133.  
  134. /**
  135. * @return program exit status.
  136. */
  137. int main(int argc, char *argv[]) {
  138. // store program start working directory for any further need.
  139. if (getcwd(cwd, sizeof(cwd)) == NULL) {
  140. perror(ERROR);
  141. return EXIT_FAILURE;
  142. }
  143. // program start.
  144. run();
  145. // program terminated successfully.
  146. return EXIT_SUCCESS;
  147. }
  148.  
  149. char *read_string(void) {
  150. // creating a buffer for storing terminal line.
  151. // setting buffer size to be allocated.
  152. size_t buffer_size = BUFFER_SIZE;
  153. // allocating memory for buffer.
  154. char *buffer = malloc(sizeof(char) * buffer_size);
  155.  
  156. // positioning index for assigning chars to their positions in buffer
  157. int index = 0;
  158. // current being read char.
  159. int c;
  160.  
  161. // failure check.
  162. // check if buffer was allocated successfully.
  163. allocation_check(buffer);
  164.  
  165. // read line from terminal until "\n" is encountered.
  166. while (true) {
  167. c = getchar();
  168.  
  169. // generating the buffer iteratively.
  170. if (c == EOF)
  171. exit(EXIT_SUCCESS);
  172. else if (c == '\n') {
  173. buffer[index] = '\0';
  174. return buffer;
  175. } else
  176. buffer[index] = (char) c;
  177. index++;
  178.  
  179. // buffer size exceeded the pre-defined size.
  180. // reallocate.
  181. if (index >= buffer_size) {
  182. buffer_size += BUFFER_SIZE;
  183. buffer = realloc(buffer, buffer_size);
  184.  
  185. // failure check.
  186. // check if buffer was reallocated successfully.
  187. allocation_check(buffer);
  188. }
  189. }
  190. }
  191.  
  192. char **tokenize_string(char *string) {
  193. // creating a buffer for storing tokens.
  194. // setting buffer size to be allocated.
  195. int buffer_size = TOKEN_BUFFER_SIZE;
  196.  
  197. // positioning index for assigning tokens to their positions in buffer
  198. int index = 0;
  199.  
  200. // allocating memory for tokens.
  201. char **tokens = malloc(buffer_size * sizeof(char *));
  202. // pointers for each token string.
  203. char *token, **tokens_backup;
  204.  
  205. // failure check.
  206. // check if buffer was allocated successfully.
  207. allocation_check((const char *) tokens);
  208.  
  209. token = strtok(string, TOKEN_DELIMITERS);
  210. while (token != NULL) {
  211. // assigning tokens.
  212. tokens[index++] = token;
  213.  
  214. // buffer size exceeded the pre-defined size.
  215. // reallocate.
  216. if (index >= buffer_size) {
  217. buffer_size += TOKEN_BUFFER_SIZE;
  218. tokens_backup = tokens;
  219. tokens = realloc(tokens, buffer_size * sizeof(char *));
  220.  
  221. // failure check.
  222. // check if buffer was allocated successfully.
  223. if (!tokens) {
  224. free(tokens_backup);
  225. fprintf(stderr, "%s: allocation error\n", ERROR);
  226. exit(EXIT_FAILURE);
  227. }
  228. }
  229. // tokenize.
  230. token = strtok(NULL, TOKEN_DELIMITERS);
  231. }
  232. // terminating tokens array by NULL value.
  233. tokens[index] = NULL;
  234. return tokens;
  235. }
  236.  
  237. bool has_ampersand(char **tokens) {
  238. return (strcmp(tokens[get_tokens_length(tokens) - 1], "&") == 0);
  239. }
  240.  
  241. int get_tokens_length(char **tokens) {
  242. char **ptr = tokens;
  243. int i = 0;
  244. // iterate on tokens array to get it's length
  245. for (char *c = *ptr; c; c = *++ptr, i++);
  246. return i;
  247. }
  248.  
  249. int get_commands_num() {
  250. return sizeof(commands_name) / sizeof(char *);
  251. }
  252.  
  253. int command_cd(char **args) {
  254. if (args[1] == NULL)
  255. // directory not specified.
  256. fprintf(stderr, "%s: expected argument to \"cd\"\n", ERROR);
  257. else {
  258. if (args[2] != NULL)
  259. // extra non-supported argument.
  260. fprintf(stderr, "%s: too many arguments for \"cd\".\n", ERROR);
  261. else if (chdir(args[1]) != 0)
  262. // directory does not exists.
  263. perror(ERROR);
  264. }
  265. return 1;
  266. }
  267.  
  268. int command_exit(char **args) {
  269. exit(EXIT_SUCCESS);
  270. }
  271.  
  272. int execute(char **args, bool background) {
  273. // initializing signal handler
  274. signal(SIGCHLD, write_to_file);
  275.  
  276. pid_t pid = fork();
  277. int status = 0;
  278.  
  279. // child process.
  280. if (pid == 0) {
  281. if (execvp(args[0], args) == -1)
  282. // call to execvp was unsuccessful.
  283. perror(ERROR);
  284. exit(EXIT_FAILURE);
  285. } else if (pid > 0) {
  286. // wait in case background.
  287. // otherwise continue.
  288. if (!background)
  289. do {
  290. waitpid(pid, &status, WUNTRACED);
  291. } while (!WIFEXITED(status) && !WIFSIGNALED(status));
  292. } else {
  293. // forking error.
  294. perror(ERROR);
  295. }
  296. return 1;
  297. }
  298.  
  299. int detect_command(char **args, bool background) {
  300. // null entry.
  301. // neglect.
  302. if (args[0] == NULL)
  303. return 1;
  304. // loop on predefined functions.
  305. // if one of them is detected, current function calls it .
  306. // otherwise, preform normal executing.
  307. for (int i = 0; i < get_commands_num(); i++)
  308. if (strcmp(args[0], commands_name[i]) == 0)
  309. return (*commands_function[i])(args);
  310. return execute(args, background);
  311. }
  312.  
  313. void run(void) {
  314. // terminal read line.
  315. char *line;
  316. // argument list.
  317. char **args;
  318. // boolean to check running status of shell
  319. int running;
  320. // boolean to check if the next execution is a background or foreground process.
  321. bool background;
  322. do {
  323. // shell title.
  324. printf("%s", SHELL_TITLE);
  325.  
  326. line = read_string();
  327. args = tokenize_string(line);
  328. background = has_ampersand(args);
  329. running = detect_command(args, background);
  330.  
  331. // deallocate.
  332. free(line);
  333. free(args);
  334. } while (running);
  335. }
  336.  
  337. bool write_to_file(pid_t pid, int status) {
  338. FILE *fptr;
  339. char path[PATH_MAX];
  340.  
  341. // defining path.
  342. strcpy(path, cwd);
  343. strcat(path, "/log.txt");
  344.  
  345. // current time.
  346. time_t t = time(NULL);
  347. struct tm tm = *localtime(&t);
  348.  
  349. fptr = fopen(path, "a");
  350.  
  351. // failure check
  352. if (fptr == NULL) {
  353. perror(ERROR);
  354. return false;
  355. }
  356. // write current log in file.
  357. fprintf(fptr, "%04d-%02d-%02d %02d:%02d:%02d "
  358. "shell:: "
  359. "process %d terminated with exit status %d.\n",
  360. tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
  361. pid, status);
  362. fclose(fptr);
  363. return true;
  364. }
  365.  
  366. bool allocation_check(const char *buffer) {
  367. // failure check.
  368. if (!buffer) {
  369. fprintf(stderr, "%s: allocation error\n", ERROR);
  370. // segment fault occurred..
  371. exit(EXIT_FAILURE);
  372. }
  373. return true;
  374. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement