execl with wget, child process, unix - why does it not work - c++

So I am trying to execute wget in a separate child process which I am duplicating with fork as follows:
int child;
pid_t child = fork();
if ( child == 0 ) { // no errors
bool done = false; // set to false
while (!done) { // while not true do
execl("wget", "someurl", NULL);
done = true; // since dl finished
}
cout << "DL Finished\n"; // to see if child was successful
}
else if ( child != 0 ) { // errors
Any apparent errors that you can point out in this code? If it matters, this is inside a void function that I am calling in main what is happening is that it is not downloading and it displays "DL Finished", but does not execute wget - then terminal takes over.
This is executed on Ubuntu 12.04.2 LTS. I have previously inside the same void function used child to execute "ls" which works properly, that is with me telling it the whole path of ls (/bin/ls). I read that not providing the full path will make it search for the command, which is what I want.

I read that not providing the full path will make it search for the
command
That happens for execlp. Also, by convention the first argument should be the name of the executable. So you could try:
execlp("wget", "wget", "someurl", NULL);
^ ^^^^^
As a side note, your while (!done) is wrong. That's not how you wait for a program to finish. In fact, once you call exec the while is gone: another process "replaces" your own. So you can think of it "exec is a function that doesn't return". The standard way is to wait(2) in the parent until the child dies.
As a second side note, if all you want is to wget something and wait until the wget is done, the system(3) is possibly more appropriate:
system("wget someurl");

The arguments you pass to execl are what will be the argv array to the new process main function. And as you know the first entry in argv is the program name itself.
So what you need to do is:
execlp("wget", "wget", "someurl", NULL);
Also, if all went well the exec family of function does not return, so any code after the exec call will not run.

Related

Interprocess communication, reading from multiple children stdout

I'm trying to write a custom shell-like program, where multiple commands can be executed concurrently. For a single command this is not much complicated. However, when I try to concurrently execute multiple commands (each one in a separate child) and capture their stdout I'm having a problem.
What I tried so far is this under my shell application I have two functions to run the commands concurrently, execute() takes multiple commands and for each of the commands it fork() a child process to execute the command, subprocess() takes 1 cmd and executes it.
void execute(std::vector<std::string> cmds) {
int fds[2];
pipe(fds);
std::pair<pid_t, int> sp;
for (int i = 0; i < cmds.size(); i++) {
std::pair<pid_t, int> sp = this->subprocess(cmds[i], fds);
}
// wait for all children
while (wait(NULL) > 0);
close(sp.second);
}
std::pair<pid_t, int> subprocess(std::string &cmd, int *fds) {
std::pair<pid_t, int> process = std::make_pair(fork(), fds[0]);
if (process.first == 0) {
close(fds[0]); // no reading
dup2(fds[1], STDIN_FILENO);
close(fds[1]);
char *argv[] = {"/bin/sh", "-c", cmd.data(), NULL};
execvp(argv[0], argv);
exit(0);
}
close(fds[1]); // only reading
return process;
}
The problem here is, when I execute multiple commands on my custom shell (not diving into spesifics here, but it will call execute() at some point.) if I use STDIN_FILENO as above to capture child process stdout, it keeps writing to shell's stdin forever what the captured output is, for example
if the input commands are
echo im done, yet?
echo nope
echo maybe
then, in writing to STDIN_FILENO case, the output is like (where >>> ) is my marker for user input.
im done, yet?
nope
maybe
>>> nope
maybe
im done, yet?
>>> im done, yet?
nope
maybe
in writing to STDOUT_FILENO case, it seems it's ignoring one of the commands (probably the first child), I'm not sure why?
maybe
nope
>>> maybe
nope
>>> nope
maybe
>>> maybe
nope
>>> nope
So, potential things I thought are in my shell I'm using std::cin >> ... for user input in a while loop ofc, this may somehow conflict with stdin case. On the other hand, in the main process (parent) I'm waiting for all children to exit, so children somehow is not exiting, but child should die off after execvp, right ? Moreover, I close the reading end in the main process close(sp.second). At this point, I'm not sure why this case happens ?
Should I not use pipe() for a process like this ? If I use a temp file to redirect stdout of child process, would everything be fine ? and if so, can you please explain why ?
There are multiple, fundamental, conceptual problems in the shown code.
std::pair<pid_t, int> sp;
This declares a new std::pair object. So far so good.
std::pair<pid_t, int> sp = this->subprocess(cmds[i], fds);
This declares a new std::pair object inside the for loop. It just happens to have the same name as the sp object at the function scope. But it's a different object that has nothing to do, whatsoever, with it. That's how C++ works: when you declare an object inside an inner scope, inside an if statement, a for loop, or anything that's stuffed inside another pair of { ... } you end up declaring a new object. Whether its name happens to be the same as another name that's been declared in a larger scope, it's immaterial. It's a new object.
// wait for all children
while (wait(NULL) > 0);
close(sp.second);
There are two separate problems here.
For starters, if we've been paying attention: this sp object has not been initialized to anything.
If the goal here is to read from the children, that part is completely missing, and that should be done before waiting for the child processes to exit. If, as the described goal is here, the child processes are going to be writing to this pipe the pipe should be read from. Otherwise if nothing is being read from the pipe: the pipe's internal buffer is limited, and if the child processes fill up the pipe they'll be blocked, waiting for the pipe to be read from. But the parent process is waiting for the child processes to exist, so everything will hang.
Finally, it is also unclear why the pipe's file descriptor is getting passed to the same function, only to return a std::pair with the same file descriptor. The std::pair serves no useful purpose in the shown code, so it's likely that there's also more code that's not shown here, where this is put to use.
At least all of the above problems must be fixed in order for the shown code to work correctly. If there's other code that's not shown, it may or may not have additional issues, as well.

A weird bug in my code is causing some strange results, I believe it's due to my use of fork

There are a few different strange results that result from different types of input. First off, I'm building a simple linux shell, and below I show some example i/o
$
$
$
$ ls -l /
$ $ exit
so the first thing you probably notice is the double $. This happens whenever I have entered something into the prompt and not simply left it blank. Second, it appears to have exited properly, as it returns control back to my terminal... or does it? I really don't know, but as I'm in my terminal, if I simply press enter, this pops up in my terminal.
finn-and-jake#CandyKingom:~/Desktop/OS/hw2$ terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::at
I'm not 100% what's causing this or how to fix it, but I have a hunch that it has something to do with fork, and I believe that's also what's causing my extra $. There's also another issue with when I put input in as I did above, but with some empty input between the initial and the exit, which results in the program not completely closing out. An example is provided below.
$
$
$ ls -l /
$ $
$
$
$
$
$
$
$ exit
$ exit
and finally, there's another issue that I'm not sure what's causing it where the program runs in an infinite loop I can't force quit out of and it crashes my operating system (Ubuntu 14.04)
In an attempt to keep the code minimal, I'm only including the method that I suspect to be the cause of this. If any more than that is requested I will include it in an edit.
void Shell::interpreteInput() {
if (commandQueue.empty()) {
return;
};
if (commandQueue.at(0) == "exit") {
exit = true;
return;
};
pid_t pid = fork();
if (pid < 0) {
cerr << "Fork unsuccessful\n";
return;
};
if (commandQueue.size() >= 3 && commandQueue.at(commandQueue.size() - 2) == ">") {
//commandQueue.at(commandQueue.size() - 1) is fileName open
//commandQueue.at(0) is name of program to run exec
//remaining substrings are args
};
//commandQueue.at(0) is name of program to run exec
// remaining substrings are args
};
Edit (response to first question in comments): In the child process, execute the given program, passing it the given arguments (if any). If the program is a bare name (i.e., it does not contain any slashes), search the PATH for the executable. If the line has form 1 (my fourth if statement) —where the output is to be redirected—open (create or overwrite) a file with the given path, and redirect the program’s output to that file. (See detailed instructions below.)
• If output is to be redirected but the file cannot be opened, display an error message and return to Step 1.
• If the given program cannot be executed (exec fails), display an error message and return to Step 1.
After fork(), there is a check for a fork error, but otherwise both parent and child process do the same thing afterwards. You probably want to diverge code paths: parent does one thing and child does another.
Traditionally, a shell parent process waits for the child process to complete, unless there is an & indicating that the parent does not wait. The child then scoops together the command pipe line and exec's the command(s).
You need to make sure you're using a function like waitpid() or one of the related wait functions in the parent process. When fork() returns successfully (not -1), there are two processes running. The parent process will return the actual PID of the child process. The child process will get a return value of 0. So you need code like this:
pid_t pid = fork();
if (pid == -1) {
// handle error
} else if (pid == 0) {
// do child process stuff
} else {
// do parent process stuff
int status, rc;
do {
rc = waitpid(pid, &status, 0);
// handle rc value from waitpid
} while (!WIFEXITED(status));
}

I'm confused how this execvp() is handled in this sample function which uses fork() to clone a process

I have the following function from a book titled "Advanced Linux Programming".
int spawn (char* program, char** arg_list)
{
pid_t child_pid;
/* Duplicate this process. */
child_pid = fork ();
if (child_pid != 0)
/* This is the parent process. */
return child_pid;
else {
/* Now execute PROGRAM, searching for it in the path. */
execvp (program, arg_list);
/* The execvp function returns only if an error occurs. */
fprintf (stderr, “an error occurred in execvp\n”);
abort ();
}
}
But I'm confused that, in cases where ls is executed successfully, the error is not printed, but in case it fails, it prints the error which is put in the line following it.
My Question
This line fprintf (stderr, “an error occurred in execvp\n”); is after the execvp() function, and it is expected to be executed after the execution of execvp() finishes, but it is not the case, and it is executed only if execvp() encounters an error. It seems the function spawn() finishes as soon as it executes execvp() successfully. Am I right?
You can have a look at the manpage for execvp, it says:
The exec() family of functions replaces the current process image with
a new process image.
So, what does that mean? It means, if execvp succeeds, your program wont be in memory anymore, thus it wont ever reach the error message. Your program in memory will be replaced by the new program (in your case ls if i understood it correctly).
So, if your program is able to reach the error message printout, then the execvp function will have failed. Otherwise the other program starts execution.
The reason why your programm will be still running is the fork command, which creates a copy of the process image, so you will be having two same processes running of which only one will be replaced by the command you try to execute. This is achieved by the if clause if (child_pid != 0), as the fork command will duplicate the process and return the new Process ID (PID). If this is set to 0 (see man 3 fork), then its the new child process, if its != 0 its the parent process. Your function there only executes execvp if its the child process, the parent process encounters an early return.

Linux, waitpid, WNOHANG and zombies

I need to be able to:
fork a process and make it execvp (I did that)
check if the child process execvp was successful (don't know how)
check if the child process finished (having problems)
I'm forking a process and I don't have any way to check if the childs's execvp worked or failed. If it failed I need to be able to know that it failed. Currently I'm using
-1 != waitpid( pid, &status, WNOHANG )
But it seems that if the execv of the pid process fails the waitpid does not return -1.
How could I check that? I read the waitpid man page, but it isn't clear to me; maybe my English isn't good enough.
EDIT: in order to explain more:
I'm building my own terminal for a Home Work. I need to get as an input a command string, lets say "ls" and then I have to execute the command.
After the child forks, the child calls execvp in order to execute the command ( after I parse the string ), and the parent need to check whether there was a '&' at the end of the command or not.
if the sign '&' does not exist at the end of the command then the parent need to wait for the child to execute.
so I need to know if the execvp failed. If it didn't failed then the parent use waitpid to wait for the child to finish it execution. If it failed then the parent will not wait for the child.
A common solution to #2 is to open a pipe prior to the fork(), then write to it in the child following the exec. In the parent, a successful read means the exec failed; an unsuccessful read means the exec succeeded and the write never took place.
// ignoring all errors except from execvp...
int execpipe[2];
pipe(execpipe);
fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC);
if(fork() == 0)
{
close(execpipe[0]);
execvp(...); // on success, never returns
write(execpipe[1], &errno, sizeof(errno));
// doesn't matter what you exit with
_exit(0);
}
else
{
close(execpipe[1]);
int childErrno;
if(read(execpipe[0], &childErrno, sizeof(childErrno)) == sizeof(childErrno))
{
// exec failed, now we have the child's errno value
// e.g. ENOENT
}
}
This lets the parent unambiguously know whether the exec was successful, and as a byproduct what the errno value was if unsuccessful.
If the exec was successful, the child process may still fail with an exit code, and examining the status with the WEXITSTATUS macro give you that condition as well.
NOTE: Calling waitpid with the WNOHANG flag is nonblocking, and you may need to poll the process until a valid pid is returned.
An exec call shouldn't return at all if it succeeds, because it replaces the current process image with another one, so if it does it means an error has occurred:
execvp(...);
/* exec failed and you should exit the
child process here with an error */
exit(errno);
To let the parent process know if exec failed you should read the status of the child process:
waitpid(pid, &status, WNOHANG);
And then use the WEXITSTATUS(status) macro, from the man page:
WEXITSTATUS(status) returns the exit status of the child. This
consists of the least significant 8 bits of the status argument that
the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main()
Note the last statement means if exec succeeds and runs the command you will get the exit status of the main() function of that command, in other words you can't reliably tell the difference between a failed exec and a failed command this way, so it depends if that matters to you.
Another issue:
if the sign '&' does not exist at the end of the command then the
parent need to wait for the child to execute.
You need to call wait() on the child process at some point in your program, regardless of the & to avoid leaving the child process in a zombie state,
Note: When you use the WNOHANG it means that waitpid() will return immediately if no process has changed its state, i.e. it will not block, I assume you know that, otherwise use wait() or call waitpid() as part of your main loop.

using exec to execute a system command in a new process

I am trying to spawn a process that executes a system command, while my own program still proceeds and two processes will run in parallel. I am working on linux.
I looked up online and sounds like I should use exec() family. But it doesn't work quite as what I expected. For example, in the following code, I only see "before" being printed, ,but not "done".
I am curious if I am issing anything?
#include <unistd.h>
#include <iostream>
using namespace std;
main()
{
cout << "before" << endl;
execl("/bin/ls", "/bin/ls", "-r", "-t", "-l", (char *) 0);
cout << "done" << endl;
}
[UPDATE]
Thank you for your guys comments. Now my program looks like this. Everything works fine except at the end, I have to press enter to finish the program. I am not sure why I have to press the last enter?
#include <unistd.h>
#include <iostream>
using namespace std;
main()
{
cout << "before" << endl;
int pid = fork();
cout << pid << endl;
if (pid==0) {
execl("/bin/ls", "ls", "-r", "-t", "-l", (char *) 0);
}
cout << "done" << endl;
}
You're missing a call to fork. All exec does is replace the current process image with that of the new program. Use fork to spawn a copy of your current process. Its return value will tell you whether it's the child or the original parent that's running. If it's the child, call exec.
Once you've made that change, it only appears that you need to press Enter for the programs to finish. What's actually happening is this: The parent process forks and executes the child process. Both processes run, and both processes print to stdout at the same time. Their output is garbled. The parent process has less to do than the child, so it terminates first. When it terminates, your shell, which was waiting for it, wakes and prints the usual prompt. Meanwhile, the child process is still running. It prints more file entries. Finally, it terminates. The shell isn't paying attention to the child process (its grandchild), so the shell has no reason to re-print the prompt. Look more carefully at the output you get, and you should be able to find your usual command prompt buried in the ls output above.
The cursor appears to be waiting for you to press a key. When you do, the shell prints a prompt, and all looks normal. But as far as the shell was concerned, all was already normal. You could have typed another command before. It would have looked a little strange, but the shell would have executed it normally because it only receives input from the keyboard, not from the child process printing additional characters to the screen.
If you use a program like top in a separate console window, you can watch and confirm that both programs have already finished running before you have to press Enter.
The Exec family of functions replaces the current process with the new executable.
To do what you need, use one of the fork() functions and have the child process exec the new image.
[response to update]
It is doing exactly what you told it: You don't have to press "enter" to finish the program: It has already exited. The shell has already given a prompt:
[wally#zenetfedora ~]$ ./z
before
22397
done
[wally#zenetfedora ~]$ 0 << here is the prompt (as well as the pid)
total 5102364
drwxr-xr-x. 2 wally wally 4096 2011-01-31 16:22 Templates
...
The output from ls takes awhile so it buries the prompt. If you want output to appear in a more logical order, add sleep(1) (or maybe longer) before the "done".
You're missing the part where execl() replaces your current program in memory with /bin/ls
I would suggest looking at popen() which will fork and exec a new process, then let you read or write to it via a pipe. (Or if you need read and write, fork() yourself, then exec())