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.