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");
}
}
Related
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?
I have a really simple code where I fork once and the child node mutates with execlp and the parent keeps doing its work. Nothing too fancy.
What I want is to redirect all the standard input to the execlp-ed thread, not the parent.
So if I execute
echo "hi" | ./parent.o
this can be captured with a string s; cin >> s; in some_command.cpp (but not in parent.cpp!).
Current situation (parent.cpp):
int main()
{
int pid = fork();
if (pid < 0) {
std::cout << "Something crashed" << std::endl;
exit(-1);
} else if (pid == 0) {
// We are the forked child
execlp("/some/command", "some_command", NULL);
std::cout << "Execlp failed" << std::endl;
exit(127);
}
// We are the parent, and we keep running
// More stuff...
}
Right now some_command.cpp contains just an empty int main() { }.
Thanks.
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
FileLocker_wo.h
#include <string>
namespace Utils
{
namespace FileLocker
{
bool lock_file(std::string aFileName, int& aFileDescriptor);
bool unlock_file(int& aFileDescriptor);
bool is_file_locked(std::string aFileName);
};
}
FileLocker_wo.cpp
namespace Utils
{
namespace FileLocker
{
bool lock_file(std::string aFileName, int& aFileDescriptor)
{
aFileDescriptor = open(aFileName.c_str(), O_RDWR);
if (aFileDescriptor != -1)
{
if (lockf(aFileDescriptor, F_TLOCK, 0) == 0)
{
return true;
}
std::cout << strerror(errno) << std::endl;
}
return false;
}
bool unlock_file(int& aFileDescriptor)
{
if (lockf(aFileDescriptor, F_ULOCK, 0) == 0)
{
std::cout << "unloced file" << std::endl;
close(aFileDescriptor);
return true;
}
close(aFileDescriptor);
return false;
}
bool is_file_locked(std::string aFileName)
{
int file_descriptor = open(aFileName.c_str(), O_RDWR);
if (file_descriptor != -1)
{
int ret = lockf(file_descriptor, F_TEST, 0);
if (ret == -1 && (errno == EACCES || errno == EAGAIN))
{
std::cout << "locked by another process" << std::endl;
close(file_descriptor);
return true;
}
if (ret != 0)
{
std::cout << "return value is " << ret << " " << strerror(errno) << std::endl;
}
}
close(file_descriptor);
return false;
}
}
}
p1.cpp
#include <iostream>
#include <fstream>
#include "FileLocker_wo.h"
int main()
{
int fd = -1;
if (Utils::FileLocker::lock_file("hello.txt", fd))
{
std::ofstream out("hello.txt");
out << "hello ding dong" << std::endl;
out.close();
std::cout << "locked" << std::endl;
sleep(5);
if (Utils::FileLocker::unlock_file(fd))
{
std::cout << "unlocked" << std::endl;
}
}
return 0;
}
p2.cpp
#include "FileLocker_wo.h"
#include <iostream>
#include <fstream>
int main()
{
int max_trys = 2;
int trys = 0;
bool is_locked = false;
do
{
is_locked = Utils::FileLocker::is_file_locked("hello.txt");
if (!is_locked)
{
std::cout << "not locked" << std::endl;
break;
}
std::cout << "locked" << std::endl;
sleep(1);
++trys;
}
while(trys < max_trys);
if (!is_locked)
{
std::string s;
std::ifstream in("hello.txt");
while(getline(in,s))
{
std::cout << "s is " << s << std::endl;
}
}
return 0;
}
I am trying to get a file lock in one process and checking whether there is any lock on that file in other process using lockf (p1.cpp, p2.cpp).
In p1.cpp I am locking the file hello.txt and waiting for 5 seconds. Meanwhile I start p2.cpp and checking whether any lock is there by other process, but always getting there is no lock> I am stuck with this for last 2 hours.
Can anybody tell what is wrong in this?
You've tripped over one of the nastier design errors in POSIX file locks. You probably didn't know about this because you only read the lockf manpage, not the fcntl manpage, so here's the important bit of the fcntl manpage:
If a process closes any file descriptor referring to a file, then
all of the process's locks on that file are released, regardless of
the file descriptor(s) on which the locks were obtained.
What this means is, in this bit of your code
if (Utils::FileLocker::lock_file("hello.txt", fd))
{
std::ofstream out("hello.txt");
out << "hello ding dong" << std::endl;
out.close();
you lose your lock on the file when you call out.close(), even though out is a different OS-level "open file description" than you used in lock_file!
In order to use POSIX locks safely you must ensure that you call open() on the file to be locked once and only once per process, you must never duplicate the file descriptor, and you must only close it again when you are ready to drop the lock. Because there may not be any way (even using unportable extensions) to construct an iostreams object from a file descriptor, or to extract a file descriptor from an iostreams object, the path of least resistance is to use only OS-level I/O primitives (open, close, read, write, fcntl, lseek, ftruncate) with files that you need to apply POSIX locks to.
I have a multi-threaded C++03 application that presently uses popen() to invoke itself (same binary) and ssh (different binary) again in a new process and reads the output, however, when porting to Android NDK this is posing some issues such as not not having permissions to access ssh, so I'm linking in Dropbear ssh to my application to try and avoid that issue. Further, my current popen solution requires that stdout and stderr be merged together into a single FD which is a bit messy and I'd like to stop doing that.
I would think the pipe code could be simplified by using fork() instead but wonder how to drop all of the parent's stack/memory which is not needed in the child of the fork? Here is a snippet of the old working code:
#include <iostream>
#include <stdio.h>
#include <string>
#include <errno.h>
using std::endl;
using std::cerr;
using std::cout;
using std::string;
void
doPipe()
{
// Redirect stderr to stdout with '2>&1' so that we see any error messages
// in the pipe output.
const string selfCmd = "/path/to/self/binary arg1 arg2 arg3 2>&1";
FILE *fPtr = ::popen(selfCmd.c_str(), "r");
const int bufSize = 4096;
char buf[bufSize + 1];
if (fPtr == NULL) {
cerr << "Failed attempt to popen '" << selfCmd << "'." << endl;
} else {
cout << "Result of: '" << selfCmd << "':\n";
while (true) {
if (::fgets(buf, bufSize, fPtr) == NULL) {
if (!::feof(fPtr)) {
cerr << "Failed attempt to fgets '" << selfCmd << "'." << endl;
}
break;
} else {
cout << buf;
}
}
if (pclose(fPtr) == -1) {
if (errno != 10) {
cerr << "Failed attempt to pclose '" << selfCmd << "'." << endl;
}
}
cout << "\n";
}
}
So far, this is loosely what I have done to convert to fork(), but fork needlessly duplicates the entire parent process memory space. Further, it does not quite work, because the parent never sees EOF on the outFD it is reading from the pipe(). Where else do I need to close the FDs for this to work? How can I do something like execlp() without supplying a binary path (not easily available on Android) but instead start over with the same binary and a blank image with new args?
#include <iostream>
#include <stdio.h>
#include <string>
#include <errno.h>
using std::endl;
using std::cerr;
using std::cout;
using std::string;
int
selfAction(int argc, char *argv[], int &outFD, int &errFD)
{
pid_t childPid; // Process id used for current process.
// fd[0] is the read end of the pipe and fd[1] is the write end of the pipe.
int fd[2]; // Pipe for normal communication between parent/child.
int fdErr[2]; // Pipe for error communication between parent/child.
// Create a pipe for IPC between child and parent.
const int pipeResult = pipe(fd);
if (pipeResult) {
cerr << "selfAction normal pipe failed: " << errno << ".\n";
return -1;
}
const int errorPipeResult = pipe(fdErr);
if (errorPipeResult) {
cerr << "selfAction error pipe failed: " << errno << ".\n";
return -1;
}
// Fork - error.
if ((childPid = fork()) < 0) {
cerr << "selfAction fork failed: " << errno << ".\n";
return -1;
} else if (childPid == 0) { // Fork -> child.
// Close read end of pipe.
::close(fd[0]);
::close(fdErr[0]);
// Close stdout and set fd[1] to it, this way any stdout of the child is
// piped to the parent.
::dup2(fd[1], STDOUT_FILENO);
::dup2(fdErr[1], STDERR_FILENO);
// Close write end of pipe.
::close(fd[1]);
::close(fdErr[1]);
// Exit child process.
exit(main(argc, argv));
} else { // Fork -> parent.
// Close write end of pipe.
::close(fd[1]);
::close(fdErr[1]);
// Provide fd's to our caller for stdout and stderr:
outFD = fd[0];
errFD = fdErr[0];
return 0;
}
}
void
doFork()
{
int argc = 4;
char *argv[4] = { "/path/to/self/binary", "arg1", "arg2", "arg3" };
int outFD = -1;
int errFD = -1;
int result = selfAction(argc, argv, outFD, errFD);
if (result) {
cerr << "Failed to execute selfAction." << endl;
return;
}
FILE *outFile = fdopen(outFD, "r");
FILE *errFile = fdopen(errFD, "r");
const int bufSize = 4096;
char buf[bufSize + 1];
if (outFile == NULL) {
cerr << "Failed attempt to open fork file." << endl;
return;
} else {
cout << "Result:\n";
while (true) {
if (::fgets(buf, bufSize, outFile) == NULL) {
if (!::feof(outFile)) {
cerr << "Failed attempt to fgets." << endl;
}
break;
} else {
cout << buf;
}
}
if (::close(outFD) == -1) {
if (errno != 10) {
cerr << "Failed attempt to close." << endl;
}
}
cout << "\n";
}
if (errFile == NULL) {
cerr << "Failed attempt to open fork file err." << endl;
return;
} else {
cerr << "Error result:\n";
while (true) {
if (::fgets(buf, bufSize, errFile) == NULL) {
if (!::feof(errFile)) {
cerr << "Failed attempt to fgets err." << endl;
}
break;
} else {
cerr << buf;
}
}
if (::close(errFD) == -1) {
if (errno != 10) {
cerr << "Failed attempt to close err." << endl;
}
}
cerr << "\n";
}
}
There are two kinds of child processes created in this fashion with different tasks in my application:
SSH to another machine and invoke a server that will communicate back to the parent that is acting as a client.
Compute a signature, delta, or merge file using rsync.
First of all, popen is a very thin wrapper on top of fork() followed by exec() [and some call to pipe and dup and so on to manage the ends of a pipe] .
Second, the memory is only duplicated in form of "copy-on-write" memory - meaning that unless one of the processes writes to some page, the actual physical memory is shared between the two processes.
It does mean, of course, the OS has to create a memory map with 4-8 bytes per 4KB [in typical cases] (probably plus some internal OS data to track how many copies there are of that page and stuff - but as long as the page remains the same one as the parent process, the child page uses the parent processes internal data). Compared to everything else involved in creating a new process and loading an executable file into the new process, it's a pretty small part of the time. Since you are almost immediately doing exec, not much of the parent process' memory will be touched, so very little will happen there.
My advice would be that if popen works, keep using popen. If popen doesn't quite do what you want for some reason, then use fork + exec - but make sure you know what the reason for doing so is.