I have a scenario where a spawned child process is killed when the parent is killed with SIGABRT. In my understanding, the child should continue to run. To mimic the actual code, I created two files presenting child and parent. The parent writes to a pipe and child reads from the read end (STDIN_FILENO).
parent code -> parent.cpp
#include <iostream>
#include <stdlib.h>
#include <string>
#include <unistd.h>
int main() {
int pipefd[2];
std::string message = "test\n";
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
int pid = fork();
if (0 == pid) {
std::cout << "Inside child process\n";
close(pipefd[1]);
char *args[] = {"./CHILD", NULL};
char *envp[] = {NULL};
if (dup2(pipefd[0], STDIN_FILENO) == -1) {
std::cout << "dup2 failed\n";
}
close(pipefd[0]);
close(pipefd[1]);
execve(args[0], args, envp);
} else {
close(pipefd[0]);
while (1) {
sleep(1);
std::cout << "parent writing -> " << message;
write(pipefd[1], message.c_str(), message.length());
}
}
return 0;
}
child code -> child.cpp
#include <iostream>
#include <string>
#include <unistd.h>
int main() {
std::string str;
char buf;
std::cout << "[child] started\n";
while (read(STDIN_FILENO, &buf, sizeof(buf)) > 0) {
if (buf != '\n')
str += buf;
else {
write(STDOUT_FILENO, str.c_str(), str.length());
str.clear();
write(STDOUT_FILENO, "\n", 1);
}
}
std::cout << "[child] Exiting the application\n";
return 0;
}
if the parent is killed with SIGABRT, the child also receives the same. When the pipe code is removed, the signal is not propagated.
Can you please provide some insight into it?
Related
I am creating 10 children to one parent. I want all the children to write into a pipe appending the pipe, so I can then read the collective data later in the parent.
So first child writes into the pipe "9-6" then the second child writes "9-6" making the contents inside the pipe "9-6 9-6"
but what I have discovered here is that Every child when open the pipes for writing inside it. It truncates the pipe.
Is there a way I can just keep on adding content into the pipe and eventually in the end just read it.
#include <fcntl.h> //
#include <stdio.h> //
#include <stdlib.h> //
#include <string.h> //
#include <sys/types.h> //
#include <sys/wait.h> //
#include <sys/stat.h> //
#include <termios.h> //
#include <unistd.h>
#include <iostream>
using namespace std;
int main()
{
pid_t pids[10]; //10 children
int i;
int n = 10;
int f1 = mkfifo("p", 0666); //making named pipe
if (f1 < 0)
std::cerr << "Pipe not created";
char str[256] = "9-6"; //character array that every child writes in the pipe
char read_char[256] = "";
/* Start children. */
for (i = 0; i < 10; ++i) {
if ((pids[i] = fork()) < 0) {
perror("fork");
abort();
}
else if (pids[i] == 0) {\
int fifo_write = open("p", O_WRONLY); //open the pipe for writing
if (fifo_write < 0)
{
std::cerr << "Pipe could not be created";
return 0;
}
else
{
write(fifo_write, str, sizeof(str)); //write char str[] and close the pipe
close(fifo_write);
exit(0);
}
}
}
/* Wait for children to exit. */
int count(0);
int fifo_read = open("p", O_RDONLY);
char str1[256] = "";
if (fifo_read < 0)
{
std::cerr << "Pipe could not be created";
}
else
{
read(fifo_read, str1, sizeof(str));
cout << str1 << endl;
count++;
close(fifo_read);
}
cout << "the count is " << count << endl;
unlink("p");
}
I have also tried first reading the pipe and appending what is read from the pipe with "9-6" and then writing in the pipe again.
The implementation of that looks like this
#include <fcntl.h> //
#include <stdio.h> //
#include <stdlib.h> //
#include <string.h> //
#include <sys/types.h> //
#include <sys/wait.h> //
#include <sys/stat.h> //
#include <termios.h> //
#include <unistd.h>
#include <iostream>
using namespace std;
int main()
{
pid_t pids[10];
int i;
int n = 10;
int f1 = mkfifo("p", 0666); //Making named pipe
if (f1 < 0)
std::cerr << "Pipe not created";
char str[256] = "9-6";
char read_char[256] = "";
/* Start children. */
for (i = 0; i < 10; ++i) {
if ((pids[i] = fork()) < 0) {
perror("fork");
abort();
}
else if (pids[i] == 0) {
//here I open the pipe for read so that I can read what inside and concatenate it with 9-6 //so every time it is concatenated with 9-6 and written back in the pipe
int fifo_read = open("p", O_RDONLY);
if (fifo_read < 0)
{
std::cerr << "Pipe could not be created";
return 0;
}
else
{
read(fifo_read, read_char, sizeof(read_char));
strcat(str, read_char);
close(fifo_read);
}
int fifo_write = open("p", O_WRONLY);
if (fifo_write < 0)
{
std::cerr << "Pipe could not be created";
return 0;
}
else
{
write(fifo_write, str, sizeof(str));
close(fifo_write);
exit(0);
}
}
}
/* Wait for children to exit. */
int count(0);
int fifo_read = open("p", O_RDONLY);
char str1[256] = "";
if (fifo_read < 0)
{
std::cerr << "Pipe could not be created";
}
else
{
read(fifo_read, str1, sizeof(str));
cout << str1 << endl;
count++;
close(fifo_read);
}
cout << "the count is " << count << endl;
unlink("p");
}
But when I do this mkfifo fails and "pipe not created" is printed
I wrote the following code:
void execute() {
std::cout << "smash pid is " << getpid() << std::endl;
}
int main()
{
int pid=fork();
if (pid==0)
{
int fd=open("my_file.txt", O_WRONLY | O_CREAT, 0666); // 3=my_file
dup2(fd,1); // replace output stream
close(fd); //close duplicate access to my_file
execute();
close (1); // close last access to my file
}
else if (pid>0)
{
std::cout << "Hello!" << std::endl;
}
return 0;
}
my question is am I doing things correctly? and can the main process still have access to print to terminal as usual?
I tried to add notes of what I am doing, please let me know if something isn't clear.
Version 2:
int main()
{
int pid=fork();
if (pid==0)
{
close (1);
int fd=open("my_file.txt", O_WRONLY | O_CREAT, 0666); // 3=my_file
execute();
close (1); // close last access to my file
}
else if (pid>0)
{
std::cout << "Hello!" << std::endl;
}
return 0;
}
my question is am I doing things correctly?
Your child will have its output directed into the opened file and the parent will write "Hello!" to the stdout that was provided to the program at startup. It looks like that's what you want, so, yes.
I'd use pid_t instead of int for the process id though, but that may be different on different platforms.
can the main process still have access to print to terminal as usual?
Yes. The dup2 in the child process does not affect the parent process in any way.
One note. Use fileno(stdout) instead of 1:
dup2(fd, fileno(stdout));
// and
close(fileno(stdout)); // not needed really
The int fileno(FILE*) function returns the internal file descriptor from the standard FILE* that is stdout. It's just a way of making it clearer to readers of the code.
You could however redirect stdout to a file using the standard C++ function std::freopen instead.
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio> // std::freopen
#include <iostream>
void execute() {
std::cout << "smash pid is " << getpid() << std::endl;
system("echo the redirect is inherited by the grand children too");
}
int main() {
pid_t pid = fork();
if(pid < 0) { // check for errors
std::perror("fork failed");
return 1;
}
if(pid == 0) { // child
// using std::freopen
if(std::freopen("my_file.txt", "w", stdout) == nullptr) {
std::perror("freopen failed");
return 1;
}
execute();
return 0;
}
// parent
std::cout << "Parent says hello!\n";
// wait for child
int wstatus;
if(waitpid(pid, &wstatus, 0) == pid) {
std::cout << "child exited with status " << wstatus << '\n';
} else {
std::perror("waitpid failed");
}
}
wait(), exit() and signals are forbidden
only pipes allowed
A user gives an integer positive number-N and N-processes are created, father creates a child, that child becomes a father and creates another child and so on. Each one of the first processes (N-1) should wait to finish its process-child first and then itself. The initial process should print "1-My Process ID: ", the next process that's been created the number "2 My process ID: and my father's ID:" and so on.
my code. i don't have wait or exit instead i use return(-1).
but i didn't managed to print accordingly the numbers 1 my process id..., 2 my process id..., 3 my process id... and so on.
any ideas?
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
/* Read characters from the pipe and echo them to stdout. */
void read_from_pipe (int file)
{
FILE *stream;
int c;
stream = fdopen (file, "r");
while ((c = fgetc (stream)) != EOF)
putchar (c);
fclose (stream);
}
/* Write some random text to the pipe. */
void write_to_pipe (int file)
{
FILE *stream;
stream = fdopen (file, "w");
fprintf (stream, "\n");
fprintf (stream, " ");
fclose (stream);
}
int main (void)
{
pid_t pid;
int mypipe[2];
int j = 1;
int i;
cout << "\nassume father is by default the first process\n" << "Please enter how child-processes you want: ";
cin >> i;
for( ; j < i; j++)
{
/* Create the pipe. */
if (pipe (mypipe))
{
fprintf (stderr, "Pipe failed.\n");
return (-1);
}
/* Create the child process. */
pid = fork ();
if (pid == (pid_t) 0)
{
/* This is the child process. Close other end first. */
pid = getpid();
close (mypipe[1]);
read_from_pipe (mypipe[0]);
printf("Child's ID: %d\n",pid);
sleep(0);
}
else if (pid > (pid_t) 0)
{
/* This is the parent process. Close other end first. */
pid = getpid();
close (mypipe[0]);
write_to_pipe (mypipe[1]);
printf("Dad's ID: %d\n",pid);
sleep(0);
}
else
{
/* The fork failed. */
fprintf (stderr, "Fork failed.\n");
return (-1);
}
}//end for
//close (mypipe[0]);
//write_to_pipe (mypipe[1]);
// printf("Dad's ID: %d\n",pid);
return (-1);
}// EOP
Recursion could be simpler than iteration because you want each child to in turn create another child. The trick to avoid wait is to have each parent to read on the read end of the pipe, and to have the child to close the write end just before returning without writing anything. Because the read will be blocked until either something has been written or the other end is closed.
You cannot be sure of the order in which the processes will actually end because you do not call wait, but you are sure that a parent cannot end before its childs has terminated its job.
Code could be:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
using std::cout;
using std::cin;
using std::cerr;
using std::endl;
int start_child(int i, int j) {
int my_pipe[2];
pid_t parent_pid, pid;
/* Create the pipe. */
if (pipe (my_pipe))
{
cerr << "Pipe failed." << endl;
return (-1);
}
/* Create the child process. */
parent_pid = getpid();
pid = fork ();
if (pid == (pid_t) 0) {
/* child */
pid = getpid();
close(my_pipe[0]);
cout << "I'm child " << j << "- my pid is " << pid <<
" - my parent's pid is " << parent_pid << endl;
if (i > 1) start_child(i - 1, j + 1);
if (pid == getpid()) cout << "End of child "<< j << endl;
close(my_pipe[1]);
}
else if (pid == (pid_t) -1) {
perror("forking");
close(my_pipe[0]);
close(my_pipe[1]);
return -1;
}
else {
/* parent */
close(my_pipe[1]);
char buf[2];
read(my_pipe[0], buf, 2); // wait for the child to close its pipe end
close(my_pipe[0]);
}
return 0;
}
int main (void)
{
pid_t pid = getpid();
int i;
cout << "\nassume father is by default the first process\n" << "Please enter how child-processes you want: ";
cin >> i;
cout << "I'm parent - my pid is " << pid << endl;
int cr = start_child(i, 1);
if (pid == getpid()) cout << "End of parent" << endl;
return cr;
}// EOP
I want to analyse the output of strace in my C++ program. While launching /bin/strace ps from my app I get an output from ps, but not from strace and strace output is printed to stdout (my terminal). I use standard technique of using pipes and redirecting streams.
Here is my source:
#include <stdlib.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
int main(){
char *const parmList[] = {"/bin/strace", "ps", NULL};
int pipes[2];
pipe(pipes);
pid_t child = fork();
if(child == 0){
close(pipes[0]);
dup2(pipes[1],1);
execv(parmList[0], parmList);
}
else{
int status;
wait(&status);
fcntl(pipes[0], F_SETFL, O_NONBLOCK | O_ASYNC);
char buf[128] = {0};
ssize_t bytesRead;
std::string stdOutBuf;
while(1) {
bytesRead = read(pipes[0], buf, sizeof(buf)-1);
if (bytesRead <= 0)
break;
buf[bytesRead] = 0;
stdOutBuf += buf;
}
std::cout << "<stdout>\n" << stdOutBuf << "\n</stdout>" << std::endl;
}
close(pipes[0]);
close(pipes[1]);
return 0;
}
How can I get an output of strace in my program?
strace writes to stderr not to stdout, if you only want to capture the strace output just use stderr instead of stdout
change the dup2 line like this
dup2(pipes[1],2);
If you want combined strace and ps output do this:
dup2(pipes[1],1);
dup2(pipes[1],2);
if you want separated output you'll probably need to use non-blocking reads and select() or poll()
Also: after calling exec you should print an error message, if everything works exec won't return, but if something goes wrong with the exec, it's good to know.
std::cerr << "exec failed!";
I used this code and had success:
#include <stdlib.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
int main(){
char *const parmList[] = {"/usr/bin/strace", "ps", NULL};
int pipes[2];
pipe(pipes);
pid_t child = fork();
if(child == 0){
close(pipes[0]);
dup2(pipes[1],2);
execv(parmList[0], parmList);
std::cerr << "exec fail\n" ;
}
else{
int status;
wait(&status);
fcntl(pipes[0], F_SETFL, O_NONBLOCK | O_ASYNC);
char buf[128] = {0};
ssize_t bytesRead;
std::string stdOutBuf;
while(1) {
bytesRead = read(pipes[0], buf, sizeof(buf)-1);
if (bytesRead <= 0)
break;
buf[bytesRead] = 0;
stdOutBuf += buf;
}
std::cout << "<stdout>\n" << stdOutBuf << "\n</stdout>" << std::endl;
}
close(pipes[0]);
close(pipes[1]);
return 0;
}
HTH
I have a simple example program that reads from std::cin and writes to std::cout. It works fine if run in cmd.exe or the visual studio debugger. The code (server.cpp):
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
int main(int argc, char* argv[])
{
std::string input;
while (std::getline(std::cin, input))
{
if (input == "dog")
{
std::cout << "cat" << std::endl;
}
else if (input == "white")
{
std::cout << "black" << std::endl;
}
else if (input == "quit")
{
std::cout << "exiting" << std::endl;
return 0;
}
else if (input != "")
{
std::cout << "unknown" << std::endl;
}
}
std::cout << "error" << std::endl;
}
Now I want to run this from another process that writes to its stdin and reads from its stdout. I create two pipes and start a process using CreateProcess with the read handle of one pipe as StdInput handle and the write handle of the other pipe as Stdouput handle. The Code (client.cpp):
#include <Windows.h>
#include <cassert>
#include <iostream>
#include <ostream>
#include <string>
namespace
{
class Server
{
public:
Server() :
m_pi()
{
SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES)};
sa.bInheritHandle = TRUE;
assert(CreatePipe(&m_ro, &m_wo, &sa, 0));
assert(SetHandleInformation(m_ro, HANDLE_FLAG_INHERIT, 0));
assert(CreatePipe(&m_ri, &m_wi, &sa, 0));
assert(SetHandleInformation(m_ri, HANDLE_FLAG_INHERIT, 0));
STARTUPINFO si = {sizeof(STARTUPINFO)};
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = m_ri;
si.hStdOutput = m_wo;
assert(CreateProcess(L"..\\Debug\\server.exe", 0, 0, 0, 1, 0, 0, 0, &si, &m_pi));
}
~Server()
{
execute("quit\n");
assert(WaitForSingleObject(m_pi.hProcess, INFINITE) != WAIT_FAILED);
assert(CloseHandle(m_pi.hThread));
assert(CloseHandle(m_pi.hProcess));
assert(CloseHandle(m_wi));
assert(CloseHandle(m_ri));
assert(CloseHandle(m_wo));
assert(CloseHandle(m_ro));
}
std::string execute(std::string const& cmd)
{
DWORD num_bytes;
assert(WriteFile(m_wi, cmd.c_str(), (DWORD)cmd.size(), &num_bytes, 0));
std::string output;
DWORD n = 0;
while (n == 0)
{
Sleep(0);
assert(PeekNamedPipe(m_ro, 0, 0, 0, &n, 0));
if (n > 0)
{
output.resize(n);
assert(ReadFile(m_ro, &output[0], n, &num_bytes, 0));
}
}
return output;
}
private:
HANDLE m_wo, m_ro, m_wi, m_ri;
PROCESS_INFORMATION m_pi;
};
Server g_server;
}
int main(int argc, char* argv[])
{
std::cout << g_server.execute("white\n") << std::endl;
std::cout << g_server.execute("foobar\n") << std::endl;
std::cout << g_server.execute("dog\n") << std::endl;
}
The problem is that the client only receives the massage "error", so the std::cin of the server seems to be broken.
My question is, what did I do wrong?
You're disabling inheritance for the handle that the child will use to read from stdin - the child needs to inherit that handle. Instead of:
SetHandleInformation(m_ri, HANDLE_FLAG_INHERIT, 0);
Try the following to disable inheritance on the handle that the server process will use to write to the child's stdin:
SetHandleInformation(m_wi, HANDLE_FLAG_INHERIT, 0);