How to convert:
system("ps -el | grep fork")
argument to an execlp function?
I've tried:
execlp("ps", "ps", "-el", "|", "grep", "fork", (char*)0)
and:
execlp("ps", "ps", "-el", "grep", "fork1", (char*)0)
but both don't work. Is it feasible or not?
Piping is not an automatic feature on POSIX systems, you have to implement it yourself.
For this you have the pipe and fork system calls.
The pipe function creates a pair of file descriptors, one where you write to, and one where you read from. The fork function "forks" a new process.
The trick is to create a pipe using the pipe function, and create a child process. The parent process could then use the write file descriptor of the pipe to be mapped to the standard output of the process (usually done using the dup2 function). Then the parent process exec the first (left-hand side) command of the pipe.
The child process takes the read descriptor of the pipe, and maps it to the standard input, and the similarly exec the command for the other (right-hand side) of the pipe.
This will lead to all output from the first command to standard output to be piped to the standard input of the second command.
There are many tutorials and examples all over the Internet on how to do this practically.
Related
I am trying to redirect the IO of a child process (after fork()) into a file, and I can't figure out why it isn't working.
Here's what I've done:
if(fork() == 0){
execv(exe, (char*[]){ exe, "> temp.exe" });
...
And the executable runs, but it doesn't redirect to the file. I would appreciate it if anyone could explain what am I doing wrong, and how I should do it. I'm getting a feeling I need to redirect before the execv() but I have no idea how to.
Thanks in advance!
Shell redirections (like > file) are implemented by the shell. By using execve(), you are bypassing the shell; the child process will see "> temp.exe" in argv, and will attempt to process it as an argument.
If you want to redirect output to a file, the easiest approach will be to implement that redirection yourself by opening the file after forking and using dup2() to move its file descriptor to standard output:
if (fork() == 0) {
int fd = open("temp.exe", O_CREAT | O_WRONLY, 0666);
if (fd < 0) { handle error... exit(255); }
dup2(fd, 1);
close(fd);
execv(exe, ...);
}
The execX() family of calls does not have the same flexibility as, say system() or popen(). These latter methods call shell to do the interpretation of the command.
The arguments to the execX call are the exact path of the program you want to run and the arguments you want to give to that program. Any "shell" features such as redirection you have to implement yourself before calling execX.
Alternatively, you can let shell actually do the work, execp("sh","sh",myexe+" >test.txt");, but that is lazy, and then why not just use system anyway?
Two very useful methods are pipe() and dup2(): pipe allows you to create pipes to your host program; dup2 lets you set a scenario where the program being executed thinks that it is writing to stdout (1), or reading from stdin (0), but is actually writing or reading to a file or pipe that you created.
You will get a long way by reading the man pages for pipe and dup2, or in google looking for exec pipe and dup2, so I won't take your enjoyment away by writing a full implementation here.
It's possible to redirect everything that is written in the terminal to a process?
For example, after I started the process, if I write "command" in the terminal, this should be redirected to a pipe from my process or something like this.
Yes, it should be practical to redirect all terminal output from your program (and all of its child processes) after your program has started. Unix programs usually write to the terminal by writing to standard output (stdout). Standard output is always on the file descriptor number 1 (the C constant is STDOUT_FILENO), for all processes. You can use the dup2() system call to replace any file descriptor number with another file descriptor.
So you can e.g. create a pipe using int fds[2]; pipe(fds);. Then fds[1] will be a file descriptor number that you can use to write to the pipe. If you do dup2(fds[1], STDOUT_FILENO); then standard output will also write to the pipe. (You can close(fds[1]); afterwards since you probably don't need it, now that you can use stdout instead.)
You can also open a file for writing with fd = open("filename", O_WRONLY); and then dup2(fd, STDOUT_FILENO); so everything written to stdout goes into your file.
Note that you need to redirect stdout at the very beginning of your program before doing anything that might write to stdout.
The above trick will make standard output go to your pipe instead of the terminal. If you want the output to go to the terminal, and also get a copy of the output in a pipe of file, that's more difficult but can also be done. You need to create an internal pipe, then dup2(that_pipe, STDOUT_FILENO); so stdout writes to that pipe. Then you need to read from that pipe (probably using poll() then read()) and write everything you got to both 1) the terminal and 2) to another pipe or file that is going outside your program. So you need two pipes if you want to copy output.
The tee command does this (copy stdout to files) from the shell.
This dup2() approach is not bulletproof because a Unix terminal (even when using a GUI terminal emulator instead of a hardware console) is a device in /dev. You can type tty in a shell or use ttyname(STDOUT_FILENO) in C to see which file in /dev corresponds to the terminal that stdout is writing to. In principle, any program (under the same user account) could open the terminal device using that filename and write to it without asking for permission from any other program. You can easily try this from the shell using the write program:
echo hello world | write $(whoami) /dev/ttys123
where /dev/ttys123 is whatever you got by typing tty in some other terminal window (the name looks a bit different on different operating systems, e.g. Linux and MacOS). You should see hello world appear in that other window.
From a child process, no. You must set this up in the parent preocess, and have it propagate downwards to children (barring some kind of crazy hack).
From the shell, you can redirect.
exec >file
This will redirect standard output to file, and it will apply to all future commands run in the shell. You can make this into a function, if you like.
Shortened Question:
I have a parent process that creates a child process as seen below:
int me2them[2], them2me[2];
pipe(me2them);pipe(them2me);
if (!fork()){
close(0); dup2(me2them[0],0); close(me2them[0]);
close(1); dup2(them2me[1],1); close(them2me[1]);
char * cmds[] = {"wish", "myProg.tcl",NULL};
execvp(cmds[0], cmds);
fprintf(stderr, "Unable to exec 1\n");
exit(-1);
}
close(0); dup2(them2me[0],0); close(them2me[0]);
close(1); dup2(me2them[1],1); close(me2them[1]);
But, I need the child process to be able to recieve input from the user. With this method, the stdin for the child is changed from the keyboard to the stdout of the parent. How can I maintain communication with both the keyboard and the parent?
Also, the parent is the client of a server, and thus multiple parents can be running on the same or different machines, making a shared file between parent and child difficult because the child of any parent would be able to access any other parent's file.
NOTE: I'd prefer to keep the parent's stdout being mapped to the child's input because I did not write the c code and I want to re-route its printf statements to the child.
Original Version:
I am using tcl to make a GUI for a c code. The tcl is a child process of the c code and I use I/O redirection to make the stdout of the c to be the stdin of the tcl and the stdout of the tcl to be the stdin of the c. However, there is a part where the c requests the user's name and it sends the request via stdout to the stdin of the tcl code, no problems, then the tcl requests the name. The tcl name request presents two problems:
1) tcl is in effect sending the request to the c code, causing the c code to mistake the request as being the actual name (solved by sending the request to stderr instead of stdout)
2) When tcl attempts to get the user input for the name, it will be checking stdin, which is mapped to receive from the c code not the keyboard, and will not be able to read the response from the user.
Is there a way to specify to get the response from the keyboard? Or should I map the stdout of the c code to a different fd for the tcl? And if so, how do I specify to get from the keyboard/new fd.
Here is how I make the tcl a child process of the c code:
int me2them[2], them2me[2];
pipe(me2them);pipe(them2me);
if (!fork()){
close(0); dup2(me2them[0],0); close(me2them[0]);
close(1); dup2(them2me[1],1); close(them2me[1]);
char * cmds[] = {"wish", "myProg.tcl",NULL};
execvp(cmds[0], cmds);
fprintf(stderr, "Unable to exec 1\n");
exit(-1);
}
close(0); dup2(them2me[0],0); close(them2me[0]);
close(1); dup2(me2them[1],1); close(me2them[1]);
It sounds as if the child would have a conventional command-line interface, e.g.,line-buffered. I suggest these design changes:
modify the two-way pipe to the child to something other than its standard input and output (you can read/write on other streams)
it might be simplest to make that change within the child
you can use dup2, etc., within the child to modify the pipe. That leaves the question of how to get a usable keyboard interface for the child.
you can solve that problem by opening /dev/tty directly, and (again with dup2 and friends) making the file opened on /dev/tty into the child's standard input and output.
As an example, the dialog program has a feature for reading data via a pipe (at the shell level, that is its standard input), and in initialization, changing that into a different stream and opening /dev/tty for a "real" standard input. Your problem is a little more complicated (with both input and output pipes), but reading the dialog source may be helpful. For reference, that is the init_dialog function in util.c (source here).
I'm writing a C program where I fork() read a file in parent and pass to child via a pipe, then in child redirect the file receive from the pipe to the program I want to execv,
For example, if I exec /bin/less with doc.txt, I will read doc.txt in parent and pass to child, then execute less with the string receive from the read end of pipe.
Everything else is ok, except the execv() part.
I have read the man page for execv(), but it doesn't really help on doing this...
Any help?
Since any forked child share the parents file descriptors, you can simply redirect stdin to a file descriptor with dup2() then just fork and exec away in your child process.
When the child process reads data from stdin, it'll read from the file descriptor you opened (which could be a descriptor to your doc.txt).
I'm using fork(). However, before executing fork(), I open a file (say a.txt) using freopen for writing. Now the child process redirects the output of execlp to a.txt. After terminating the child process, the parent process closes a.txt. Now how can the parent process read a.txt and show some information in stdout?
If the parent process opened the file with freopen(3), then the rewind(3) library call can be used to re-wind the stream's pointer to the start of the file, for use with fread(3) or fgets(3) or whatever API you'd like to use.
freopen does not belong in this code at all. Instead, you should do something like:
FILE *tmp = tmpfile();
if (!(pid=fork())) {
dup2(fileno(tmp), 1);
close(fileno(tmp));
execlp(...);
_exit(1);
}
wait(&status);
/* read from tmp */
However it would actually be a lot better to use a pipe if possible.