process termination doesn't affect waitpid() - c

I need to simulate the following bash commands using C under Linux (with fork, exec, kill, signal, wait, waitpid, dup2, open, sleep, pipe etc).
[0] echo 'tail-f $1' > /tmp/rtail
[1]/tmp/rtail ~/.bash_history >> /tmp/1.txt &
PID of process [1] should be saved.
[2] Expect termination of the command started on step [1]. After termination print on the screen: "Program 1 terminated."
So far I have this code:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
pid_t pID = fork();
if (pID == 0) // child
{
int file = open("/tmp/rtail", O_CREAT | O_WRONLY);
//Now we redirect standard output to the file using dup2
dup2(file, 1);
puts("tail -f $1");
close(file);
system("chmod 777 /tmp/rtail");
exit(0);
} else if (pID < 0) // failed to fork
{
printf("Failed to fork");
exit(1);
// Throw exception
} else // parent
{
pid_t pID2 = fork();
if (pID2 == 0) {
char tmp1[20];
sprintf(tmp1, "echo %i > /tmp/pidprog1", getpid());
system(tmp1);
int file = open("/tmp/1.txt", O_APPEND | O_WRONLY);
//Now we redirect standard output to the file using dup2
dup2(file, 1);
FILE* proc = popen("sh /tmp/rtail ~/.bash_history", "r");
char tmp[20];
while (fgets(tmp, 40, proc) != NULL) {
printf(tmp);
}
fclose(proc);
exit(0);
}
else if (pID2 < 0) // failed to fork
{
printf("Failed to fork");
exit(1);
// Throw exception
} else {
FILE* fl = fopen("/tmp/pidprog1", "r");
char buff[10];
fgets(buff, 10, fl);
int pidc = atoi(buff);
fclose(fl);
int status;
waitpid(pidc, &status, 0);
printf("Program 1 terminated\n");
}
}
// Code executed by both parent and child.
return 0;
}
The problem is that when I manually kill the process using PID saved into /tmp/pidprog1, parent process doesn't stop waiting and doesn't print "Program 1 terminated" line.

The parent is very likely reading a garbage value into pidc. You are doing nothing to ensure that the grandchild has actually written the pid before the parent tries to read it. You need to use wait to ensure that valid pids are in the file. (Or, just keep track of the pids from the return value of fork.)
You are not doing enough error checking: what happens if any open fails? (eg, when you try
to open /tmp/1.txt for appending but it doesn't already exist?)
Why are you using fgets to read 40 characters into a buffer of size 20?
Why are you dup'ing and using fputs instead of just writing to the fd?
Why are you printing error messages to stdout instead of stderr ( use perror ).

Related

Can't read from a pipe after dup2() and fork. C

I'm writing a code that echo a string and sed it two times. My output is correct, but when I try to place that string on an array it blocks on read and goes on with the other calls.
Here's the code:
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
char **sendout=NULL;
int send_i=0;
void sender2(char* str_) {
int fd[2];
int fd1[2];
int fd2[2];
int pid;
char* echo[] = {"echo", str_, NULL};
char* sed[] = {"sed", "regex1", NULL};
char* sed2[] = {"sed", "regex2", NULL};
int status;
if (pipe(fd) < 0) {
exit(100);
}
pid = fork();
if (pid == 0) {
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
execvp(echo[0], echo);
printf("Error in execvp1\n");
}
if (pipe(fd1) < 0) {
exit(100);
}
pid = fork();
if (pid == 0) {
close(fd[1]);
close(fd1[0]);
dup2(fd[0], 0);
dup2(fd1[1], 1);
dup2(fd1[1], 2);
close(fd[0]);
close(fd1[1]);
execvp(sed2[0], sed2);
printf("Error in execvp2\n");
}
if (pipe(fd2) < 0) {
exit(100);
}
pid = fork();
if (pid == 0) {
close(fd1[1]);
close(fd2[0]);
dup2(fd1[0], 0);
dup2(fd2[1], 1);
dup2(fd2[1], 2);
close(fd2[1]);
close(fd1[0]);
execvp(sed[0], sed);
}
pid = fork();
if (pid == 0) {
close(fd2[1]);
char* line = NULL;
size_t len = 0;
ssize_t read_;
FILE* f_pipe;
f_pipe = fdopen(fd2[0], "r");
printf("1\n");
while ((read_ = getline(&line, &len, f_pipe)) != -1) {
printf("2\n");
sendout = realloc(sendout, sizeof(char*) * (send_i + 1));
sendout[send_i] = strdup(line);
send_i++;
printf("%s\n", line);
}
fclose(f_pipe);
close(fd2[0]);
return;
}
close(fd[1]);
close(fd[0]);
close(fd1[1]);
close(fd1[0]);
close(fd2[1]);
close(fd2[0]);
if (pid != 0) {
wait(&status);
}
}
int main() {
sender2("hello");
}
Like I said it all worked until the read. If I pass 3 string to the function the output is like:
1
1
1
If I don't dup to the last pipe it prints pretty well what I need, I also used return in the last fork because it's the only child process that isn't killed from execvp. But it doesn't even reach the first print. I even tried opening the pipe as a file or with a classic open, so it goes that I tried open and also fopen, as you can see. I'm failing because it can't read anything. That would be a time problem.
Fork and File Descriptors
When you fork a process, copies of all file descriptors are inherited. Since those are copies, the descriptors must be closed in both the child and the parent. You should always close them as soon as possible. This is especially true if you fork several times.
It's very easy to miss something here. It is therefore best to check very carefully that all file descriptors have been closed.
Minimum Amount of Changes
So the minimum number of changes for your code to get a result would be as follows.
If the first fork in line 41 is successful then in the parent you need to close the pipe file descriptors fd[0] and fd[1], e.g. in line 56.
pid = fork();
if (pid == 0) {
...
}
close(fd[0]); //<-- add these two lines
close(fd[1]);
if (pipe(fd2) < 0) {
...
Likewise you need to do the same after the second fork for fd1, so:
pid = fork();
if (pid == 0) {
...
}
close(fd1[0]); //<-- add these two lines
close(fd1[1]);
pid = fork();
When you now run your code you would already get as output:
1
2
hello
Better Test Case
This would not yet verify that both sed commands would run correctly. For a test case change the call in main to:
sender2("hello mars");
and change your sed commands to:
char* sed[] = {"sed", "s/moon/world/", NULL};
char* sed2[] = {"sed", "s/mars/moon/", NULL};
(sed2 command is executed before sed in your code, it would make the code a bit easier to understand if sed is executed before sed2)
This gives as output then:
1
2
hello world
So both sed commands are executed.
Additional Remarks
Below are some remarks in no particular order, mainly concerning error handling.
A call to fork returns pid_t and not int. So you should change your definition of the variable pid to: pid_t pid;.
If execvp fails one should print the error cause and exit with an error status, e.g. something like this:
perror("execvp of command xyz failed");
exit(EXIT_FAILURE);
If opening a pipe fails, also print a descriptive message on stderr.
Also fork calls can fail, this should also be handled. In this case fork returns -1. Same as above, print error message on stderr and return an error status.
In main you should return a success or failure state (e.g. return EXIT_SUCCESS;).
You don't use the the variable read_. Then the variable can be removed.
If fdopen fails it returns NULL. This error case should be handled.
The memory allocated with realloc is never released.

How to Create IPC (Interprocess Communication) C programme to create with two child process

I want to create a IPC c program to create one parent and two child's processes. My code is:
#include <stdio.h>
void main()
{
int pid, status;
pid = fork();
if(pid == -1) {
printf(“fork failed\n”);
exit(1);
}
if(pid == 0) { /* Child */
if (execlp(“/bin/ls”, “ls”, NULL)< 0) {
printf(“exec failed\n”);
exit(1);
}
}
else { /* Parent */
wait(&status);
printf(“Well done kid!\n”);
exit(0);
}
}
I want to show you an other code snippet to create one parent and two child process. This is what I am looking for. Now I want to write shell script for IPC, first take look of this code.
Note: there is an other code with same logic but different process names UP, uc1, uc2 e.g in this way we have two parent VP and UC and there childs vp1 vp2 and uc1 uc2.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX_BUF 1024
int main(){
int mypipe_c1[2];
int ret_c1;
char buf_c1[6];
ret_c1 =pipe(mypipe_c1);
int mypipe_c2[2];
int ret_c2;
char buf_c2[6];
ret_c2 =pipe(mypipe_c2);
if(ret_c1 == -1)
{
perror("pipe");
exit(1);
}
pid_t vc1;
pid_t vc2;
vc1 = fork ();
if (vc1 == 0)
{
read(mypipe_c1[0], buf_c1 , 37);
printf("PIPE1 :%s\n", buf_c1);
printf (" vc1 : I'm the child! My pid is (%d)\n", getpid ());
close(ret_c1);
int fd;
char * fifo1 = "/tmp/fifo1";
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(fifo1, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("FIFO1: %s\n", buf);
close(fd);
exit(0);
}
if(vc1 < 0)
{
perror ("Ouch! Unable to fork() child process!\n");
exit (1);
}
vc2 = fork ();
if (vc2 == 0)
{
printf ("vc2 : I'm the child! My pid is (%d)\n", getpid ());
read(mypipe_c2[0], buf_c2 , 37);
printf("PIPE2 %s\n", buf_c2);
int fd;
char * fifo2 = "/tmp/fifo2";
/* create the FIFO (named pipe) */
mkfifo(fifo2, 0666);
/* write "Hi" to the FIFO */
fd = open(fifo2, O_WRONLY);
write(fd, " assignment VU 2 ", sizeof(" assignment VU 2 "));
close(fd);
/* remove the FIFO */
unlink(fifo2);
exit(0);
}
else if (vc2 < 0)
{
perror ("Ouch! Unable to fork() child process!\n");
exit (1);
}
printf ("I'm the parent! My pid is (%d)!\n",getpid());
write(mypipe_c1[1], "I am going to close you carry on UC1 \n", 37);
write(mypipe_c2[1], "I am going to close you carry on UC2 \n", 37);
exit(0);
}
Now I want shell script such that VP and UP should be started when users types … script.sh start VP or UP. vc1, vc2, uc1,uc2 should be stoppable only using script.sh stop vc1 or vc2 or uc1 or uc2
script.sh connect command should create two fifo and connect processes as shown in figure.
So you are asking for methods for IPC, with the sample code you provided, I think the best one is the use of pipes.
From the pipe() man page:
A pipe is a unidirectional data channel that can be used for interprocess communication
Basically, it is handled like a pair of file descriptors. First, you must init the pipe, and then create the childs using the fork() call, so both parents and childs share the resource. Then, using write and read methods, you can send data between them.
In this example I create a child which reads some data from the parent process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int pid;
char buffer[255];
int fd[2]; // channel 0 for reading and 1 for writing
pipe(fd);
pid = fork();
if(pid == 0) {
close(fd[1]); // close fd[1] since child will only read
read(fd[0], &buffer, sizeof(buffer));
close(fd[0]);
exit(0);
} else { // parent
close(fd[0]) // close fd[0] since parent will only write
// init buffer contents
write(fd[1], &buffer, sizeof(buffer));
close(fd[1]);
}
return 0;
}
As you can see pipe creates a pair of file descriptors, one for writing (number 1) and one for reading (number 0).
In my sample code, the child process closes the writing one, since it will only read, and the parent closes the reading one, since it will only write data.
Note that pipes are unidirectional, so if you want that both the childs and the parent write and read data from it, you should create two pipes (so 4 file descriptors) for each of the childs. An example of how to handle that situation:
int pipeA[2], pipeB[2];
pid = fork();
if (pid == 0) { // child will write to pipeB and read from pipeA
close(pipeA[1]); // closing pipeA writing fd
close(pipeB[0]); // closing pipeB reading fd
write(pipeB[1],&buffer, sizeof(buffer));
read(pipeA[0], &buffer2, sizeof(buffer2));
close(pipeA[0]);
close(pipeB[1]);
exit(1);
} else { // parent will write to pipeA and read from pipeB
close(pipeA[0]); // closing pipeA reading fd
close(pipeB[1]); // closing pipeB writing fd
read(pipeB[0], &buffer, sizeof(buffer));
write(pipeA[1], &buffer2, sizeof(buffer2));
close(pipeA[1]);
close(pipeB[0]);
}
If you want more info about pipes you can check the man page here.
Also, other simple ways of IPC would be the use of Unix Sockets, although I think that for the example you presented pipes will be enough.
You'r code create one parent and one child, not two child, so you need to add another fork into child block :
#include <stdio.h>
void main()
{
int pid,status;
pid = fork();
if(pid == -1) {
printf(“fork failed\n”);
exit(1);
}
if(pid == 0) { /* Child */
fork();// another child
if (execlp(“/bin/ls”, “ls”, NULL)< 0) {
printf(“exec failed\n”);
exit(1);
}
}
else { /* Parent */
wait(&status);
printf(“Well done kid!\n”);
exit(0);
}
}

headache with named pipes and forks

I need to write program that have construction like this:
Parent makes fifo, then fork()
child 1 reads message from stdin and writes it to named pipe (FIFO)
then in parent process I need to create pipe (unnamed) and another fork()
child number 2 reades from FIFO, counts length of message and send number to parent via pipe(unnamed).
I created a simple program with one fork where child can communicate with parent:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO "/tmp/my_fifo"
int main()
{
pid_t fork_result;
int pipe_fd;
int res;
char writer[3];
char reader[3];
res = mkfifo(FIFO,0777);
if (res == 0)
{
printf("FIFO created!\n");
fork_result = fork();
if (fork_result == -1)
{
fprintf(stderr, "fork error");
exit(EXIT_FAILURE);
}
if (fork_result == 0)
{
printf("CHILD 1\n");
pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
scanf("%s", writer);
res = write(pipe_fd,writer,3);
if (res == -1)
{
fprintf(stderr,"error writing fifo\n");
exit(EXIT_FAILURE);
}
(void)close(pipe_fd);
exit(EXIT_SUCCESS);
}
else
{
printf("PARENT\n");
pipe_fd = open(FIFO, O_RDONLY);
res = read(pipe_fd, reader, 3);
printf("reader: 0: %c\n",reader[0]);
printf("reader: 1: %c\n",reader[1]);
printf("reader: 2: %c\n",reader[2]);
(void)close(res);
}
}
else
{
printf("deleting fifo... run program again!\n");
unlink(FIFO);
}
exit(EXIT_SUCCESS);
}
and it is working very well. So I created code that have architecture described above:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO "/tmp/my_fifo"
int main()
{
pid_t fork_result;
pid_t fork_result2;
int pipe_fd;
int res;
char writer[3];
char reader[3];
res = mkfifo(FIFO,0777);
if (res == 0)
{
printf("FIFO created!\n");
fork_result = fork();
if (fork_result == -1)
{
fprintf(stderr, "fork error");
exit(EXIT_FAILURE);
}
if (fork_result == 0)
{
printf("CHILD 1\n");
pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
scanf("%s", writer);
res = write(pipe_fd,writer,3);
if (res == -1)
{
fprintf(stderr,"error writing to fifo\n");
exit(EXIT_FAILURE);
}
(void)close(pipe_fd);
exit(EXIT_SUCCESS);
}
else
{
printf("PARENt 1\n");
//don't forget pipe!
fork_result = fork();
pipe_fd = open(FIFO, O_RDONLY);
if (fork_result == 0)
{
printf("CHILD 2\n");
res = read(pipe_fd, reader, 3);
printf("Odczytano: 0: %c\n",reader[0]);
printf("Odczytano: 1: %c\n",reader[1]);
printf("Odczytano: 2: %c\n",reader[2]);
(void)close(res);
}
}
}
else
{
printf("deleting fifo\n");
unlink(FIFO);
}
exit(EXIT_SUCCESS);
}
Running sequence is like this:
PARENT 1
CHILD 1
CHILD 2
so in Parent 1 I'm opening FIFO to read, in child 1 I'm writing to FIFO and child 2 should read it. I mean in code because when I run it I can't even write anything to FIFO. In blocks in scanf("%s", writer); which worked in first program.
Am I using open() correctly? Do I need to use getpid() somewhere? Why it's blocking when I try to write to fifo.
The problem is that CHILD1 is opening the fifo with O_NONBLOCK, which will fail (with EWOULDBLOCK or EAGAIN) if no other process has the fifo open for reading. Now in the first program, the parent continues running after the fork and opens the fifo for reading before the child gets going and opens the write end, so it works. But in the second case, the parent does an extra fork first, which slows it down just enough that CHILD1 gets to its open command before PARENT or CHILD2 has opened the fifo for reading, so the CHILD1 open fails.
Get rid of the O_NONBLOCK and it works just fine (though you do open the fifo for reading in both PARENT and CHILD2, which is probably not what you want).
You have another issue if you want to read from the keyboard. If you run this from the shell, PARENT will exit immediately (more or less), so the shell will go back to reading commands from the keyboard, which means that CHILD1 and the shell will be fighting over the input. If on the other hand, you do what you originally describe and have PARENT wait reading from a pipe from CHILD2, it should do what you want.
Isn't it because you use twice the same variable fork_result? As you created another variable fork_result2, which you don't use, it is probably unintended.
I don't know if this will solve your problem, but at least using fork_result2 at the second fork would make it easier to understand...

UNIX Commands Implemented in C

For my Operating Systems class I have an assignment due that is built onto a previous assignment. Unfortunately my previous project doesn't work correctly in addition to me not knowing where I need to start for the next project. The code which I have below is suppose to mimic a simple UNIX/Linux shell with some additional commands that cannot be performed with execvp: background processing via the ampersand operator, the 'jobs' shell command: list the pids of all living child processes (i.e. not ones that have terminated), "reaping" of "zombie" processes, and the 'cd' shell command: change the shell's working directory.
I believe, everything but the "jobs" command, and "cd" command work, but I'm not sure why these two don't.
The next assignment is to add some I/O redirection in the form of "mysh$ cmd arg1 arg2 argN > file.out" which I don't know where to even really begin...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc, char **argv) {
char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
int jobs[100];
int jobList = 0;
int background;
ssize_t rBytes;
int aCount;
pid_t pid;
int status;
while(!feof(stdin)) {
pid = waitpid(-1, &status, WNOHANG);
if (pid > 0)
printf("waitpid reaped child pid %d\n", pid);
write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
rBytes = read(0, bBuffer, BUFSIZ-1);
if(rBytes == -1) {
perror("read");
exit(1);
}
bBuffer[rBytes-1] = '\0';
if(!strcasecmp(bBuffer, "exit")){
exit(0);
}
sPtr = bBuffer;
aCount = 0;
do {
aPtr = strsep(&sPtr, " ");
pArgs[aCount++] = aPtr;
} while(aPtr);
background = (strcmp(pArgs[aCount-2], "&") == 0);
if (background)
pArgs[aCount-2] = NULL;
if (strlen(pArgs[0]) > 1) {
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
} else if (pid == 0) {
jobs[jobList] = pid;
jobList++;
if(!strcasecmp(pArgs[0], "jobs")){
for(int i; i<jobList; i++) {
if(kill(jobs[i],0)==0){
printf(jobs[i]);
}
printf("these are jobs\n");
exit(1);
}
if(!strcasecmp(pArgs[0], "cd")){
int ret;
if (!pArgs[1])
strcpy(bBuffer, "pwd");
ret = chdir(pArgs[1]);
strcpy(bBuffer, "pwd");
exit(1);
}
fclose(stdin);
fopen("/dev/null", "r");
execvp(pArgs[0], pArgs);
exit(1);
} else if (!background) {
pid = waitpid(pid, &status, 0);
if (pid > 0)
printf("waitpid reaped child pid %d\n", pid);
}
}
}
return 0;
}
First you;ll want to parse your line and detect that you need to redirect to a file. So let;s say you use strsep or whatever and you found out output is going to file.out or input is coming from file.in.
At this point you want to redirect output using dup / dup2. For example, to redirect STDOUT:
int
do_redirect(int fileno, const char *name)
{
int newfd;
switch (fileno) {
case STDOUT_FILENO:
newfd = open(name, O_WRONLY | O_CREAT, S_IRUSR | S_IRUSR);
break;
}
if (newfd == -1) {
perror("open");
return -1;
}
return dup2(fileno, newfd);
}
/* ... */
pid = fork();
do_redirect(STDOUT_FILENO, name);
Things to note:
I didn't test the code - it might not even compile
I didn't do much error-checking - you should (the way I did for open)
You need to implement STDIN_FILENO redirection on your own
Note how I used a separate function, your main is WAY to large as it is
Your code has something like 7 levels of indentation - ever heard about arrow code ?
Since this is homework, I will not give you code directly.
dup, dup2 and freopen are good to look at for input/output redirection.
fork for starting a concurrent process (ampersand)
You are on the right track using waitpid to reap child processes.

Having issues with pipe, fork, dup2

I am using pipes, fork , dup2 to implement “ls | more” or “ls | sort” etc.
I am just not able to understand the issue here.
When I run my program, I get this error:
./a.out
Missing filename ("less --help" for help)
Why am I getting "less" ??
What is wrong with this code ? If I change “more” to “ls” again, it works fine. I mean, its like doing ls | ls.
#define STDIN 0
#define STDOUT 1
int main()
{
int fd[2];
int pid;
char *lschar[20]={"ls",NULL};
char *morechar[20]={"more",NULL};
pid = fork();
if (pid == 0) {
/* child */
int cpid;
cpid = fork();
if(cpid == 0) {
//printf("\n in ls \n");
pipe(fd);
dup2(fd[1], STDOUT);
close(fd[0]);
close (fd[1]);
execvp("ls",lschar);
} else if(cpid>0) {
waitpid(cpid, NULL,0);
dup2(fd[0],STDIN);
close(fd[0]);
close(fd[1]);
execvp("more", morechar);
}
} else if (pid > 0) {
/* Parent */
waitpid(pid, NULL,0);
}
return 0;
}
Appreciate your help.
Your main problem lies in your placement of the pipe() call. You must call it before you fork():
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#define STDIN 0
#define STDOUT 1
int main()
{
int fd[2];
int pid;
char *lschar[20]={"ls",NULL};
char *morechar[20]={"more", NULL};
pid = fork();
if (pid == 0) {
/* child */
int cpid;
pipe(fd);
cpid = fork();
if(cpid == 0) {
//printf("\n in ls \n");
dup2(fd[1], STDOUT);
close(fd[0]);
close (fd[1]);
execvp("ls",lschar);
} else if(cpid>0) {
dup2(fd[0],STDIN);
close(fd[0]);
close(fd[1]);
execvp("more", morechar);
}
} else if (pid > 0) {
/* Parent */
waitpid(pid, NULL,0);
}
return 0;
}
Otherwise, the more process doesn't have the correct file descriptors. Further, the waitpid() in your more process is problematic and unnecessary (more will wait for input on its own). If ls had a particularly long output the pipe could get full causing ls to block on its writes. The result is a deadlock and it waits forever. Hence, I've also removed the offending waitpid() call.
Also, if you make a good practice of checking the return values of functions like pipe() and dup2() this error would have been much easier to find -- you would have seen that your dup2() was failing.

Resources