Let's say, I have two programs - input.c & output.c
All I want to do is send some payload/characters in "half pyramid" format into another one using execl() function.
input.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define SIZE 1024
int length;
int main(int argc, char *argv[])
{
pid_t pid;
char *target;
//char payload[length+1];
char payload[SIZE + 1];
int status;
int i = 0;
if(argc < 2)
{
printf("Usage %s <length> <target>\n", argv[0]);
exit(EXIT_FAILURE);
}
length = atoi(argv[1]);
target = argv[2];
while(i < length)
{
pid = fork();
if(pid != 0)
{
waitpid(-1, &status, 0);
//exit(0);
}
if(pid == 0)
{
payload[i] = 'A';
payload[i + 1] = '\0';
execl(target, target, payload, NULL);
//printf("%s\n", payload);
}
++i;
}
return 0;
}
Commented passages are just for debugging purposes. Because As you can see (while trying), when you want just print it, everything works properly.
output.c (or if you want 'target.c')
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buffer[30];
strncpy(buffer, argv[1], sizeof(buffer));
printf("Output: %s\n", buffer);
return 0;
}
When I compile input.c like:
gcc input.c -o input
& output.c:
gcc output.c -o output
Ok. Now, everything is prepared. Let's say, I'd like to send a payload - length 6
./input 6 ./output
but all I get on output is just this (or simply with another junks characters):
Output: A
Output: 0A
Output: 0,A
Output: 0,�A
Output: 0,�8A
Output: 0,�8�A
I tried so many things, but all of them failed and output was still the same, as you can see above.
I'd be very grateful if anyone can help me and possibly show me where is problem. Can be problem in using fork() and execl() functions together?
Got it, you should not update payload in the child block code...
Here's a fix (cannot test it now) :
while(i < length)
{
pid = fork();
payload[i] = 'A';
payload[i + 1] = '\0';
if(pid != 0)
{
waitpid(-1, &status, 0);
//exit(0);
}
if(pid == 0)
{
execl(target, target, payload, NULL);
//printf("%s\n", payload);
}
++i;
}
[removed unrelated sentence]
EDIT (additional explanations) : payload updating must be in both parent and child code. If you don't understand why, I can add more explanation.
EDIT2 (as requested). You want update payload for the next forked child process. In your code, all child code is replaced by execl() into target code. So the fork() is executed only by the first parent process (the root one).
You have to update payload by the first parent and make it accessible too all the children. Putting it into this block won't work either :
// block only executed by the first parent.
if(pid != 0)
{
waitpid(-1, &status, 0);
}
Therefore, You must update it in a place both accessible by the parent and the child : after the fork(), before if(pid == 0) block.
In your code, you increment i in the common block, but the parent's payload is never updated. So in the child block, just before the execl(), your adding 'A\0' at the end of an uninitialized C string.
When your program forks, it creates a new process. This new process, after if(pid == 0), alters the payload, and runs exec (ie executes output, which then dies. I.e, your change of payload stays in the child, and does not affect the parent process. Only ++i does, which is why you're seeing unitialized data.
Move payload change before fork (or at least out of child-only block), so it's in the parent as well.
Related
I am writing a program about process
The process accept a bash command and run it with exec
I use fork() to create child process run exec inside it
and plan to do some post process in parent process
but for some reason the exec run properly but wait is not working poperly
"post process here" print put before exec's program terminate
It worked before but suddenly not working,
I have not idea what changes I have made to cause this problem
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
/* code */
for(int i = 0; i < argc; i++){
//delete argv 0 for convincent
argv[i] = argv[i + 1];
}
int pid = fork();
if(pid == -1){
return 1;
}
if(pid == 0){
execvp(argv[0], argv);
}else{
wait(NULL);
printf("post process here");
}
return 0;
}
First:
//delete argv 0 for convincent
argv[i] = argv[i + 1];
i would us a new array with bound [argc+1] otherwise you are in danger of an array out of bound error (bufferoverflow).
Second:
the called bash command is important. Some commands are just starting a deamon and finish themself.
I am a new to pipes in C.
I am trying to Write "hello" on the pipe from a child process & read the same from parent process, but I am getting unexpected output.
I using this piece of code:
#include<stdio.h>
#include<unistd.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int fds[2];
int ret;
char ch[20];
ret = pipe(fds);
if(ret == -1)
{
perror("pipe failed");
exit(0);
}
pid = fork();
if (pid == 0)
{
printf("Child process\n");
write(fds[1],"Hello",5);
}
if (pid > 0)
{
printf("Parent Process\n");
read(fds[0],ch,15);
printf("%s\n",ch);
}
return 0;
}
I am getting this as output :
Parent Process
Child process
Helloq.
I can't understand why this extra "q." is coming ??
You are trying to write 6 bytes but are setting the size to 5. You need to also send the '\0' at the end of Hello along.
Just change your write call to
write(fds[1],"Hello",6);
and you should be fine.
Use memset() function in your code before writing data into buffer, which fill memory with a constant byte. like,
memset(ch,'\0',20);
Full code may be help you.
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int fds[2];
int ret;
char ch[20];
memset(ch,'\0',20);
ret = pipe(fds);
if(ret == -1)
{
perror("pipe failed");
exit(0);
}
pid = fork();
if (pid == 0)
{
printf("Child process\n");
write(fds[1],"Hello",5);
}
if (pid > 0)
{
printf("Parent Process\n");
read(fds[0],ch,15);
printf("%s\n",ch);
}
}
Since you don't record how many bytes were read off the pipe, your code is printing the garbage that was already in the ch variable. There are numerous ways to deal with it. This code shows two of them. I used memset() to ensure that ch contained some data (and the assignment makes sure it is null terminated).
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
pid_t pid;
int fds[2];
int ret;
char ch[20];
memset(ch, 'X', sizeof(ch)-1);
ch[sizeof(ch)-1] = '\0';
ret = pipe(fds);
if (ret == -1)
{
perror("pipe failed");
exit(0);
}
pid = fork();
if (pid == 0)
{
printf("Child process\n");
write(fds[1], "Hello", 5);
}
else if (pid > 0)
{
printf("Parent Process\n");
int nbytes = read(fds[0], ch, 15);
printf("[%s]\n", ch);
printf("[%.*s]\n", nbytes, ch);
ch[nbytes] = '\0';
printf("[%s]\n", ch);
}
else
fprintf(stderr, "fork() failed\n");
return 0;
}
The code records how many bytes were written (truly diligent code would ensure that the correct amount of data was written, too). It prints the data 3 times — once using your original technique, then once using the number of bytes read off the pipe to limit the output, and then null-terminating the data so that it can be written as a simple string.
The %.*s conversion specification uses two values — a number and the string. The number is the maximum number of bytes that will be written. If the string is shorter than that, so be it. If the string is longer, the excess bytes are ignored.
Sample output:
Parent Process
[HelloXXXXXXXXXXXXXX]
[Hello]
[Hello]
Child process
This was the result of piping the program output. Visually, on the terminal, I usually got:
Parent Process
Child process
[HelloXXXXXXXXXXXXXX]
[Hello]
[Hello]
Both outputs are valid. Note how the first printing of the data also includes a number of the X's because there was no null byte read from the pipe.
Another alternative is to have the child write the null of the null-terminated string to the pipe: write(fds[1], "Hello", sizeof("Hello"));. Other options include writing the length of the string on the pipe followed by the data and then reading the length and that many bytes of data. This is a minor variant on a TLV (type, length, value) encoding system — the type is not explicitly specified as it is assumed to be char.
//code for foo (run executable as ./a.out)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
int main (int argc, char **argv) {
pid_t pid;
pid = fork();
int i = 1;
char *parms[] = {"test2", "5", NULL}; //test executable named test2
if(pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
else if(pid == 0) {
printf("Child pid is %d\n", pid);
i = execv("test2", parms); //exec call to test with a param of 5
}
else {
wait(NULL);
}
printf("I is now %d\n", i); //i is still 1 here, why?
return 0;
}
Hey everybody, I am trying to learn a little bit about fork and execv() calls. I make my foo.c program above make a call to a file I have named test.c. I fork a child and have the child make a call to execv, which will just add 10 to the parameter read in. I am unsure of why the variable does not change, at the bottom of my foo.c function. Does the call need to be a pointer or return an address? Any help would be greatly appreciated. Thanks
Code for test.c (executable named test2)
#include <stdio.h>
int main(int argc, char ** argv[]) {
int i = atoi(argv[1]);
i = i +10;
printf("I in test is %d\n", i);
return i;
}
You only call execv() in the child process. The exec() family functions never return if it runs successfully. See evec(3):
The exec() functions only return if an error has occurred. The return value is -1, and errno is set to indicate the error.
You printed the value of i in the parent process, it never changed in the parent process.
To get the exit status from the child process, you can make use of wait() or waitpid():
else {
int waitstatus;
wait(&waitstatus);
i = WEXITSTATUS(waitstatus);
}
I am learning the way to use ordinary pipeline in linux for the communication between parent and child process. The basic task is just to send a message to the child process from parent process, and then the child do some conversion and pass the result back to the parent. My result shown is some random character like ���. I have been contemplating for a long while and still couldn't figure out the bug. Thanks for your help.
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define READ_END 0
#define WRITE_END 1
void convert(char* str);
int main(int argc, char *argv[]){
int pid; /* Process ID */
int status;
char *input;
char *read_msg_c;
char *read_msg_p;
int pfd1[2], pfd2[2];
if (argc !=2){/* argc should be 2 for correct execution */
/* We print argv[0] assuming it is the program name */
printf("Please provide the string for conversion \n");
exit(-1);
}
input = argv[1];
if(pipe(pfd1) < 0 || pipe(pfd2) < 0){
printf("Failed to create a pipe between parent and child \n");
exit(-1);
}
if((pid = fork()) < 0){ /* Fork the process */
printf("Fork error \n");
exit(-1);
}
else if(pid > 0){ /* Parent code */
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
printf("Process ID of the parent is %d. \n", getpid()); /* Print parent's process ID */
write(pfd1[WRITE_END],input,strlen(input)+1);
close(pfd1[WRITE_END]);
read(pfd2[READ_END],read_msg_p,strlen(input)+1);
printf("%s\n",read_msg_p);
close(pfd2[READ_END]);
}
else if(pid == 0){ /* Child code */
close(pfd1[WRITE_END]);
close(pfd2[READ_END]);
printf("Process ID of the child is %d. \n", getpid()); /* Print child's process ID */
read(pfd1[READ_END],read_msg_c, strlen(input)+1);
printf("Child: Reversed the case of the received string. \n");
write(pfd2[WRITE_END],read_msg_c,strlen(input)+1);
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
exit(0); /* Child exits */
}
}
void convert(char *str){
int i = 0;
while (str[i]){
if (isupper(str[i])){
str[i] = tolower(str[i]);
}
else if (islower(str[i])){
str[i] = toupper(str[i]);
}
i++;
}
}
Your primary bug is that your variables read_msg_p and read_msg_c are uninitialized pointers.
Make them into arrays:
char read_msg_p[1024];
char read_msg_c[1024];
You seem to be missing <stdio.h> (but you don't really need <sys/types.h> any more). You should error check your reads and writes; your reads will probably use a different maximum size once you've allocated the space for them. Etc.
I spotted the problem by looking at the compiler warnings:
$ gcc -O3 -g -std=c99 -Wall -Wextra pipes-14420398.c -o pipes-14420398
pipes-14420398.c: In function ‘main’:
pipes-14420398.c:40:22: warning: ‘read_msg_p’ may be used uninitialized in this function [-Wuninitialized]
pipes-14420398.c:52:22: warning: ‘read_msg_c’ may be used uninitialized in this function [-Wuninitialized]
$
Ignore the line numbers; I'd moderately seriously hacked your code by the time these were the only warnings left. But the lines in question are the read() calls.
Example output form the hacked code, working correctly.
$ ./pipes-14420398 string-to-convert
Process ID of the parent is 37327.
Process ID of the child is 37328.
Child read 18 bytes: <<string-to-convert>>
Parent read 18 bytes: <<string-to-convert>>
$
Note that the code below reads 18 bytes (including the null), but does not print the null (because of the nbytes-1 argument to printf().
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define READ_END 0
#define WRITE_END 1
int main(int argc, char *argv[])
{
int pid; /* Process ID */
char *input;
char read_msg_c[1024];
char read_msg_p[1024];
int pfd1[2], pfd2[2];
if (argc !=2){/* argc should be 2 for correct execution */
/* We print argv[0] assuming it is the program name */
fprintf(stderr, "Usage: %s string-to-convert\n", argv[0]);
exit(-1);
}
input = argv[1];
if(pipe(pfd1) < 0 || pipe(pfd2) < 0){
printf("Failed to create a pipe between parent and child \n");
exit(-1);
}
if((pid = fork()) < 0){ /* Fork the process */
printf("Fork error \n");
exit(-1);
}
else if(pid > 0){ /* Parent code */
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
printf("Process ID of the parent is %d. \n", getpid()); /* Print parent's process ID */
write(pfd1[WRITE_END], input, strlen(input)+1);
close(pfd1[WRITE_END]);
int nbytes = read(pfd2[READ_END], read_msg_p, sizeof(read_msg_p));
if (nbytes <= 0)
printf("Parent: read failed\n");
else
printf("Parent read %d bytes: <<%.*s>>\n", nbytes, nbytes-1, read_msg_p);
close(pfd2[READ_END]);
}
else if(pid == 0){ /* Child code */
close(pfd1[WRITE_END]);
close(pfd2[READ_END]);
printf("Process ID of the child is %d. \n", getpid()); /* Print child's process ID */
int nbytes = read(pfd1[READ_END], read_msg_c, sizeof(read_msg_c));
if (nbytes <= 0)
printf("Child: read failed\n");
else
{
printf("Child read %d bytes: <<%.*s>>\n", nbytes, nbytes-1, read_msg_c);
write(pfd2[WRITE_END], read_msg_c, nbytes);
}
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
exit(0); /* Child exits */
}
}
As noted by WhozCraig, there are numerous other changes that could be made. This, however, gets things working reasonably cleanly. You were very close to OK.
Note the debugging techniques:
Compile with high warning levels and fix all warnings.
Print information as it becomes available (or run in a debugger and observe the information as it becomes available).
I need help with this sample application. When I run it, it gets stuck after the child process prints "Child sending!".
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#define INPUT 0
#define OUTPUT 1
int main()
{
int fd1[2];
int fd2[2];
int pid;
if (pipe(fd1) < 0)
exit(1);
if (pipe(fd2) < 0)
exit(1);
if ((pid = fork()) < 0)
{
perror("fork");
exit(1);
}
else if (pid == 0)
{
close(fd1[INPUT]);
close(fd2[OUTPUT]);
char *str = "Hello World!";
printf("Child sending!\n");
write(fd1[OUTPUT], str, strlen(str));
char *bufferc = (char *)malloc(1000);
char *readbufferc = (char *)malloc(80);
int rdc;
int gotdata = 0;
while (gotdata == 0)
while ((rdc = read(fd2[INPUT], readbufferc, sizeof(readbufferc))) > 0)
{
strncat(bufferc,readbufferc,rdc);
gotdata = 1;
}
printf("Child received: %s",bufferc);
free(readbufferc);
free(bufferc);
exit(0);
}
else
{
close(fd1[OUTPUT]);
close(fd2[INPUT]);
int rd;
char *buffer = (char *)malloc(1000);
char *readbuffer = (char *)malloc(80);
int gd = 0;
while (gd == 0)
while ((rd = read(fd1[INPUT],readbuffer, sizeof(readbuffer))) > 0)
{
strncat(buffer, readbuffer,rd);
gd = 1;
}
printf("Parent received: %s\n",buffer);
free(readbuffer);
printf("Parent sending!");
write(fd2[OUTPUT], buffer, strlen(buffer));
free(buffer);
}
return 0;
}
On a side note, is there a way to debug when I use fork because gdb automatically goes to the parent process
After the child writes to the parent, it must close the write end of the pipe so the parent knows it has reached EOF.
There are many bugs in your code. Why are you using fd2 without initializing it? Remove it.
Now its stuck at "Child sending" because pipe read is a blocking call and you are putting it in a while loop which will never return. Please refer to man page of pipe.
If you want to break that while loop, close all write ends of that pipe.
Also to debug child process, use gdb command follow-fork-mode as child before call to fork() while debugging.
Several things wrong:
fd2 is just never initialized.
The parent will never exit this:
while ((rd = read(fd1[INPUT],readbuffer, sizeof(readbuffer))) > 0)
{
strncat(buffer, readbuffer,rd);
gd = 1;
}
If there is no data to read, read will block and just not return. The only thing that would make it exit is if the connection was closed and the child doesn't close it.
You are calling read() in the expectation that if there is nothing to read, it will return with zero bytes read. However, what you are seeing is because read() is waiting for some data before returning. To address this, you need to do one of two things:
set your socket to do non-blocking reads (not recommended)
use select() or poll() to see whether there is some data to read before you read it
Also, several other points:
don't cast the returns from malloc()
check that malloc() does not return NULL
replace the whole gotdata thing with a break instruction