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 %
Related
I tried programming a file writer, but when i try to write to a file with something that has multiple words it will suddenly create files.
My code
#include <fstream>
#include <iostream>
#include <unistd.h>
int main(int argc, char *argv[]) {
char cwd[256];
while (true) {
getcwd(cwd, 256);
std::string cwd_s = (std::string)cwd;
std::string Input;
std::cout << cwd_s << "> ";
std::cin >> Input;
std::ofstream file(Input);
std::cout << "cmd /";
std::cin >> Input;
file << Input;
};
for (int i; i < argc; i++) {
std::cout << argv[i] << '\n';
};
return 0;
}
I expected to get this:
C:\Users\code> File.txt
cmd /hello world!
File.txt
hello world!
But it only had "hello", it created another file named world!
I have tried changing the code, but to no avail.
So I have wrote this code that I think does what you expect. The behavior you were seing is because you used the same string to store the filename and the user input. Also you redefined a new file every loop (without closing the previous one). I added a signal handler since if you press Ctrl+C the program would quit without saving/closing the file.
I added comments about how you can make a better CLI interface (if you're interested)
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
std::ofstream outfile;
void signalHandler(int signum) {
outfile.close();
exit(signum);
}
int main() {
char cwd[256];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
std::cout << cwd << "> ";
} else {
std::cerr << "Error: Could not get current working directory." << std::endl;
return 1;
}
std::string filename;
std::getline(std::cin, filename);
outfile.open(filename);
// We intercept the Ctrl+C signal to close the file before exiting. Else nothing will be written to it.
// You can also use Ctrl+D (EOF: End Of File) to exit the program.
// The best praticte would be to implement a command line interface with a "quit" command. (like a map<string, function> for example)
signal(SIGINT, signalHandler);
// Another good practice is to check if the file did open correctly.
if (!outfile.is_open()) {
std::cerr << "Error: Could not open file for writing." << std::endl;
return 1;
}
std::cout << "cmd / ";
char ch;
while (std::cin.get(ch)) {
outfile.put(ch);
if (ch == '\n') {
std::cout << "cmd / ";
}
}
outfile.close();
return 0;
}
Hope it will help you ! And if you have any question about the code feel free to ask I'll explain !
I am intending to set up a pipeline between two processes: parent and child. The parent forks the child and uses execve to replace its image with that of a specified process.
The parent reads from stdin via std::getline(std::cin, input_line).
The child writes to the stdout via std::cout << output_line.
I am looking to setup a pipe and redirect the output of the child to the input of the parent.
The problem is that the parent receives each input (where each input is a number output by the child on stdout) twice. I would like to fix this issue but I don't understand why it is happening.
Code is compiled with g++ 7.4.0 and C++11 standard version.
Child is compiled to a binary called 'p1'.
Parent code:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>
char *
const p1argv[] = {
(char * )
"./p1",
nullptr
};
char *
const p1envp[] = {
(char * ) nullptr
};
int main(int argc, char ** argv) {
pid_t p1id;
int p1fd[2];
pipe(p1fd);
if (p1id = fork() == 0) {
close(p1fd[0]);
dup2(p1fd[1], STDOUT_FILENO);
execve(argv[0], p1argv, p1envp);
perror("Error: failed to execve ./p1.");
} else {
dup2(p1fd[0], STDIN_FILENO);
close(p1fd[1]);
std::string line;
while (std::getline(std::cin, line)) {
std::cout << "d(" << line << ")" << std::endl;
}
int status;
waitpid(p1id, & status, 0);
close(p1fd[0]);
}
}
Child code:
#include <iostream>
#include <thread>
int main(int argc, char** argv) {
long it = 0;
while(true) {
it += 1;
std::cout << std::to_string(it) << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
The actual output for the sample code is:
d(d(1))
d(d(2))
...
The expected output is:
d(1)
d(2)
...
The problem is that this line:
execve(argv[0], p1argv, p1envp);
Is re-executing the main parent program, because that is what the content of argv[0] is at this point. I think you want to find some way to specify "./p1" there.
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.
The below code expects the user to key in a character on every loop. If I want to keep running this loop without user having to enter any character on every loop till the number 0 is keyed in, how do i achieve it.
#include<iostream>
int main()
{
int i = 1;
int ch = 1;
while (ch != 0)
{
std::cin >> ch;
std::cout << "Hi" << i << std::endl;
++i;
}
return 1;
}
Threading is your only possibility. Also it always requires the ENTER when you are using std::cin. This could work:
#include <future>
#include <iostream>
#include <thread>
int main(int argc, char** argv) {
int i = 1;
std::atomic_int ch{1};
std::atomic_bool readKeyboard{true};
std::thread t([&ch, &readKeyboard]() {
while (readKeyboard) {
int input;
if (std::cin >> input) {
ch = input;
if (ch == '0') {
break;
}
}
}
});
while (ch != '0') {
std::cout << "Hi" << i << std::endl;
++i;
}
readKeyboard = false;
t.join();
return 1;
}
You can do this but you will have to use threads. Here is the minimal example how to achive this behaviour. Please note that you will need C++11 at least.
#include <iostream>
#include <thread>
#include <atomic>
int main()
{
std::atomic<bool> stopLoop;
std::thread t([&]()
{
while (!stopLoop)
{
std::cout << "Hi";
}
});
while (std::cin.get() != '0') //you will need to press enter after pressing '0'
{
; //empty loop, just wait until there is 0 on input
}
stopLoop = true; //this stops the other loop
}
Other options will be to dive into OS specific libraries. You must now that C++ doesn't have any kind of non-blocking I/O in standard library and for most time you will have to press <ENTER> to have any input in input stream (std::cin)
I am learning C++ [Java background fwiw] and trying to write a UNIX shell as a project. I am running into a funny little problem with tokenizing the input for execution. The tok function is getting called twice and I'm not sure why. My current test code is the following:
#include <iostream>
#include <vector>
#include <sstream>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
void tok(string, char**);
int main(){
const char* EXIT = "exit";
string input;
cout << "shell>> ";
getline(cin, input);
pid_t pid = fork();
char* args[64]; //arbitrary size, 64 possible whitespace-delimited tokens in command
tok(input, args);
return 0;
}
//copied from http://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
void tok(string inStr, char** args){
int last = 0, next = 0, i = 0;
while( (next = inStr.find(' ', last)) != -1){
cout << i++ << ": " << inStr.substr(last, next-last) << endl;
*args++ = strdup(inStr.substr(last, next-last).c_str());
last = next + 1;
}
cout << i++ << ": " << inStr.substr(last) << endl;
*args++ = strdup(inStr.substr(last).c_str());
*args = '\0';
cout << "done tokenizing..." << endl;
}
My output when I actually run the program is:
$ ./a.out
shell>> ls -l
0: ls
1: -l
done tokenizing...
0: ls
1: -l
done tokenizing...
I'm not sure why it would do that. Can anyone guide me in the right direction please? Thank you
The fork function returns twice, once in the original process and once in the newly-created, forked process. Both of those processes then call tok.
There doesn't seem to be any clear reason why you called fork. So the fix may be as simple as eliminating the call to fork.
When you call fork, you create two processes. Each process has nearly the exact same state except for the respective pid_t you receive. If that value is greater than 0, then you are in the parent process (main), and otherwise you are in the child (or fork failed).
Without performing a check on the returned pid_t, both processes will call tok, resulting in the double call behavior you witnessed.
Hide the call behind a check on pid like so:
pid_t pid = fork();
if (pid > 0) // have parent process call tok
{
char* args[64]; //arbitrary size, 64 possible whitespace-delimited tokens in command
tok(input, args);
}
To see what else parent and child processes have in common (or not): check the docs
following code may work fine
#include <iostream>
#include <vector>
#include <sstream>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
void tok(string, char**);
int main(){
const char* EXIT = "exit";
string input;
cout << "shell>> ";
getline(cin, input);
// pid_t pid = fork();
char* args[64];
tok(input, args);
return 0;
}
void tok(string inStr, char** args){
int last = 0, next = 0, i = 0;
while( (next = inStr.find(' ', last)) != -1){
cout << i++ << ": " << inStr.substr(last, next-last) << endl;
*args++ = strdup(inStr.substr(last, next-last).c_str());
last = next + 1;
}
cout << i++ << ": " << inStr.substr(last) << endl;
*args++ = strdup(inStr.substr(last).c_str());
*args = '\0';
cout << "done tokenizing..." << endl;
}