How to stop the first child process from being executed? - c++

Aim: To design a linux shell, which shows a prompt to take input from user, creates a new process to execute that command then terminates/exits the process. Here is my code
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
using namespace std;
string cmd; //global string so cmd copied to child to execute
void HandleAsParent(){
cout<<"Linux Shell 1.0\n";
string s;
while (!exitflag) {
cout<<"myShell>";
getline(cin,cmd); //Take user input
fork();
wait(NULL);
}
}
void HandleAsChild(){
cout<<"Executing";
system(cmd.c_str());
}
int main() {
pid_t p = fork();
if(p != 0){
HandleAsParent(); //This is parent process
}
else {
HandleAsChild(); //This is child process
}
}
The problem is that, because of the first fork() call in the main,
myShell>Executing
is displayed on the first line when the program runs instead of just
myShell>
.
I am able to understand why this is happening but cannot figure out how do I stop that first child process from being executed.
Please suggest me workarounds/solutions to my problem.
Edit 1: This is one of my Assignment(for learning UNIX Processes)
questions, and It is clearly stated that the program " prompts the
user for a command, parses the command, and then executes it with a
child process "

As I already guessed, system() probably uses a combination of fork(), exec() and wait(). Out of curiosity, I googled for source code and found one on woboq.org: glibc/sysdeps/posix/system.c.
This in mind, using system(), the required child process "comes for free". So, I got this minimal sample:
#include <iostream>
void callCmd(const std::string &cmd)
{
system(cmd.c_str());
}
int main()
{
std::cout << "My Linux Shell 1.0\n"
<< "Type exit[Enter] to exit.\n";
for (;;) {
std::cout << "> ";
std::string input; std::getline(std::cin, input);
if (input == "exit") return 0;
callCmd(input);
}
}
Compiled and tested on cygwin on Windows 10:
$ g++ -std=c++11 -o mycroShell mycroShell.cc
$ ./mycroShell
My Linux Shell 1.0
Type exit[Enter] to exit.
> echo "Hello"
Hello
> exit
$
After getting this running, the system() call in callCmd() can be replaced by fork()/exec()/wait() without the necessity to change anything else.
A simplified version could look like this:
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void callCmd(const std::string &input)
{
// the pre-processing: split the input into command and arguments
std::string cmdArgs = input;
std::vector<char*> args;
char *cmd = &cmdArgs[0];
args.push_back(cmd);
for (char *c = cmd; *c; ++c) {
if (*c == ' ') {
*c = '\0'; args.push_back(c + 1);
}
}
args.push_back(nullptr); // append terminator
// simple replacement of system() (not that sophisticated)
int ret = fork();
if (ret < 0) { // failure
std::cerr << "Failed to execute '" << cmd << "'!\n";
} else if (ret == 0) { // child
execvp(cmd, args.data());
} else { // parent
waitpid(ret, nullptr, 0);
}
}
int main()
{
std::cout << "My Linux Shell 1.1\n"
<< "Type exit[Enter] to exit.\n";
for (;;) {
std::cout << "> ";
std::string input; std::getline(std::cin, input);
if (input == "exit") return 0;
callCmd(input);
}
}
Compiled and tested on cygwin on Windows 10 again:
$ g++ -std=c++11 -o mycroShell mycroShell.cc
$ ./mycroShell
My Linux Shell 1.1
Type exit[Enter] to exit.
> /usr/bin/echo "Hello"
"Hello"
> exit
$
Notes:
IMHO, the most tricky part of this is to prepare a proper argument vector for execvp.
I tried with echo "Hello" as well and it worked. This surprised me a bit as echo is a bash built-in command. I assume that it found /usr/bin/echo and used it as well as in my above output.
The error handling is rather poor – something which should be extended for serious applications.

Related

Read deadlock when using pipes to execute multiple shell commands on the same process

I am making a C++ program that needs to run multiple commands in the same bash shell instance. I need this because some of the commands are setting a bash variable that needs to be read by a subsequent command.
I am using pipes to make file descriptors that are then read and written to using read and write, the other ends of these pipes are connected to a child that is made using fork.
A problem arises when a command doesn't return an output, such as setting a bash variable. In the following code, the read will hang forever on command number 2. I've been searching around for a couple days and there doesn't seem to be a way to detect when the command has finished running without closing the pipe somewhere. I believe that if I close the pipe, I won't be able to reopen it, and that would mean I'd need to make a new bash shell that doesn't have the variables loaded.
Additionally, I can't be sure which commands won't return an output as this code will get the commands it needs to run from a web server and would like to avoid concatenating commands with '&&' for granular error reporting.
#include <unistd.h>
#include <fcntl.h>
#include <cstdlib>
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int inPipeFD[2];
int outPipeFD[2];
// Create a read and write pipe for communication with the child process
pipe(inPipeFD);
pipe(outPipeFD);
// Set the read pipe to be blocking
fcntl(inPipeFD[0], F_SETFL, fcntl(inPipeFD[0], F_GETFL) & ~O_NONBLOCK);
fcntl(inPipeFD[1], F_SETFL, fcntl(inPipeFD[1], F_GETFL) & ~O_NONBLOCK);
// Create a child to run the job commands in
int pid = fork();
if(pid == 0) // Child
{
// Close STDIN and replace it with outPipeFD read end
dup2(outPipeFD[0], STDIN_FILENO);
// Close STDOUT and replace it with inPipe read end
dup2(inPipeFD[1], STDOUT_FILENO);
system("/bin/bash");
}
else // Parent
{
// Close the read end of the write pipe
close(outPipeFD[0]);
// Close the write end of the read pipe
close(inPipeFD[1]);
}
// Command 1
char buf[256];
string command = "echo test\n";
write(outPipeFD[1], command.c_str(), command.length());
read(inPipeFD[0], buf, sizeof(buf));
cout << buf << endl;
// Command 2
char buf2[256];
command = "var=worked\n";
write(outPipeFD[1], command.c_str(), command.length());
read(inPipeFD[0], buf2, sizeof(buf2));
cout << buf2 << endl;
// Command 3
char buf3[256];
command = "echo $var\n";
write(outPipeFD[1], command.c_str(), command.length());
read(inPipeFD[0], buf3, sizeof(buf3));
cout << buf3 << endl;
}
Is there a way to detect that a child's command has finished without having to close the pipe?
One solution could be to set bash in interactive mode by starting it with system("/bin/bash -i"); and to set the prompt to the exit code of the last command.
First, a convenience function to make writing and reading simpler:
std::string command(int write_fd, int read_fd, std::string cmd) {
write(write_fd, cmd.c_str(), cmd.size());
cmd.resize(1024); // turn cmd into a buffer
auto len = read(read_fd, cmd.data(), cmd.size());
if(len == -1) len = 0;
cmd.resize(static_cast<std::size_t>(len));
return cmd;
}
Then in your parent process:
sleep(1); // ugly way to make reasonably sure the child has started bash
int& out = outPipeFD[1]; // for convenience
int& in = inPipeFD[0]; // for convenience
// first, set the prompt
std::cout << command(out, in, "export PS1='$?\\n'\n") << '\n';
// then all these will print something
std::cout << command(out, in, "echo test\n") << '\n';
std::cout << command(out, in, "var=worked\n") << '\n';
std::cout << command(out, in, "echo $var\n") << '\n';
This way you will always have something to read - and you can also use it to verify that the command executed correctly.
If your bash requires a real terminal in -i(nteractive) mode, we have to do it without it. The idea:
Add echo $? + a delimiter to every command sent
Set the pipes in non-blocking mode to be able to catch misc. bad situations, like if the command exit is sent.
Read until the delimiter is found or an error occurs.
To make the delimiter something hard to guess (to not be able to easily force reading to get out of sync) I would generate a new delimiter for every command.
Here is an example of what it could look like with those ideas in place with inline comments:
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <random>
#include <sstream>
#include <utility>
#include <vector>
// a function to generate a random string to use as a delimiter
std::string generate_delimiter() {
thread_local std::mt19937 prng(std::random_device{}());
thread_local std::uniform_int_distribution dist('a', 'z');
thread_local auto gen = [&]() { return dist(prng); };
std::string delimiter(128, 0);
std::generate(delimiter.begin(), delimiter.end(), gen);
return delimiter;
}
// custom exit codes for the command function
enum exit_status_t {
ES_WRITE_FAILED = 256,
ES_READ_FAILED,
ES_EXIT_STATUS_NOT_FOUND
};
// a function executing a command and returning the output and exit code
std::pair<std::vector<std::string>, exit_status_t> command(int write_fd,
int read_fd,
std::string cmd) {
constexpr size_t BufSize = 1024;
// a string that is unlikely to show up in the output:
const std::string delim = generate_delimiter() + "\n";
cmd += "\necho -e $?\"\\n\"" + delim; // add echoing of status code
auto len = write(write_fd, cmd.c_str(), cmd.size()); // send the commands
if(len <= 0) return {{}, ES_WRITE_FAILED}; // couldn't write, return
cmd.resize(0); // use cmd to collect all read data
std::string buffer(BufSize, 0);
// a loop to extract all data until the delimiter is found
fd_set read_set{};
FD_SET(read_fd, &read_set);
while(true) {
// wait until something happens on the pipe
select(read_fd + 1, &read_set, nullptr, nullptr, nullptr);
if((len = read(read_fd, buffer.data(), buffer.size())) <= 0) {
// Failed reading - pipe probably closed on the other side.
// Add a custom exit code and the delimiter and break out.
cmd += "\n" + std::to_string(ES_READ_FAILED) + "\n" + delim;
break;
}
// append what was read to cmd
cmd.append(buffer.begin(), buffer.begin() + len);
// break out of the loop if we got the delimiter
if(cmd.size() >= delim.size() &&
cmd.substr(cmd.size() - delim.size()) == delim)
{
break;
}
}
cmd.resize(cmd.size() - delim.size()); // remove the delimiter
// put what was read in an istringstream for parsing
std::istringstream is(cmd);
// extract line by line
std::vector<std::string> output;
while(std::getline(is, cmd)) {
output.push_back(cmd);
}
// extract the exit code at the last line
exit_status_t retval = ES_EXIT_STATUS_NOT_FOUND;
if(not output.empty()) { // should never be empty but ...
retval = static_cast<exit_status_t>(std::stoi(output.back(), nullptr));
output.resize(output.size() - 1);
}
return {output, retval}; // return the pair
}
Test driver:
int main() {
int inPipeFD[2];
int outPipeFD[2];
// Create a read and write pipe for communication with the child process
pipe(inPipeFD);
pipe(outPipeFD);
// Set the read pipe to be non-blocking
fcntl(inPipeFD[0], F_SETFL, fcntl(inPipeFD[0], F_GETFL) | O_NONBLOCK);
fcntl(inPipeFD[1], F_SETFL, fcntl(inPipeFD[1], F_GETFL) | O_NONBLOCK);
// Create a child to run the job commands in
int pid = fork();
if(pid == 0) // Child
{
// Close STDIN and replace it with outPipeFD read end
dup2(outPipeFD[0], STDIN_FILENO);
close(outPipeFD[0]); // not needed anymore
// Close STDOUT and replace it with inPipe read end
dup2(inPipeFD[1], STDOUT_FILENO);
close(inPipeFD[1]); // not needed anymore
// execl() is cleaner than system() since it replaces the process
// completely. Use /bin/sh instead if you'd like.
execl("/bin/bash", "bash", nullptr);
return 1; // to not run the parent code in case execl fails
}
// Parent
// Close the read end of the write pipe
close(outPipeFD[0]);
// Close the write end of the read pipe
close(inPipeFD[1]);
sleep(1);
int& out = outPipeFD[1]; // for convenience
int& in = inPipeFD[0]; // for convenience
// a list of commands, including an erroneous command(foobar) + exit
for(std::string cmd : {"echo test", "var=worked", "echo $var", "foobar", "exit"})
{
std::cout << "EXECUTING COMMAND: " << cmd << '\n';
auto [output, exit_status] = command(out, in, cmd);
// print what was returned
for(auto str : output) std::cout << str << '\n';
std::cout << "(exit status=" << exit_status << ")\n";
}
}
Possible output:
EXECUTING COMMAND: echo test
test
(exit status=0)
EXECUTING COMMAND: var=worked
(exit status=0)
EXECUTING COMMAND: echo $var
worked
(exit status=0)
EXECUTING COMMAND: foobar
bash: line 7: foobar: command not found
(exit status=127)
EXECUTING COMMAND: exit
(exit status=257)

std::cin is not blocking when process is forked

I have a C++ console application which forks itself and closes the main process.
std::cin in the childprocess is not blocking anymore if a press any key. This results in an endless loop. If I don't fork before then the application behaves as expected.
I have tried different combinations of cin::ignore, cin::fail, cin::clear and close to get this fixed but without success.
I'm using Ubuntu 18.04.
Why this is happening and how can I fix that?
/* includes */
#include <iostream>
#include <unistd.h>
#include <limits>
void fork_to_background()
{
pid_t f_return = fork();
if (f_return == -1)
{
exit(1);
}
if (f_return != 0)
{
exit(0);
}
}
int main(int argc, char** argv)
{
fork_to_background();
std::string commands;
while(true)
{
std::cin >> commands;
std::cout << "Loop" << std::endl;
//std::cin.clear();
//std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
std::cin >> commands; does not block when you reach EOF, you can see it checking if >> success, for instance
if (! std::cin >> commands) {
std::cout << "EOF" << std::endl;
break;
}
the two lines you put in comment are useless here because you read a string, they are useful in case you read for instance a number and the input is not a valid number
note the parent process exit immediately after the fork, closing stdin for the child because they share stdin
if I modify your program to have :
/* includes */
#include <stdlib.h>
#include <iostream>
#include <unistd.h>
#include <limits>
#include <sys/wait.h>
void fork_to_background()
{
pid_t f_return = fork();
if (f_return == -1)
{
exit(1);
}
if (f_return != 0)
{
waitpid(f_return, NULL, 0); // wait end of child
exit(0);
}
}
int main(int argc, char** argv)
{
fork_to_background();
std::string commands;
while(std::cin >> commands)
{
std::cout << commands << std::endl;
std::cout << "Loop" << std::endl;
}
std::cout << "done" << std::endl;
}
Compilation and execution :
/tmp % g++ -Wall f.cc
/tmp % echo "aze qsd" | ./a.out
aze
Loop
qsd
Loop
done
/tmp %

How to properly implement a pipe to communicate with an executable on windows C++?

I have two programs. The following code is an example I came up with to understand the basics before implementing the method into my main program. The child process is not editable and is an executable (as I do not have access to the source code for my main program).
The code for the child process code for my example:
#include <iostream>
#include <string>
using namespace std;
bool is_number(const std::string& s)
{
string::const_iterator it = s.begin();
while (it != s.end() && std::isdigit(*it)) ++it;
return !s.empty() && it == s.end();
}
int main() {
cout << "Enter some positive numbers" << endl;
string testInput = "";
while(true) {
cin >> testInput;
if(is_number(testInput)) {
testInput += " is a positive number";
cout << testInput << endl;
}
else {
cout << "invalid" << endl;
}
}
return EXIT_SUCCESS; //never exits
}
The code for the parent function:
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <cstring>
#include <array>
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd, "r"), _pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
for (int returnNum = 0; returnNum < 5; returnNum++) {
if(fgets(buffer.data(), buffer.size(), pipe.get()) == nullptr)
break;
result += buffer.data();
}
return result;
}
int main() {
std::cout << "Result: " << exec(".\\child.exe") << "." << std::endl;
system("PAUSE");
return EXIT_SUCCESS;
}
The parent function code was adapted from an answer given to How do I execute a command and get the output of the command within C++ using POSIX?. My understanding is the parent function opens the executable and allows me to send commands via the parent functions command line (not really sure how these are passed to the child process but it does work). If the child function was not in the infinite while loop, the result would be printed to the parent terminal.
Note that I will always need to call the child function a known number of times (hence the for loop). I also don't need this code to be perfect as it will just be me using the program.
Why is the result never returned even after 5 commands?
How do I get the result to return?
How do I send commands in the code of my parent program instead of typing them into the terminal of the parent function?

In C++, calling fork when cin is a bash heredoc causes repeated input fragments

I am implementing a shell-like program in C++. It has a loop that reads from cin, forks, and waits for the child.
This works fine if the input is interactive or if it's piped from another program. However, when the input is a bash heredoc, the program rereads parts of the input (sometimes indefinitely).
I understand that the child process inherits the parent's file descriptors, including shared file offset. However, the child in this example does not read anything from cin, so I think it shouldn't touch the offset. I'm kind of stumped about why this is happening.
test.cpp:
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
std::string line;
while (std::getline(std::cin, line)) {
pid_t pid = fork();
if (pid == 0) { // child
break; // exit immediately
}
else if (pid > 0) { // parent
waitpid(pid, nullptr, 0);
}
else { // error
perror("fork");
}
std::cout << getpid() << ": " << line << "\n";
}
return 0;
}
I compile it as follows:
g++ test.cpp -std=c++11
Then I run it with:
./a.out <<EOF
hello world
goodbye world
EOF
Output:
7754: hello world
7754: goodbye world
7754: goodbye world
If I add a third line foo bar to the input command, the program gets stuck in an infinite loop:
13080: hello world
13080: goodbye world
13080: foo bar
13080: o world
13080: goodbye world
13080: foo bar
13080: o world
[...]
Versions:
Linux kernel: 4.4.0-51-generic
Ubuntu: 16.04.1 LTS (xenial)
bash: GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu)
gcc: g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
I was able to reproduce this problem, not only using the heredoc but also using a standard file redirection.
Here is the test script that I used. In both the first and second cases, I got a duplication of the second line of input.
./a.out < Input.txt
echo
cat Input.txt | ./a.out
echo
./a.out <<EOF
hello world
goodbye world
EOF
Closing the stdin before exiting in the child seems to eliminate both of the problems.
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <sys/wait.h>
#include <limits>
int main(int argc, char **argv)
{
std::string line;
while (std::getline(std::cin, line)) {
pid_t pid = fork();
if (pid == 0) { // child
close(STDIN_FILENO);
break; // exit after first closing stdin
}
else if (pid > 0) { // parent
waitpid(pid, nullptr, 0);
}
else { // error
perror("fork");
}
std::cout << getpid() << ": " << line << "\n";
}
return 0;
}

How to keep my shell running after a command

I am running this basic shell program in another shell. I am unable to figure out why my shell doesn't keep running after "ls" executes. I dont have an exit for it but it goes back to original shell. I have to run my shell program every time if want to use it. i figured thats what the fork() is supposed to do. I only want my shell to exit using the exit command which i coded with the if else statement. Any suggestions would be much appreciated. Oh and disregard the gettoks() parser function, i couldn't figure out how to use it for input so i wrote if else statements for the string input cmSTR rather then using the gettoks() parser. Mainly because i couldn't figure how to pass the input into it
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <cstdlib>
#include <sys/wait.h>
using namespace std;
// Initializing counters for trapping
static int cc_counter = 0;
static int cz_counter = 0;
static int cq_counter = 0;
//Functions for trap signal handler
void cc_handler( int signo )
{
++cc_counter;
}
void cz_handler( int signo )
{
++cz_counter;
}
void cq_handler( int signo )
{
++cq_counter;
}
//*********************************************************
//
// Extern Declarations
//
//*********************************************************
using namespace std;
extern "C"
{
extern char **gettoks();
}
//*********************************************************
//
// Main Function
//
//*********************************************************
int main( int argc, char *argv[] )
{
// local variables
int ii;
char **toks;
int retval;
// initialize local variables
ii = 0;
toks = NULL;
retval = 0;
char buf[1000];//Initialize of size for current working directory
string cmSTR;//String to hold input
int status;//Initialization of status for fork()
pid_t pid;//Declaration of pid
// main (infinite) loop
while( true )
{
signal( SIGINT, cc_handler );// Traps Ctrl+C
signal( SIGTSTP, cz_handler);// Traps Ctrl+Z
signal( SIGQUIT, cq_handler);// Traps Ctrl+\
//prompt and show current working directory
cout <<("RS_SHELL:") << getcwd(buf,1000) << "\t";
getline(cin ,cmSTR);//read input from keyboard
// if else loop to switch based on command input
if(cmSTR == "ls")// if ls, then execute arguement
{
execl( "/bin/ls", "ls", NULL );//System call to execute ls
}
else if(cmSTR == "exit")//if exit, then execute block of code
{
cout << "Ctrl C entered: " << ++cc_counter << "times"<< endl;
cout << "Ctrl Z entered: " << ++cz_counter << "times"<< endl;
cout << "Ctrl Back Slash entered: " << ++cq_counter << "times"<< endl;
exit(1);
}
else if(cmSTR == "guish")// if guish, execute guish shell
{
execvp("guish", NULL);
}
//if input is not any of previous commands then fork()
else if(cmSTR != "ls" && cmSTR != "exit" && cmSTR != "guish" && cmSTR != "\n")
{
pid = fork();
if (pid < 0)//Loop to fork parent and child process
{
fprintf(stderr, "Fork Failed");
exit(-1);
}
else if (pid == 0)//Child process
{
execvp("guish", NULL);//system call to execute guish shell
}
else //Parent process
{
waitpid( -1, &status,0);
exit(0);
}
}
// get arguments
toks = gettoks();
if( toks[0] != NULL )
{
// simple loop to echo all arguments
for( ii=0; toks[ii] != NULL; ii++ )
{
cout << "Argument " << ii << ": " << toks[ii] << endl;
}
if( !strcmp( toks[0], "exit" ))
break;
}
}
// return to calling environment
return( retval );
}
As you suspected, execl and its related functions overlay the current process with a new process. Thus, after the execl call that starts ls, your program won't exist any more to keep running.
If you want your shell program to stay around after running ls, you'll need to fork() before the call execl( "/bin/ls", "ls", NULL );.
Also, if you want the output from ls to appear in the same console as your shell, as I think you might be intending, you will need to pipe the output from ls back to your shell and then write that output onto your shell's console. See Writing my own shell… stuck on pipes?, for instance.