Advertisement
Guest User

Untitled

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