Advertisement
Guest User

Untitled

a guest
Jan 23rd, 2018
105
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.41 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6.  
  7. #include <sys/resource.h>
  8. #include <sys/time.h>
  9. #include <sys/types.h>
  10. #include <sys/wait.h>
  11.  
  12. #include <readline/history.h>
  13. #include <readline/readline.h>
  14.  
  15. #define LIST_BLOCK_SZ 4
  16. #define MAX_ARGS 31
  17. #define MAX_USER_COMMANDS 32
  18. #define MAX_BG_CMDS 128
  19.  
  20. typedef char **Command;
  21.  
  22. struct backgroundCommand {
  23.   pid_t pid;
  24.   Command cmd;
  25.  
  26.   struct backgroundCommand *last;
  27.   struct backgroundCommand *next;
  28. };
  29.  
  30. struct backgroundCommand *createBgCommand(pid_t pid, Command cmd) {
  31.   struct backgroundCommand *newCmd = (struct backgroundCommand *)malloc(sizeof(struct backgroundCommand));
  32.  
  33.   if (newCmd == NULL) {
  34.     perror("Error allocating background command.");
  35.     exit(1);
  36.   }
  37.  
  38.   newCmd->pid = pid;
  39.   newCmd->cmd = cmd;
  40.  
  41.   newCmd->next = NULL;
  42.   newCmd->last = NULL;
  43.  
  44.   return newCmd;
  45. }
  46.  
  47. void appendBgCommand(pid_t pid, Command cmd, struct backgroundCommand *root) {
  48.   // Find tail of linked list
  49.   while (root->next != NULL) {
  50.     root = root->next;
  51.   }
  52.  
  53.   struct backgroundCommand *newCmd = createBgCommand(pid, cmd);
  54.  
  55.   root->next = newCmd;
  56.   newCmd->last = root;
  57. }
  58.  
  59. struct backgroundCommand *removeBgCommand(struct backgroundCommand *cmd) {
  60.   struct backgroundCommand *last = cmd->last;
  61.   struct backgroundCommand *next = cmd->next;
  62.  
  63.   if (last != NULL) cmd->last->next = next;
  64.   if (next != NULL) cmd->next->last = last;
  65.  
  66.   free(cmd);
  67.  
  68.   if (last == NULL) {
  69.     return next;
  70.   } else {
  71.     while (1) {
  72.       if (last->last == NULL) {
  73.         return last;
  74.       } else {
  75.         last = last->last;
  76.       }
  77.     }
  78.   }
  79. }
  80.  
  81. void freeBackgroundCommands(struct backgroundCommand *root) {
  82.   do {
  83.     struct backgroundCommand *next = root->next;
  84.  
  85.     free(root);
  86.  
  87.     root = next;
  88.   } while (root != NULL);
  89. }
  90.  
  91. Command parseCommand(char *str) {
  92.   Command cmd = (Command)calloc(LIST_BLOCK_SZ, sizeof(char *));
  93.  
  94.   if (cmd == NULL) {
  95.     perror("Error allocating command while parsing.");
  96.     exit(1);
  97.   }
  98.  
  99.   char *tok = strtok(str, " ");
  100.   size_t i = 0;
  101.   while (tok != NULL) {
  102.     // Append token to command
  103.     cmd[i++] = tok;
  104.     tok = strtok(NULL, " ");
  105.  
  106.     if (i % LIST_BLOCK_SZ == 0) {
  107.       cmd = (Command)realloc(cmd, (i + LIST_BLOCK_SZ) * sizeof(char *));
  108.  
  109.       if (cmd == NULL) {
  110.         perror("Error re-allocating background command.");
  111.         exit(1);
  112.       }
  113.     }
  114.   }
  115.  
  116.   // Terminate sequence with NULL
  117.   cmd[i] = NULL;
  118.   return cmd;
  119. }
  120.  
  121. Command *newCommandList() {
  122.   Command *list = (Command *)calloc(LIST_BLOCK_SZ, sizeof(Command));
  123.   if (list == NULL) {
  124.     perror("Error allocating command list.");
  125.     exit(1);
  126.   }
  127.   return list;
  128. }
  129.  
  130. Command *appendCommand(Command cmd, Command *cmdList, size_t cmdListLen) {
  131.   cmdList[cmdListLen] = cmd;
  132.  
  133.   // Re-allocate list if needed (cmdListLen not updated from append)
  134.   if (cmdListLen % LIST_BLOCK_SZ == (LIST_BLOCK_SZ - 1)) {
  135.     Command *newCmd = realloc(cmdList, (cmdListLen + LIST_BLOCK_SZ) * sizeof(Command));
  136.  
  137.     if (newCmd == NULL) {
  138.       perror("Error re-allocating command list while appending.");
  139.       exit(1);
  140.     }
  141.  
  142.     return newCmd;
  143.   }
  144.  
  145.   return cmdList;
  146. }
  147.  
  148. char *printCommand(Command cmd) {
  149.   Command iCmd = cmd;
  150.   do {
  151.     printf("%s ", (*iCmd));
  152.   } while (*(++iCmd) != NULL);
  153. }
  154.  
  155. void freeCommand(Command cmd) {
  156.   char *str = cmd[0];
  157.   size_t i = 1;
  158.   while (str != NULL) {
  159.     free(str);
  160.     str = cmd[i++];
  161.   }
  162.  
  163.   free(cmd);
  164. }
  165.  
  166. void freeCommandList(Command *cmdList, size_t len) {
  167.   size_t i = len;
  168.   while ((i--) != 0) {
  169.     freeCommand(cmdList[i]);
  170.   }
  171.   free(cmdList);
  172. }
  173.  
  174. int main(int argc, char **argv) {
  175.   Command *userCommands = newCommandList();
  176.   size_t userCommandsLen = 0;
  177.  
  178.   struct backgroundCommand *backgroundCmd = NULL;
  179.  
  180.   char basedir[256];
  181.   getcwd(basedir, sizeof(basedir));
  182.  
  183.   while (1) {
  184.     printf("== Midday Commander, v1 ===\n");
  185.     printf("Enter a command:\n");
  186.     printf("\t0. whoami\tPrints out the result of the whoami command.\n");
  187.     printf("\t1. last\t\tPrints out the result of the last command.\n");
  188.     printf("\t2. ls\t\tPrints out the result of a listing of a user-specified "
  189.            "path.\n");
  190.  
  191.     size_t i = 0;
  192.     for (; i < userCommandsLen; i++) {
  193.       printf("\t%zu. ", i + 3);
  194.       printCommand(userCommands[i]);
  195.       printf("\t\tUser-provided command.\n");
  196.     }
  197.  
  198.     printf("\ta. add command\tAdds a new command to the menu.\n");
  199.     printf("\tc. change directory\tChanges process working directory.\n");
  200.     printf("\te. exit\t\tLeave mid-day commander.\n");
  201.     printf("\tp. pwd\tPrints\t the working directory.\n");
  202.  
  203.     // Get command via input
  204.     char *input = readline("> ");
  205.     printf("\n");
  206.  
  207.     // `readline` returns NULL if EOF is read on an empty line
  208.     char firstChar;
  209.     if (input == NULL) {
  210.       firstChar = 'e';
  211.     } else {
  212.       firstChar = input[0];
  213.     }
  214.  
  215.     // Check if any background process have finished
  216.     pid_t lastPid;
  217.     do {
  218.       struct rusage usage;
  219.       lastPid = wait3(NULL, WNOHANG, &usage);
  220.  
  221.       int i = 0;
  222.       struct backgroundCommand *cmd = backgroundCmd;
  223.       while (cmd != NULL) {
  224.         i++;
  225.         struct backgroundCommand *next = cmd->next;
  226.  
  227.         if (cmd->pid == lastPid) {
  228.           printf("== Job Complete [%d] ==\n", i);
  229.           printf("Process ID: %d\n", cmd->pid);
  230.           printf("[ Output ]\n");
  231.  
  232.           // Get output file and print to stdout
  233.           char filename[16];
  234.           sprintf(filename, "%s/.%d_output", basedir, lastPid);
  235.           FILE *chOutput = fopen(filename, "r");
  236.           if (chOutput) {
  237.             char c;
  238.             while ((c = getc(chOutput)) != EOF)
  239.               putchar(c);
  240.             fclose(chOutput);
  241.  
  242.             // Remove file now that we've seen it
  243.             remove(filename);
  244.           }
  245.          
  246.           // Re-set root in case it changed
  247.           backgroundCmd = removeBgCommand(cmd);
  248.         }
  249.  
  250.         cmd = next;
  251.       }
  252.     } while (lastPid > 0);
  253.  
  254.     // Parse the input command
  255.     Command cmd;
  256.     switch (firstChar) {
  257.     case 'a': // Add a user-defined command
  258.       cmd = parseCommand(readline("Command to add: "));
  259.       if (cmd[0] == NULL) {
  260.         printf("Invalid command entered\n\n");
  261.         continue;
  262.       }
  263.  
  264.       appendCommand(cmd, userCommands, userCommandsLen++);
  265.       printf("Okay, added with ID %zu\n\n", userCommandsLen + 2);
  266.       continue;
  267.  
  268.     case 'c': { // Change Directory
  269.       printf("== Change Directory ==\n");
  270.       const char *dir = readline("Directory: ");
  271.       chdir(dir);
  272.       printf("\n");
  273.       continue;
  274.     }
  275.  
  276.     case 'e': // Exit program
  277.       printf("Logging you out, commander.\n");
  278.       return 0;
  279.  
  280.     case 'p': { // Print working directory
  281.       printf("== Current Directory ==\n");
  282.       char dir[256];
  283.       getcwd(dir, sizeof(dir));
  284.       printf("Directory: %s\n\n", dir);
  285.       continue;
  286.     }
  287.  
  288.     case 'r': { // Print all running processes
  289.       printf("== Running Processes ==\n");
  290.       struct backgroundCommand *cmd = backgroundCmd;
  291.       while (cmd != NULL) {
  292.         printf("[%d]  ", cmd->pid);
  293.         printCommand(cmd->cmd);
  294.         printf("\n");
  295.  
  296.         cmd = cmd->next;
  297.       }
  298.  
  299.       printf("\n");
  300.  
  301.       continue;
  302.     }
  303.  
  304.     // Input must be a number, and will execute a command
  305.     default: {
  306.       int selection = atoi(input);
  307.  
  308.       switch (selection) {
  309.       case 0: // whoami command
  310.         printf("== Who am I? ==\n");
  311.         cmd = parseCommand(strdup("whoami"));
  312.         break;
  313.  
  314.       case 1: // last command
  315.         printf("== Last Logins ==\n");
  316.         cmd = parseCommand(strdup("last"));
  317.         break;
  318.  
  319.       case 2: { // ls command
  320.         printf("== Directory Listing ==\n");
  321.         char *tStr = strcat(strdup("ls "), readline("Arguments: "));
  322.         tStr = strcat(tStr, " ");
  323.         tStr = strcat(tStr, readline("Path: "));
  324.         cmd = parseCommand(tStr);
  325.         break;
  326.       }
  327.  
  328.       default: { // User-defined command
  329.         // Offset selection by 3 to take pre-defined commands into account
  330.         size_t adjSelection = selection - 3;
  331.         if (adjSelection >= userCommandsLen || adjSelection < 0) {
  332.           printf("Not a valid command.\n");
  333.           continue;
  334.         }
  335.  
  336.         cmd = userCommands[adjSelection];
  337.       }
  338.       }
  339.     }
  340.     }
  341.  
  342.     // Find last element in command
  343.     Command iCmd = cmd;
  344.     while (*(++iCmd) != NULL);
  345.     iCmd--;
  346.  
  347.     // Check if selected process is a background process
  348.     int background = (**iCmd == '&');
  349.  
  350.     struct timeval beforeTime;
  351.     gettimeofday(&beforeTime, NULL);
  352.  
  353.     // Print custom command's name
  354.     if (!background && atoi(input) > 2) {
  355.       printf("== ");
  356.       printCommand(cmd);
  357.       printf("==\n");
  358.     }
  359.  
  360.     pid_t pid = fork();
  361.     struct rusage usage;
  362.     if (pid < 0) {
  363.       perror("Error during fork()\n");
  364.       exit(1);
  365.     } else if (pid != 0) {
  366.       if (background) {
  367.         // Check if we've created a background cmd already
  368.         // if not, then create a new background cmd.
  369.         // Otherwise place a new background cmd in the linked list
  370.         if (backgroundCmd == NULL) {
  371.           backgroundCmd = createBgCommand(pid, cmd);
  372.         } else {
  373.           appendBgCommand(pid, cmd, backgroundCmd);
  374.         }
  375.  
  376.         printf("[%d] Running ", pid);
  377.         printCommand(cmd);
  378.         printf("in background...\n\n");
  379.       } else {
  380.         wait3(NULL, 0, &usage);
  381.       }
  382.     } else if (pid == 0) {
  383.       // If command is a background cmd...
  384.       if (background) {
  385.         // remove the & at the end
  386.         *iCmd = NULL;
  387.  
  388.         // close stdout and output to a file
  389.         close(STDOUT_FILENO);
  390.         char filepath[16];      
  391.         sprintf(filepath, "%s/.%d_output", basedir, getpid());
  392.         int redirect = open(filepath, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
  393.  
  394.         if (redirect == -1) {
  395.           perror("Could not open output redirect file.");      
  396.           exit(1);  
  397.         }
  398.  
  399.         // Create grand-child process in order for us to time execution
  400.         pid_t gcPID = fork();
  401.         if (gcPID < 0) {
  402.           perror("Error during gc fork().");
  403.           exit(1);
  404.         } else if (gcPID != 0) {
  405.           wait(NULL);
  406.         } else {
  407.           // Run command
  408.           int res = execvp(cmd[0], cmd);
  409.           if (res == -1) {
  410.             printf("Error: invalid command %s.\n", cmd[0]);
  411.             exit(1);
  412.           }
  413.         }
  414.       } else {
  415.         // Run command
  416.         int res = execvp(cmd[0], cmd);
  417.         if (res == -1) {
  418.           printf("Error: invalid command %s.\n", cmd[0]);
  419.           exit(1);
  420.         }
  421.       }
  422.     }
  423.  
  424.     // If running cmd is a background command, don't print stats
  425.     if (background && pid != 0) continue;
  426.  
  427.     struct timeval afterTime;
  428.     gettimeofday(&afterTime, NULL);
  429.  
  430.     int elapsedTime = (afterTime.tv_sec - beforeTime.tv_sec) * 1000 +
  431.                       (afterTime.tv_usec - beforeTime.tv_usec) / 1000;
  432.  
  433.     printf("\n-- Statistics --\n");
  434.     printf("Elapsed Time: %d ms\n", elapsedTime);
  435.     printf("Page Faults: %ld\n", usage.ru_majflt);
  436.     printf("Page Faults (reclaimed): %ld\n\n", usage.ru_minflt);
  437.  
  438.     // If we're a child process, we've done our job.
  439.     if (pid == 0) return 0;
  440.   }
  441.  
  442.   // Free allocated memory
  443.   freeBackgroundCommands(backgroundCmd);
  444.   freeCommandList(userCommands, userCommandsLen);
  445.  
  446.  
  447.   return 0;
  448. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement