Getting return from execlp() - c

I have a program which I would like to sort the first column in a file, from a child process, and return the output to the parent process. How can I retrieve the response from the execlp and print it? Here is what I have so far:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define WRITE 1
#define READ 0
int main(int argc, char **argv)
{
int i, k;
int p1[2], p2[2];
int p1[2], p2[2];
pid_t childID;
if (pipe(p1) < 0 || pipe(p2) < 0) {
perror("pipe");
exit(0);
}
childID = fork();
if (childID < 0) {
perror("fork");
exit(0);
}
else if (childID == 0){
close(p1[WRITE]);
close(p2[READ]);
dup2(p1[READ], STDIN_FILENO);
close(p1[READ]);
dup2(p2[WRITE], STDOUT_FILENO);
close(p2[WRITE]);
execlp("sort", "-k1", "-n", "temp.txt", (char *)NULL);
perror("exec");
exit(0);
}
else {
//parent process
//Not sure how to get response from exec
}
}

After call execlp(), the memory image of current process will be replaced by the called progame, so you cannot get what you want through return value. What you can do is let the child process write its result to somehere, such as a temporal file or a pipe, and the parent process read the result from this place.
After proper setup a pipe to communite between parent and child processes, you can write the result of child process in its stdout, and read the result in parent processes from its stdin.
Something like this:
else if (childID == 0){
close(p1[READ]);
dup2(p1[WRITE], STDOUT_FILENO);
close(p1[WRITE]);
execlp("sort", "-k1", "-n", "temp.txt", (char *)NULL);
perror("exec");
exit(0);
}
else {
close(p1[WRITE]);
dup2(p1[READ], STDIN_FILENO);
close(p1[READ]);
while (scanf("%ms ", &l) != EOF) {
printf("%s\n", l);
free(l);
}
}
Here is full code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define WRITE 1
#define READ 0
int main(int argc, char **argv)
{
int p1[2];
char *l;
pid_t childID;
if (pipe(p1) < 0) {
perror("pipe");
exit(0);
}
childID = fork();
if (childID < 0) {
perror("fork");
exit(0);
}
else if (childID == 0){
close(p1[READ]);
dup2(p1[WRITE], STDOUT_FILENO);
close(p1[WRITE]);
execlp("sort", "-k1", "-n", "temp.txt", (char *)NULL);
perror("exec");
exit(0);
}
else {
close(p1[WRITE]);
dup2(p1[READ], STDIN_FILENO);
close(p1[READ]);
while (scanf("%ms ", &l) != EOF) {
printf("%s\n", l);
free(l);
}
}
return 0;
}
And test file temp.txt:
$ cat temp.txt
a
e
b
d
f
c
Result of a test run:
$ ./a.out
a
b
c
d
e
f

Related

Using sed with the execvp to cut part of stout. C

I'm using a function that echo a string and redirect output to a sed input in c.
If i echo a string like "hello: bye bye", i need to cut everything before the ":". So i buildt a function that fork and pipe for this but sed won't recognize my regex:
void sender (char * str_ ,char * pipe_ ,char **args_) {
int fd[2];
int pid;
char* cmd1[] = {"echo", str_,NULL};
char* sed[] = {"sed","'[^:]*$'",NULL};
int status;
pid = fork();
if (pid == 0) {
if(pipe(fd) < 0){
exit(100);
}
pid = fork();
if (pid == 0) {
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
execvp(cmd1[0], cmd1);
printf("Error in execvp1\n");
}else{
close(fd[1]);
wait(&status);
dup2(fd[0],0);
close(fd[0]);
dup2(1,2);
execvp(sed[0],sed);
printf("Error in execvp2\n");
}
}
else{
close(fd[0]);
close(fd[1]);
wait(&status);
wait(&status);
}
}
The output is error for every line read because of sed:expression -e #1, character 1: unknown command: `''
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
void pipe_exec(int pfd[], char *cmd_args[], int redirect_output)
{
printf("%s, pid %d\n", cmd_args[0], getpid());
if (redirect_output)
dup2(pfd[1], 1);
else
dup2(pfd[0], 0);
close(pfd[0]);
close(pfd[1]);
execvp(cmd_args[0], cmd_args);
printf("Error in execvp\n");
exit(1);
}
void sender(char *str_, char *unused1, char **unused2)
{
int status, pid, fd[2];
char *cmd1[] = { "echo", str_, NULL };
char *sed[] = { "sed", "s/[^:]*://", NULL };
if (pipe(fd) < 0)
exit(100);
pid = fork();
if (pid == 0)
pipe_exec(fd, cmd1, 1);
pid = fork();
if (pid == 0)
pipe_exec(fd, sed, 0);
close(fd[0]);
close(fd[1]);
wait(&status);
wait(&status);
}
int
main(void)
{
sender("hello: bye bye", NULL, NULL);
return (0);
}

Program stuck on Pipe (exec ls grep sort)

I'm trying to make a program that executes the following commands connecting the output of one to the input of the next using pipes and taking two arguments DIR (directory) and ARG (filetype, example: jpg).
ls DIR -laR | grep ARG | sort
Here's the code:
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid arguments. <dir> <arg>\n");
exit(1);
}
int pipe_fd1[2];
int pipe_fd2[2];
pid_t ls_pid, grep_pid;
int status;
pipe(pipe_fd1);
pipe(pipe_fd2);
ls_pid = fork();
if (ls_pid == 0) { //first child ls DIR -laR
dup2(pipe_fd1[1], STDOUT_FILENO);
close(pipe_fd1[0]);
execlp("ls", "ls", argv[1], "-laR", NULL);
} else if (ls_pid > 0) {
grep_pid = fork();
if (grep_pid == 0) { //second child grep ARG
dup2(pipe_fd1[0], STDIN_FILENO);
dup2(pipe_fd2[1], STDOUT_FILENO);
close(pipe_fd1[1]);
close(pipe_fd2[0]);
waitpid(ls_pid, &status, 0);
execlp("grep", "grep", argv[2], NULL);
} else if (grep_pid > 0) { //parent sort
dup2(pipe_fd2[0], STDIN_FILENO);
close(pipe_fd2[1]);
waitpid(grep_pid, &status, 0);
execlp("sort", "sort", NULL);
}
}
return 0;
}
It seems to be stuck? Not sure why?
You never close pipe_fd1 on the parent, so grep and sort doen't know when to stop reading input: because the pipe read and write ends are never closed on the parent, the reader blocks waiting for more input that will never arrive. You need to close it.
Also, you don't need waitpid(): the way pipes work ensures that input flows linearly and in order throughout the pipe.
Here's the working version with these issues addressed:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid arguments. <dir> <arg>\n");
exit(1);
}
int pipe_fd1[2];
int pipe_fd2[2];
pid_t ls_pid, grep_pid;
pipe(pipe_fd1);
ls_pid = fork();
if (ls_pid == 0) { //first child ls DIR -laR
dup2(pipe_fd1[1], STDOUT_FILENO);
close(pipe_fd1[0]);
execlp("ls", "ls", argv[1], "-laR", NULL);
} else if (ls_pid > 0) {
dup2(pipe_fd1[0], STDIN_FILENO);
close(pipe_fd1[1]);
pipe(pipe_fd2);
grep_pid = fork();
if (grep_pid == 0) { //second child grep ARG
dup2(pipe_fd2[1], STDOUT_FILENO);
close(pipe_fd2[0]);
execlp("grep", "grep", argv[2], NULL);
} else if (grep_pid > 0) { //parent sort
dup2(pipe_fd2[0], STDIN_FILENO);
close(pipe_fd2[1]);
execlp("sort", "sort", NULL);
}
}
return 0;
}

Two pipes in c program and the second one does not work as expected

I am currently making some tests with the pipes and forks in C.
I am trying to copy the behavior of this shell command in my program:
cat < test | wc
The test file contains only a little text.
Here is my program's code:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <stdlib.h>
// cat < test | wc
int main(int ac, char **av)
{
int p1[2];
int pid1;
printf("\nBegin of the test...\n");
if (pipe(p1) == -1)
{
perror("Pipe1");
exit(1);
}
else
{
pid1 = fork();
if (pid1 > 0)
{
int p2[2];
int pid2;
close(p1[0]);
if (pipe(p2) == -1)
{
perror("Pipe1");
exit(1);
}
else
{
pid2 = fork();
if (pid2 > 0)
{
int pid3;
close(p1[1]);
pid3 = fork();
if (pid3 > 0)
{
close(p2[1]);
wait(0);
printf("All is done !\n");
}
else if (pid3 == 0)
{
close(p2[1]);
dup2(p2[0], 0);
execve("/usr/bin/wc", av, NULL);
}
wait(0);
}
else if (pid2 == 0)
{
int fd;
char buffer[20];
int ret;
close(p2[0]);
dup2(p2[1], 1);
fd = open("test", O_RDONLY);
if (fd >= 0)
{
while ((ret = read(fd, buffer, 20)) > 0)
write(p1[1], buffer, ret);
close(fd);
close(p1[1]);
}
else
{
perror("Open file error");
exit(1);
}
close(p1[1]);
exit(1);
}
}
wait(0);
}
else if (pid1 == 0)
{
close(p1[1]);
dup2(p1[0], 0);
execve("/bin/cat", av, NULL);
}
}
printf("\nEnd of the program.\n");
return (1);
}
The output is:
Begin of the test...
All is done !
Content of the test file
0 0 0
End of the program.
Can someone explain to me why the cat output is not redirected to the wc through the pipe 2?

execlp redirect stdin in c

Question is simple
I've searched around but I couldn't find the solution
char *data1;
char *data2;
pid_t pid = fork();
int stat;
if (pid == 0){
execlp("Program B");
} else {
wait(&stat);
if (WIFEXITED(stat))
printf("%d\n", WEXITSTATUS(stat));
}
the thing is I need to send data1 and data2 to Program B as stdin
but I couldn't find the solution
how can I deal with this?
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
char *data1 = "First message.\n";
char *data2 = "What the rest of the world has been waiting for.\n";
pid_t pid;
int p[2];
if (pipe(p) < 0)
perror("pipe() failed");
else if ((pid = fork()) < 0)
perror("fork() failed");
else if (pid == 0)
{
dup2(p[0], STDIN_FILENO);
close(p[0]);
close(p[1]);
execlp("cat", "cat", (char *)0);
perror("execlp() failed");
}
else
{
close(p[0]);
write(p[1], data1, strlen(data1));
write(p[1], data2, strlen(data2));
close(p[1]);
int status;
int corpse = wait(&status);
if (WIFEXITED(status))
printf("%d exited with status %d\n", corpse, WEXITSTATUS(status));
}
return 0;
}
Note how many closes are necessary.
Building a pipe to stdin is the way to go,
Like
char *data1;
char *data2;
int stat;
pid_t pid;
if( pipe(pfd) < 0 ) {
perror("pipe");
return 1;
}
pid = fork();
if (pid == 0)
{
// Close the writing end of the pipe
close(pfd[1]);
execlp("Program B");
}
else if(pid==-1)
{
perror("fork");
}
else
{
// Write to the pipe.
if (write(pfd[1], "This is my data \n", 16) != 16)
perror("write");
close(pfd[1]);
wait(&stat);
if (WIFEXITED(stat))
printf("%d\n", WEXITSTATUS(stat));
}
You can provide data as argument list to the new process.
Syntax:- int execlp(const char *path, const char *arg0, ..., NULL);
So your call can look something like this
// convert the input data into string format i.e data1 and data2 should be strings
execlp("Program B","Program B",data1,data2,NULL);
In program B use appropriately to convert it into whatever type you want to.

I'm building a small shell. How do I set the standard in- and output of two processes to a pipe, so they can communicate?

I'm trying to implement a very small shell of my own. I have to be able to handle pipes, like
ls -l | wc -l
but only for two programs at a time. Right now, I have this:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER_SIZE 256
#define NO_PARAMS 32
void split_string(char **params, char *string){
char *arg;
int i;
arg = strtok(string, " ");
params[0] = arg;
i = 1;
while(arg != NULL){
arg = strtok(NULL, " ");
params[i] = arg;
i++;
}
}
int main(int argc, char **argv){
char string[BUFFER_SIZE];
char *prog1, *prog2;
int i, err;
int fd[2];
pid_t pid1, pid2;
size_t buffer = BUFFER_SIZE;
char *params1[NO_PARAMS], *params2[NO_PARAMS];
int pipe_exists = 0;
memset(string,0,buffer);
while(1){
/*Read command*/
fgets(string, BUFFER_SIZE-1, stdin);
if(string == NULL){
perror("Error reading input:\n");
exit(1);
}
/*replace linefeed character with end of line character*/
for(i=0;i<BUFFER_SIZE;i++){
if(string[i] == 10){
string[i] = 0;
}
}
/*check if command is "exit"*/
if(strcmp(string,"exit") == 0){
return 0;
}
/*split command into different program calls*/
prog1 = strtok(string, "|");
prog2 = strtok(NULL,"\0");
if(prog2 != NULL){
pipe_exists = 1;
printf("PIPE!\n");
err = pipe(fd);
if(err<0){
perror("Error creating pipe:\n");
exit(1);
}
}
/*split string into arguments*/
split_string(params1, prog1);
if(pipe_exists){
split_string(params2, prog2);
}
/*fork child process*/
pid1 = fork();
if(pid1==0){ /*child 1*/
if(pipe_exists){
close(fd[0]); /*close read-end*/
err = dup2(fd[1], 1);
if(err<0){
perror("Error with dup in child 1!\n");
exit(1);
}
}
execvp(params1[0],params1);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent*/
if(pipe_exists){
pid2 = fork();
if(pid2==0){ /*child 2*/
close(fd[1]); /*close pipe write-end*/
err = dup2(fd[0], 0);
if(err<0){
perror("Error with dup in child 2!\n");
exit(1);
}
execvp(params2[0],params2);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent with 2 children*/
waitpid(pid1,0,0);
waitpid(pid2,0,0);
}
}else{ /*parent with 1 child*/
waitpid(pid1,0,0);
}
}
}
}
Right now, it'll handle single commands fine, but when I input something like the command above, nothing happens!
Thanks!
Oh! I've already figured it out. I had to close the pipe in the parent program as well :)
To start with, you should loop as long as you find the pipe character. Then you need to create a pipe for each "piping".
Real shells usually forks and exec itself for each command in the pipeline. This is so it should be able to handle internal commands.
There are 3 main parts in a command with pipes.
The begining, that takes stdin and pipes its output something |
The middle, optionnal or repeated at will with two pipes | something |
The end, that outputs to stdout | something
Then use three functions, one for each of those:
#define PIPE_INPUT 0
#define PIPE_OUTPUT 1
execute_pipe_start(t_cmdlist *commands)
{
int pid;
int fd[2];
if (!commands)
return;
if (commands->next)
{
if (pipe(fd) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd[PIPE_INPUT]);
if (dup2(fd[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
{
waitpid(...); //what you put here is a bit tricky because
//some shells like tcsh will execute all
//commands at the same time (try cat | cat | cat | cat)
}
if (commands->next->next != null) //If you have 2 commands in line there is a middle
execute_pipe_middle(commands->next, fd);
else // no middle
execute_pipe_end(commands->next, fd);
}
else
parse_and_exec_cmd(commands->cmd);
}
execute_pipe_middle(t_cmdlist *commands, int fd_before[2])
{
int pid;
int fd_after[2];
if (pipe(fd_after) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
close(fd_after[PIPE_INPUT]);
if (dup2(fd_after[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
if (commands->next->next != null) //More than two following commands : a middle again
execute_pipe_middle(commands->next, fd_after);
else // No more repetition
execute_pipe_end(commands->next, fd_after);
}
execute_pipe_end(t_cmdlist *commands, int fd_before[2])
{
int pid;
if (!commands)
return;
if (commands->next)
{
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
}
}

Resources