I have two executable. The first one (target) print data to stdout, read data from stdin and print another data to stdout. The second one (wrapper) is a wrapper of the first one. It forks, it changes child stdin and stdout then execute target.
I want that wrapper gets target's stdout, print it, send data to target's stdin and get last target's data. But the command block without printing anything.
The target code (I have one constraint, I can't modify this code):
#include <stdio.h>
int main(){
char buffer[0x100] = { 0 };
printf("First line\n");
printf("Input:");
scanf("%s", buffer);
printf("Last line\n");
return 0;
}
The wrapper code:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#define READING_IDX 0
#define READING_IDX 0
#define WRITING_IDX 1
int main(int argc, char *argv[], char *env[]){
pid_t pid = 0;
int pipeStdin[2] = {0};
int pipeStdout[2] = {0};
char buffer[1024] = {0};
int ret = 0;
if (pipe(pipeStdout) == -1){
perror("pipe");
return 1;
}
if (pipe(pipeStdin) == -1){
perror("pipe");
return 1;
}
if ((pid = fork()) == -1){
perror("fork");
return 1;
}
else if (pid == 0){
close(pipeStdin[WRITING_IDX]);
close(pipeStdout[READING_IDX]);
if (dup2(
pipeStdin[READING_IDX],
STDIN_FILENO
) == -1){
perror("dup2");
return 1;
}
if (dup2(
pipeStdout[WRITING_IDX],
STDOUT_FILENO
) == -1){
perror("dup2");
return 1;
}
if (execve(
"./target",
argv + 1,
env
) == -1){
perror("execve");
return 1;
}
}
else{
close(pipeStdin[READING_IDX]);
close(pipeStdout[WRITING_IDX]);
ret = read(
pipeStdout[READING_IDX],
buffer,
1023);
if(ret == -1){
perror("read");
return 1;
}
printf("%d\n", ret);
buffer[ret] = 0;
printf("%s\n", buffer);
ret = write(
pipeStdin[WRITING_IDX],
"Response\n",
sizeof("Response\n")
);
}
return 0;
}
I build this two executable with:
gcc -o target target.c && gcc -o wrapper wrapper.c
Thanks to any help.
I see that child process block at read syscall. I think it is at the scanf line. But target does not write any data.
The strace logs:
>! execve("./wrapper", ["./wrapper"], 0x7ffca81b4f78 /* 50 vars */) = 0
brk(NULL) = 0x55bc56890000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (Aucun fichier ou dossier de ce type)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=310009, ...}) = 0
mmap(NULL, 310009, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f28eaf36000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260A\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1820400, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f28eaf34000
mmap(NULL, 1832960, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f28ead74000
mprotect(0x7f28ead96000, 1654784, PROT_NONE) = 0
mmap(0x7f28ead96000, 1339392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f28ead96000
mmap(0x7f28eaedd000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x169000) = 0x7f28eaedd000
mmap(0x7f28eaf2a000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b5000) = 0x7f28eaf2a000
mmap(0x7f28eaf30000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f28eaf30000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f28eaf35500) = 0
mprotect(0x7f28eaf2a000, 16384, PROT_READ) = 0
mprotect(0x55bc55a1b000, 4096, PROT_READ) = 0
mprotect(0x7f28eafa9000, 4096, PROT_READ) = 0
munmap(0x7f28eaf36000, 310009) = 0
pipe([3, 4]) = 0
pipe([5, 6]) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f28eaf357d0) = 13384
strace: Process 13384 attached
[pid 13383] close(5 <unfinished ...>
[pid 13384] close(6 <unfinished ...>
[pid 13383] <... close resumed> ) = 0
[pid 13384] <... close resumed> ) = 0
[pid 13383] close(4 <unfinished ...>
[pid 13384] close(3 <unfinished ...>
[pid 13383] <... close resumed> ) = 0
[pid 13384] <... close resumed> ) = 0
[pid 13383] read(3, <unfinished ...>
[pid 13384] dup2(5, 0) = 0
[pid 13384] dup2(4, 1) = 1
[pid 13384] execve("./target", [], 0x7ffec0e88de8 /* 50 vars */) = 0
[pid 13384] brk(NULL) = 0x5564da73c000
[pid 13384] access("/etc/ld.so.preload", R_OK) = -1 ENOENT (Aucun fichier ou dossier de ce type)
[pid 13384] openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[pid 13384] fstat(3, {st_mode=S_IFREG|0644, st_size=310009, ...}) = 0
[pid 13384] mmap(NULL, 310009, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fbfc376b000
[pid 13384] close(3) = 0
[pid 13384] openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
[pid 13384] read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260A\2\0\0\0\0\0"..., 832) = 832
[pid 13384] fstat(3, {st_mode=S_IFREG|0755, st_size=1820400, ...}) = 0
[pid 13384] mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbfc3769000
[pid 13384] mmap(NULL, 1832960, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fbfc35a9000
[pid 13384] mprotect(0x7fbfc35cb000, 1654784, PROT_NONE) = 0
[pid 13384] mmap(0x7fbfc35cb000, 1339392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7fbfc35cb000
[pid 13384] mmap(0x7fbfc3712000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x169000) = 0x7fbfc3712000
[pid 13384] mmap(0x7fbfc375f000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b5000) = 0x7fbfc375f000
[pid 13384] mmap(0x7fbfc3765000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fbfc3765000
[pid 13384] close(3) = 0
[pid 13384] arch_prctl(ARCH_SET_FS, 0x7fbfc376a500) = 0
[pid 13384] mprotect(0x7fbfc375f000, 16384, PROT_READ) = 0
[pid 13384] mprotect(0x5564d8c2f000, 4096, PROT_READ) = 0
[pid 13384] mprotect(0x7fbfc37de000, 4096, PROT_READ) = 0
[pid 13384] munmap(0x7fbfc376b000, 310009) = 0
[pid 13384] fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
[pid 13384] brk(NULL) = 0x5564da73c000
[pid 13384] brk(0x5564da75d000) = 0x5564da75d000
[pid 13384] fstat(0, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
[pid 13384] read(0, kolk
<unfinished ...>
[pid 13383] <... read resumed> 0x7ffec0e888d0, 1023) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
[pid 13383] --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
[pid 13383] read(3, <unfinished ...>
[pid 13384] <... read resumed> 0x5564da73d270, 4096) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
[pid 13384] --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
[pid 13384] read(0, <unfinished ...>
[pid 13383] <... read resumed> 0x7ffec0e888d0, 1023) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
[pid 13384] <... read resumed> 0x5564da73d270, 4096) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
[pid 13383] --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
[pid 13384] --- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
[pid 13383] read(3, <unfinished ...>
[pid 13384] read(0,
Your problem is with stdio buffering, and if you could modify the target, you would just do:
#include <stdio.h>
int main(){
char buffer[0x100] = { 0 };
printf("First line\n");
printf("Input:");
fflush(stdout); // this is what you'd add
scanf("%s", buffer);
printf("Last line\n");
return 0;
}
That is a bug in the target. Here's how it plays out:
printf("First line\n"); - The text First line\n is put into the stdio's stdout buffer. Interactive programs have a TTY for stdout, and when that's the case, if stdio sees a \n, it flushes the stdout buffer. But you're writing into a pipe, so stdio doesn't buffer by line, but rather by some arbitrary size, e.g. 4096 bytes.
printf("Input:"); - The text Input: is put into the stdio's stdout buffer. The buffer now contains 17 characters.
If fflush(stdout); is called, then newline or not - stdio will flush its stdout buffer to the system's stdout file descriptor, at which point it is written to the pipe.
but since the target doesn't call it:
scanf("%s", buffer); - The target blocks, waiting for input.
Control switches to the wrapper:
read(pipeStdout[READING_IDX], buffer, 1023); - Nothing has been yet written by the target to its stdout, so the wrappers stdin is empty. That means read() blocks until there's any input.
That's where your program hangs. If the target called fflush() then the pipe would have contained 17 characters and read() would have succeeded.
Unfortunately, adding setbuf(stdout, NULL) to the wrapper doesn't work, because execve(2) destroys that buffer and creates a new one in the new process image.
On Linux, there's a program called stdbuf which fixes that. You can verify by running your wrapper as follows:
stdbuf -o 0 ./wrapper
Apparently the way stdbuf works is by running the sub-program with an LD_PRELOAD library that calls setbuf(3).
I don't think it's reasonable to try and do that yourself, but since your question is tagged with linux, you can replace your
if (execve(
"./target",
argv + 1,
env
) == -1){
perror("execve");
return 1;
}
with:
if (system("stdbuf -o 0 ./target") == -1){
perror("system");
return 1;
}
And that would workaround the bug in the target.
However, once you've done that, you'll get the following output:
17
First line
Input:
Whereas it should be:
17
First line
Input:
10
Last line
(10 being the length of Response\n).
I Assume that you want to interleave this interaction because the writes from the wrapper have to be related to the writes from the target.
But you wouldn't know when the target finished writing its part - it could stop writing due to scheduling, or because the pipe buffer is full, or because it just takes long to produce output.
You actually need a protocol between the wrapper and the target - something that signifies "end of message, like "roger that, over".
But given, again, that you can't change the target, for the purpose of this question we will have to assume that:
There are only 3 operations (from the POV of the wrapper: read-write-read).
All communication fits in all the buffers - the wrappers' read buffer (1023 bytes), the target's read buffer (0x100 bytes), the system's pipe buffer (usually 4096 bytes) and any of stdio's stdin buffers.
Everything is happy and peachy, there are no short reads or short writes.
Under these conditions, you just need to add the last read:
ret = read(
pipeStdout[READING_IDX],
buffer,
1023);
if(ret == -1){
perror("read");
return 1;
}
printf("read ret: %d\n", ret);
buffer[ret] = 0;
printf("read buf: '%s'\n", buffer);
ret = write(
pipeStdin[WRITING_IDX],
"Response\n",
sizeof("Response\n")
);
ret = read(
pipeStdout[READING_IDX],
buffer,
1023);
if(ret == -1){
perror("read");
return 1;
}
printf("read ret: %d\n", ret);
buffer[ret] = 0;
printf("read buf: '%s'\n", buffer);
(I added read buf and read ret for clarity).
Related
I am trying to make my program able to interact with another program which takes input. I tried that with fork() by executing execv("/bin/sh", NULL) in the child process. Now it should be replaced with the new process, /bin/sh. Now I want my parent process to communicate with /bin/sh like send commands. I tried that using pipes, but I guess I am doing something wrong. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int pipefd[2];
pipe(pipefd);
pid_t pid = fork();
if(pid == 0) { // child code
close(0); // close stdin fd
dup(pipefd[0]); // duplicate the pipe fd (will return 0)
close(pipefd[1]); // close the pipefd 1 (I dont need to write for child now)
execv("/bin/sh", NULL); // execute /bin/sh
} else { // parent code
close(1); // close stdout fd
dup(pipefd[1]); // duplicate the pipe fd (will return 1)
close(pipefd[0]); // close the pipefd 0 (I dont need to read right now)
}
write(0, "touch xxx\n", 10); // try to run a command with the parent process
waitpid(-1, WNOHANG, 0); // wait for the child to exit
}
I tried to write to stdin from the parent, because I thought /bin/sh takes input from stdin I can write to it. Sadly my command isn't executed. After that I can enter something, but it does nothing. Here is the output of strace:
execve("./a.out", ["./a.out"], 0x7fffffffe870 /* 41 vars */) = 0
brk(NULL) = 0x555555559000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffffffe710) = -1 EINVAL (Das Argument ist ungültig)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (Datei oder Verzeichnis nicht gefunden)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=210476, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 210476, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7f92000
close(3) = 0
openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`|\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0#\0\0\0\0\0\0\0#\0\0\0\0\0\0\0#\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0#\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 80, 848) = 80
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0T\246\344\4\347\334\35\347\301CJ\0\267\261\2552"..., 68, 928) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2154488, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7f90000
pread64(3, "\6\0\0\0\4\0\0\0#\0\0\0\0\0\0\0#\0\0\0\0\0\0\0#\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 1884632, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7dc3000
mmap(0x7ffff7de9000, 1359872, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7ffff7de9000
mmap(0x7ffff7f35000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x172000) = 0x7ffff7f35000
mmap(0x7ffff7f81000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7ffff7f81000
mmap(0x7ffff7f87000, 33240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7f87000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7dc1000
arch_prctl(ARCH_SET_FS, 0x7ffff7f91580) = 0
mprotect(0x7ffff7f81000, 12288, PROT_READ) = 0
mprotect(0x555555557000, 4096, PROT_READ) = 0
mprotect(0x7ffff7ffb000, 8192, PROT_READ) = 0
munmap(0x7ffff7f92000, 210476) = 0
pipe([3, 4]) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ffff7f91850) = 29114
close(1) = 0
dup(4) = 1
close(3) = 0
write(0, "touch xxx\n", 10touch xxx
) = 10
wait4(-1,
I hope someone can help.
Your parent process (the else block) is closing and replacing fd 1, corresponding to stdout, meaning writes to its own stdout will go to the stdin of the child. But you write to the parent's unmodified fd 0, stdin.
Change the code so the parent writes to fd 1, stdout, which is attached to the pipe that leads to the child's fd 0, stdin.
There are other issues here as well (which compiling with any level of warnings enabled would make clear). Fix those, and this should work (assuming, as you state, your system is liberal in allowing execv to accept NULL as the second argument).
// This is my code for reading a file from command line arguments and storing it in another file.//
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h> //for system calls such as dup(),pipe(),etc...
#include <sys/wait.h>
#define COUNT_PROGRAM "b"
#define CONVERT_PROGRAM "c"
int main(int argc, char *argv[])
{
if (argc != 3)
{
fprintf(stderr,"%s","ERROR : argument count not satisfied!\n");
exit(1);
}
/* It is important to check all system calls (open, creat, dup, etc.) for a return value < 0,
particularly -1 because such a return value means an error has occurred. */
int fd_in = open(argv[1], O_RDONLY);
if (fd_in < 0)
{
fprintf(stderr,"%s", "ERROR : file to be read does not exist!\n");
exit(1);
}
int fd_out = creat(argv[2], 0644); /* mode = permissions, here rw-r--r-- */
if (fd_out < 0)
{
fprintf(stderr,"%s", "ERROR : file could not be created!\n");
exit(1);
}
if(dup(fd_in) < 0)//dup fd_in to 3
fprintf(stderr , "ERROR assigning STREAM 3 to fd_in");
if(dup(fd_out) < 0)//dup fd_in to 4
fprintf(stderr , "ERROR assigning STREAM 4 to fd_out");
//dup fd_in to 0
close(0);
dup(fd_in);
close(3);
//dup fd_out to 1
close(1);
dup(fd_out);
close(4);
int fd[2];
pipe(fd);
pid_t pid_child_1, pid_child_2;
int status;
if ((pid_child_1 = fork()) != 0)
{
if ((pid_child_2 = fork()) != 0)
{
close(fd[0]);
close(fd[1]);
wait(&status);
wait(&status);
// fprintf(stderr , "\nstatus of child_1 = %d",wait(&status));
// fprintf(stderr , "\nstatus of child_2 = %d",wait(&status));
}
else
{
// close(fd[1]);
// dup(1);
dup2(fd[1],1);
close(fd[0]);
execl( CONVERT_PROGRAM, CONVERT_PROGRAM, (char*) NULL);
}
}
else
{
// close(fd[0]);
// dup(0);
dup2(fd[0],0);
close(fd[1]);
execl( COUNT_PROGRAM , COUNT_PROGRAM ,(char*) NULL);
}
}
After compiling my text file which should contain the output is empty.
THOSE PROGRAMS ARE WORKING WELL ALONE.
Here I am adding the strace result after running the strace command. Here it is printing 7 on the screen and no output in the text file.
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\35\2\0\0\0\0\0"..., 8 32) = 832
strace: Process 6018 attached
strace: Process 6019 attached
[pid 6019] read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\35\2\0\0\ 0\0\0"..., 832) = 832
[pid 6018] read(4, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\35\2\0\0\ 0\0\0"..., 832) = 832
[pid 6019] read(0, "hello my name is himanshu KAUSHI"..., 4096) = 35
[pid 6019] read(0, "", 4096) = 0
[pid 6019] write(1, "HELLO MY NAME IS HIMANSHU kaushi"..., 35) = 35
[pid 6019] +++ exited with 0 +++
[pid 6018] read(0, "HELLO MY NAME IS HIMANSHU kaushi"..., 4096) = 35
[pid 6017] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6019, si_u id=1062, si_status=0, si_utime=0, si_stime=0} ---
[pid 6018] read(0, "", 4096) = 0
[pid 6018] write(2, "\n7", 2
7) = 2
[pid 6018] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6018, si_uid=1062, si_ status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
I changed standard table descriptors in 1st dup2(f_dsc[1],3)
command and I got the output in the text file but then my first program stooped running.
#include<stdio.h>
#include<ctype.h>
int main()
{
char c;
int count=0;
while(1)
{
c=getchar();
if(c==EOF)
{
break;
}
if(!isalpha(c))
{
count++;
}
}
fprintf(stderr,"\n%d",count);
}
this is my simple b program.
#include<stdio.h>
#include<ctype.h>
int main()
{
char c;
int count=0;
while(1)
{
c=getchar();
if(c==EOF)
{
break;
}
if(islower(c))
{
c=toupper(c);
}
else
{
c=tolower(c);
}
putchar(c);
}
return 0;
}
and this is my simple c program.
When I try your program, it correctly executes equivalently to
c <a.txt | b >b.txt
So, let's consider what may be different in your setup. Although you write
/* It is important to check all system calls (open, creat, dup, etc.) for a return value < 0,
particularly -1, because such a return value means an error has occurred. */
you don't check the return value of the execl calls (or just put perror("…"); after them to see if they fail). Perhaps b or c is a script without a first line like
#!/bin/sh
You can get away without such a line when calling the script from a shell (I guess you mean that when you yell THOSE PROGRAMS ARE WORKING WELL ALONE), but not when using execl.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I have a shell scripts ,which will execute something like below:
file:example.sh
#!/bin/sh
#some other code
echo "someconfig">config_file
I hope the config_file just contain someconfig ,but strange thing happen with config_file,It has a single 'c' in the first line. I found no printf('c') in the parent process who execute the example.sh
My process will call the linux c function to execute the script this way:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
int execute_shell(const char *shell)
{
pid_t pid;
int iRet = 0;
if((pid = fork()) < 0)
{
return -1;
}
else if (pid == 0)
{
execlp("sh", "sh", "-c", shell, (char*)0);
exit(127);
}
else
{
while (waitpid(pid, &iRet, 0) < 0) {
if (errno != EINTR) {
iRet = -1;
break;
}
}
}
if(WIFEXITED(iRet) == 0)
{
return -1;
}
if(WEXITSTATUS(iRet) != 0)
{
return -1;
}
return 0;
}
int main()
{
char shell_cmd[1024]="./example.sh";
if( execute_shell(shell_cmd) == -1 )
{
// handle error
}
/*other code blew,may be will write to stdout*/
return 0;
}
Sometimes the config file looks strange,not what the shell scripts echo.
I use the cmd to analysis the possibility:
strace -f ./fork
[pid 12235] open("config_file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[pid 12235] fcntl(1, F_GETFD) = 0
[pid 12235] fcntl(1, F_DUPFD, 10) = 10
[pid 12235] fcntl(1, F_GETFD) = 0
[pid 12235] fcntl(10, F_SETFD, FD_CLOEXEC) = 0
[pid 12235] dup2(3, 1) = 1
[pid 12235] close(3) = 0
[pid 12235] fstat(1, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
[pid 12235] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbabb409000
[pid 12235] write(1, "someconfig\n", 11) = 11
[pid 12235] dup2(10, 1) = 1
[pid 12235] fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
[pid 12235] close(10) = 0
[pid 12235] rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
[pid 12235] read(255, "", 52) = 0
[pid 12235] exit_group(0) = ?
[pid 12235] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 12235
I do not understand what the meaning of function = num?
I will appreciate if someone analysis what the meaning of the strace output.
I suspect the parent and child write to the stdout,leading to strange output in config.
In my project we just use the linux c code execute a shell scripts which would echo someconfig to config_file,It run 6 months normally,but one day the config looks strange(two machine,with the same error,first line with c not what it echo to).
I just want to talk if there any possibility this happen,to have a direction to fix the problem.
After analysis the strace output ,child process execute some fd operation,wich make sure child and parent echo to differnet fd.So I think there is no possibility to make the config mess.
the following proposed code:
cleanly compiles
performs the desired functionality
properly handles any failures seen in the sub function
properly calls the function: execlp()
Here is what I fixed:
the call (and results) of execlp()
the handling of the call to fork() when it fails
the call to waitpid() when it returns an error.
added include statements for: <sys/types.h> and <sys/wait.h> for the function: waitpid()
replaced any calls to _exit() with exit() since most beginning programmers have no idea about the result of using _exit() rather than exit()
added place to handle the returned value from the call to: execute_shell()
and now, the proposed code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
int execute_shell(const char *shell)
{
pid_t pid;
pid = fork();
if( pid < 0 )
{
perror( "fork failed" );
return -1;
}
else if (pid == 0)
{ // child process
execlp("sh", "sh", "-c", shell, (char*)0);
perror( "execlp failed" );
return -2;
}
else
{ // parent process
int iRet;
if( waitpid(pid, &iRet, 0) == -1 )
{
return -3;
}
if (errno != EINTR)
{
return -4;
}
if(WIFEXITED(iRet) == 0)
{
return -5;
}
if(WEXITSTATUS(iRet) != 0)
{
return -6;
}
}
return 0;
}
int main( void )
{
char shell_cmd[1024]="./example.sh";
if( execute_shell(shell_cmd) )
{
// handle error
}
/*other code blew,may be will write to stdout*/
return 0;
}
I created a file: example.sh that contained:
#!/bin/sh
#some other code
echo "someconfig" > config_file
then ran the command:
chmod 777 example.sh
to make it executable, then ran the program I posted.
The result is nothing displayed on the terminal, however, now there is a new file: config_file that contains:
someconfig
I ran the code I posted several times and the file config_file contents did not change
I.E. no stray character 'c'
s
What have you not told us?
I have written the below code for better understanding of the file descriptors in c language.
int main(){
int p[2], n=7, r;
pipe(p);
if(fork() == 0){
//close(p[0]);
printf("child: %d\n", getpid());
write(p[1], &n, sizeof(n));
}
//the below code will be executed by parent and child process
printf("shared print: %d\n", getpid());
r = read(p[0], &n, sizeof(n));
printf("shared print, value from child: %d, pid: %d\n", n, getpid());
printf("read result: %d, pid: %d\n", r, getpid());
}
I'm getting the following result:
shared print: 332
child: 333
shared print: 333
shared print, value from child: 7, pid: 332
read result: 4, pid: 332
If the first line from child is uncommented I'm getting:
shared print: 339
child: 340
shared print: 340
shared print, value from child: 7, pid: 339
shared print, value from child: 7, pid: 340
read result: 4, pid: 339
read result: -1, pid: 340
Is anyone who can explain me what is happening when the read descriptor is closed ? My expectation was the code under the child block to be executed everytime by both processes, but this is happen only when the close(p[0]) is enabled in child. Why ?
You have a race condition: two processes are calling read, but there is only enough data for one of them.
If the parent reads first, and the child will hang - but since it's in the background, you probably didn't notice.
If the child reads first, the parent will hang, and you would notice.
Using strace -f ./foo helps show what's going on:
execve("./foo", ["./foo"], [/* 52 vars */]) = 0
// loading libraries
brk(NULL) = 0x55fd6dc09000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcf9ebce000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=168993, ...}) = 0
mmap(NULL, 168993, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcf9eba4000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\4\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1689360, ...}) = 0
mmap(NULL, 3795296, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcf9e60f000
mprotect(0x7fcf9e7a4000, 2097152, PROT_NONE) = 0
mmap(0x7fcf9e9a4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x195000) = 0x7fcf9e9a4000
mmap(0x7fcf9e9aa000, 14688, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fcf9e9aa000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcf9eba2000
arch_prctl(ARCH_SET_FS, 0x7fcf9eba2700) = 0
mprotect(0x7fcf9e9a4000, 16384, PROT_READ) = 0
mprotect(0x55fd6c47b000, 4096, PROT_READ) = 0
mprotect(0x7fcf9ebd1000, 4096, PROT_READ) = 0
munmap(0x7fcf9eba4000, 168993) = 0
pipe([3, 4]) = 0
// the fork() function actually calls `clone` these days
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fcf9eba29d0) = 6640
strace: Process 6640 attached
[pid 6639] getpid() = 6639
// the processes are running at the same time, so you get a lot of <unfinished ...> even for short-duration syscalls
[pid 6640] getpid( <unfinished ...>
[pid 6639] fstat(1, <unfinished ...>
[pid 6640] <... getpid resumed> ) = 6640
// fstat is needed before the first write, to know whether stdout should be line-buffered or fully-buffered
// this happens in *both* processes since you didn't do any writes before fork()ing
// (and if you did, you would have to call fflush)
[pid 6639] <... fstat resumed> {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
[pid 6640] fstat(1, <unfinished ...>
// brk is used to implement malloc, for stdout's buffer
[pid 6639] brk(NULL <unfinished ...>
[pid 6640] <... fstat resumed> {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
[pid 6639] <... brk resumed> ) = 0x55fd6dc09000
[pid 6640] brk(NULL <unfinished ...>
[pid 6639] brk(0x55fd6dc2a000 <unfinished ...>
[pid 6640] <... brk resumed> ) = 0x55fd6dc09000
[pid 6639] <... brk resumed> ) = 0x55fd6dc2a000
[pid 6640] brk(0x55fd6dc2a000 <unfinished ...>
// note mixed output since strace and the program are writing to the same place
// note also that if output wasn't to a TTY, buffering would delay the writes.
[pid 6639] write(1, "shared print: 6639\n", 19shared print: 6639
<unfinished ...>
[pid 6640] <... brk resumed> ) = 0x55fd6dc2a000
[pid 6639] <... write resumed> ) = 19
[pid 6639] read(3, <unfinished ...>
[pid 6640] write(1, "child: 6640\n", 12child: 6640
) = 12
// little-endian
[pid 6640] write(4, "\7\0\0\0", 4) = 4
// parent happens to win the read - often the case with multiple CPUs
[pid 6639] <... read resumed> "\7\0\0\0", 4) = 4
[pid 6640] getpid( <unfinished ...>
[pid 6639] getpid( <unfinished ...>
[pid 6640] <... getpid resumed> ) = 6640
[pid 6639] <... getpid resumed> ) = 6639
[pid 6640] write(1, "shared print: 6640\n", 19shared print: 6640
<unfinished ...>
[pid 6639] write(1, "shared print, value from child: "..., 45shared print, value from child: 7, pid: 6639
<unfinished ...>
[pid 6640] <... write resumed> ) = 19
[pid 6639] <... write resumed> ) = 45
// losing read here
[pid 6640] read(3, <unfinished ...>
[pid 6639] getpid() = 6639
[pid 6639] write(1, "read result: 4, pid: 6639\n", 26read result: 4, pid: 6639
) = 26
// parent finishes - outside of strace, this looks like everything is done
[pid 6639] exit_group(0) = ?
[pid 6639] +++ exited with 0 +++
// parent interrupted ... child is still waiting
// (although since ^C applies to the whole process group, the child happens to get interrupted sometime after strace stops paying attention - without strace, I wouldn't've given it the signal)
^Cstrace: Process 6640 detached
Contrast what happens if you add else sleep(1) so the child has some time to run:
// startup omitted
pipe([3, 4]) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f469a2149d0) = 25597
strace: Process 25597 attached
[pid 25596] nanosleep({tv_sec=1, tv_nsec=0}, <unfinished ...>
[pid 25597] getpid() = 25597
[pid 25597] fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
[pid 25597] brk(NULL) = 0x555f092f8000
[pid 25597] brk(0x555f09319000) = 0x555f09319000
[pid 25597] write(1, "child: 25597\n", 13child: 25597
) = 13
// note that writing-then-reading a pipe in the same thread is only safe if at most PIPE_BUF bytes, which is guaranteed to be 512 by POSIX, but is 4096 on Linux
[pid 25597] write(4, "\7\0\0\0", 4) = 4
[pid 25597] getpid() = 25597
[pid 25597] write(1, "shared print: 25597\n", 20shared print: 25597
) = 20
[pid 25597] read(3, "\7\0\0\0", 4) = 4
[pid 25597] getpid() = 25597
[pid 25597] write(1, "shared print, value from child: "..., 46shared print, value from child: 7, pid: 25597
) = 46
[pid 25597] getpid() = 25597
[pid 25597] write(1, "read result: 4, pid: 25597\n", 27read result: 4, pid: 25597
) = 27
// child finishes first
[pid 25597] exit_group(0) = ?
[pid 25597] +++ exited with 0 +++
<... nanosleep resumed> {tv_sec=0, tv_nsec=998890543}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=25597, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
restart_syscall(<... resuming interrupted nanosleep ...>) = 0
getpid() = 25596
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
brk(NULL) = 0x555f092f8000
brk(0x555f09319000) = 0x555f09319000
write(1, "shared print: 25596\n", 20shared print: 25596
) = 20
// parent still blocking when I press ^C
read(3, ^Cstrace: Process 25596 detached
<detached ...>
Note also that you can set the pipe to non-blocking mode, but then you must handle EAGAIN (usually by calling somemthing in the select family) so it won't help here:
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
If you close p[0] in the child, the read() call of the child fails and you keep the old value (7). If you do not close p[0] in the child, the read() call of the child blocks, because there is nothing there to read (the parent read all the data), but since p[1] is not closed, there is also no EOF that would allow read() to return.
So basically, the child is stuck at the read() call if p[0] is not closed in the child.
I'm trying to get the return value of a bash script launched from a c daemon program through the system() function.
If the skeleton_daemon is commented the output is correctly 5120 (20 << 8) otherwise it's always -1
int main (void)
{
skeleton_daemon();
printf("%d", system("/tmp/prova.sh") ); // prova.sh return 20
}
void skeleton_daemon(void)
{
pid_t pid;
pid = fork();
if (pid < 0)
exit(EXIT_FAILURE);
if (pid > 0)
exit(EXIT_SUCCESS);
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
umask(0);
// chdir("/"); // commented as per nos suggestion, nothing changed
}
Anyone know how to get the script value from the deamon?
EDIT: running with strace -f as requested by nos
To be honest I don't understand a thing
execve("./pr", ["./pr"], [/* 17 vars */]) = 0
brk(0) = 0xd79000
uname({sys="Linux", node="alarm", ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb6f80000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=30110, ...}) = 0
mmap2(NULL, 30110, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb6f53000
close(3) = 0
open("/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\370\203\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1541182, ...}) = 0
mmap2(NULL, 1262984, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb6e1e000
mprotect(0xb6f45000, 32768, PROT_NONE) = 0
mmap2(0xb6f4d000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x127000) = 0xb6f4d000
mmap2(0xb6f50000, 9608, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb6f50000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb6f7f000
set_tls(0xb6f7f4c0, 0xb6f7fba8, 0xb6f82000, 0xb6f7f4c0, 0xb6f82000) = 0
mprotect(0xb6f4d000, 8192, PROT_READ) = 0
mprotect(0xb6f81000, 4096, PROT_READ) = 0
munmap(0xb6f53000, 30110) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb6f7f068) = 7344
exit_group(0) = ?
+++ exited with 0 +++
Thanks in advance
This happens because your skeleton_daemon() functions ignores SIGCHLD, however the system() call assumes it to not be ignored, so it can properly wait until the executed program has finished.