How can I handle multiple (compound) commands in a command server program?

25 views Asked by At

I am trying to implement a command server program. I can handle single commands right now. However, I cannot handle the compound commands. To explain I need to handle with user commands like "ls | ps aux". At the end, I need to display the outputs of the both commands. However, my code only prints the last command's output. For the above example, it only prints the output of the "ps aux" command. But if the user enters "ls | ps aux | ls", it displays only the output of the "ls" command. How can I fix this? My server code that deals with this logic is as follows:

                case COMLINE: {
                    char client_message[MAX_MSG_SIZE];
                    bytes_read = read(cs_fd, client_message, MAX_MSG_SIZE);
                    if (bytes_read == -1) {
                        perror("read");
                        exit(EXIT_FAILURE);
                    }
                    client_message[bytes_read] = '\0'; 
                    printf("Command received: %s\n", client_message);

                    // Split the command string into individual commands
                    char *commands[10]; // Assuming a maximum of 10 commands in a pipeline
                    int n_commands = 0;
                    char *token = strtok(client_message, "|");
                    while (token != NULL) {
                        commands[n_commands++] = token;
                        token = strtok(NULL, "|");
                    }

                    if (n_commands == 0) {
                        // Handle the case where no commands are found
                        break;
                    }

                    // Dynamically allocate pipefd
                    int *pipefd = malloc(2 * (n_commands - 1) * sizeof(int));
                    if (pipefd == NULL) {
                        perror("malloc");
                        exit(EXIT_FAILURE);
                    }

                    // Create pipes
                    for (int i = 0; i < (n_commands - 1); i++) {
                        if (pipe(pipefd + i*2) < 0) {
                            perror("pipe");
                            exit(EXIT_FAILURE);
                        }
                    }

                    // Create a temporary file to store the last command's output
                    FILE *temp_output = tmpfile();
                    if (temp_output == NULL) {
                        perror("tmpfile");
                        exit(EXIT_FAILURE);
                    }

                    // Execute each command
                    for (int i = 0; i < n_commands; i++) {
                        pid_t pid = fork();
                        if (pid == -1) {
                            perror("fork");
                            exit(EXIT_FAILURE);
                        } else if (pid == 0) { // Child process
                            // Redirect input from the previous command
                            if (i != 0) {
                                if (dup2(pipefd[(i-1)*2], STDIN_FILENO) < 0) {
                                    perror("dup2");
                                    exit(EXIT_FAILURE);
                                }
                            }

                            // Redirect output to the next command or the temp file
                            if (i != n_commands - 1) {
                                if (dup2(pipefd[i*2 + 1], STDOUT_FILENO) < 0) {
                                    perror("dup2");
                                    exit(EXIT_FAILURE);
                                }
                            } else {
                                // Last command: redirect output to temp file
                                if (dup2(fileno(temp_output), STDOUT_FILENO) < 0) {
                                    perror("dup2");
                                    exit(EXIT_FAILURE);
                                }
                            }

                            // Close all pipe file descriptors
                            for (int j = 0; j < 2*(n_commands-1); j++) {
                                close(pipefd[j]);
                            }

                            // Execute the command
                            execlp("/bin/sh", "sh", "-c", commands[i], NULL);
                            perror("execlp");
                            exit(EXIT_FAILURE);
                        }
                    }

                    // Close all pipe file descriptors in the parent
                    for (int i = 0; i < 2*(n_commands-1); i++) {
                        close(pipefd[i]);
                    }
                    free(pipefd);

                    // Wait for all child processes to finish
                    for (int i = 0; i < n_commands; i++) {
                        wait(NULL);
                    }


                    // Send the output to the client
                    Message response;
                    fseek(temp_output, 0, SEEK_END);
                    response.length = ftell(temp_output);
                    response.type = COMRESULT;
                    write(sc_fd, &response, sizeof(response));

                    rewind(temp_output);
                    while ((bytes_read = fread(temp_buffer, 1, wsize, temp_output)) > 0) {
                        ssize_t written = write(sc_fd, temp_buffer, bytes_read);
                        if (written == -1) {
                            perror("write");
                            exit(EXIT_FAILURE);
                        }
                        printf("Sent: %d\n", (int)written);
                        
                        // Print sent bytes individually
                        printf("Sent: ");
                        for (ssize_t i = 0; i < written; ++i) {
                            printf("%c", temp_buffer[i]);
                        }
                        printf("\n");

                        if(written < wsize){
                            break;
                        }
                    }

                    // Close the temporary file
                    fclose(temp_output);
                    break;
                }

I tried to use multiple pipes for maximum 10 commands. I redirect every commands input to the pipe until the last command. At the end, I redirect the last command's output to the temporary file which will be read and whose output will be sent to the client. However, I cannot see the outputs of all commands right now.

0

There are 0 answers