I'm trying to send a 1KB string over a message queue between a parent process and its forked child. Unfortunately, my calls to msgsnd, msgrcv, etc. are suddenly all returning -1 and causing the EINVAL error.
I found that this error (in the case of msgsnd, for example) occurs when msqid is invalid, the message type argument is set at <1, or the msgsz is out of range. But upon testing, as far as I can tell, msgget is returning a perfectly valid ID number and the type is set fine. There must be a problem with my buffer ranges, but I thought I set them up correctly. In the code, I have added comments to explain (sorry about all of the frantically-added #includes):
#include <errno.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <cstring>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <sstream>
#define PERMS (S_IRUSR | S_IWUSR)
#define NUMBYTES 1024 //number of chars (bytes) to be sent
using namespace std;
//Special structure for messages
typedef struct {
long mtype;
char mtext[NUMBYTES];
} mymsg_t;
int main(){
//Construct a generic test message of the specified size
char message[NUMBYTES];
for(int i = 0; i < NUMBYTES; i++)
message[i] = 'a';
//Create the message queue (accessed by both parent
//and child processes)
int msqid;
int len;
if(msqid = msgget(IPC_PRIVATE, PERMS) == -1)
perror("Failed to create new message queue!\n");
if(fork() == 0){ //Child process...does the sending
mymsg_t* mbuf;
len = sizeof(mymsg_t) + strlen(message); //doesn't work with " + sizeof(message)" either
void* space;
if((space = malloc(len)) == NULL) //this works fine; no error output
perror("Failed to allocate buffer for message queue.\n");
mbuf = (mymsg_t*)space;
strcpy(mbuf->mtext, message);
mbuf->mtype = 1; //a default
//Some error checks I tried...
//cout<<"msqid is " << msqid << endl;
//cout << "mbuf ptr size is " << sizeof(mbuf) << ". And this non-ptr: "<<sizeof(*mbuf)<<". And
//len: "<<len<<endl;
if(msgsnd(msqid, mbuf, len+1, 0) == -1)
perror("Failed to send message.\n"); //this error occurs every time!
free(mbuf);
}
else{ //Parent process...does the receiving
usleep(10000); //Let the message come
mymsg_t mymsg; //buffer to hold message
int size;
if((size = msgrcv(msqid, &mymsg, len+1, 0, 0)) == -1) //error every time
perror("Failed to read message queue.\n");
//checking that it made it
//cout << "Hopefully printing it now? : " << endl;
//if(write(STDOUT_FILENO, mymsg.mtext, size) == -1)
// perror("Failed to write to standard output!\n");
}
ostringstream oss;
oss << "ipcrm -q " << msqid;
string command = oss.str();
if(system(command.c_str()) != 0) //also errors every time, but not main focus here
perror("Failed to clean up message queue!");
}
What is going on here? I thought I had the buffer procedure working fine and with sufficient space..
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
First off, Hello and thanks for your help!
I'm trying to get an understanding of IPC with unnamed pipes. Specifically, I'm going to be communicating with Maxima to expand an input that was grabbed from stdin and sent to the input Maxima and then that output is sent to stdout. So simply read input from stdin send it to the child and then write the output to stdout. Currently, I've gotten it to output:
Input ">(x+2)^2"
(%o2) x^2+4x+4
which is correct, but there is a newline between the input and output which shouldn't be there and the (%o2) comes from the Maxima formatted output, so that also should not be there.
I guess my question now comes to two things:
1) How do I fix my output so that it is formatted without the trailing newline and the output indicator?
2) What about the following code can I fix? What can I make better? and Why? (My code is not yet near completion because I have another segment I wish to write)
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <errno.h>
#include <iostream> // cin, cout
#include <signal.h>
using namespace std;
int main(int argc, char* argv[]){
pid_t pid;
int status;
int count;
int fpipe[2];
string start = "display2d:false$expand("; string end = ");"; string inp, sent;
string quit = "quit();";
string buffer;
if(pipe(fpipe)){cerr<<"Pipe Failure" << endl; exit(1);}
if((pid = fork()) < 0){ cerr<<"Fork Failure"<<endl; exit(2);}
if(pid == 0){ // child process
close(0); // close stdin
dup(fpipe[0]); // copy stdin
close(fpipe[1]);
execlp("maxima", "maxima", "-q", (char*)0);
read(fpipe[0], (void*)buffer.c_str(), buffer.length());
cout << buffer << " 1" << endl;
exit(EXIT_FAILURE);
}
else{
if(argc == 1){ // parent process
//close(fpipe[0]);
close(1); // close stdout
//dup(fpipe[1]); // redirect stdout
while(1){
cout << ">";
cin >> buffer;
if(buffer == "quit"){
break;
}
buffer = start+buffer+end+'\n';
int dp = write(fpipe[1], buffer.c_str(), buffer.length());
//cout << buffer << endl;
waitpid(getpid(), &status, 0);
}
}
else if(argc > 1){ // just do it for # of argc
}
}
return 0;}
Sample input and output
$./expand
> (x+2)^2
x^2+4*x+4
Current output
(%o#) x^2+4*x+4
I am trying to create a child that calls some program or process. The parent write and read some data from child through a two pipes. My code compiles and runs, but there is no text on input. What am I doing wrong? Am I not closing the pipes correctly, writing the pipes or outputting the data correctly?
#include <iostream>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
int main(){
int pipedes1[2],pipedes2[2];
char buff[256];
string text = "Hello";
pid_t pid;
pipe(pipedes1);
pipe(pipedes2);
pid = fork();
if(pid > 0){
close(pipedes1[1]);
close(pipedes2[0]);
dup2(pipedes2[1], STDOUT_FILENO);
dup2(pipedes1[0], STDIN_FILENO);
execve("/home/pi/Test", NULL, NULL);
} else {
close(pipedes1[1]);
close(pipedes2[1]);
write(pipedes1[0], text.c_str(), text.length());
while((len = read(pipedes2[0], buff, 256)) != 0){
cout << buff << endl;
}
close(pipedes2[0]);
close(pipedes1[0]);
}
return 0;
}
And there is my "chield" program:
int main(){
string str;
cin >> str;
str = "echo " + str + " >> /home/pi/1";
cout << str << endl;
return 0;
}
Output of prog:
echo << /home/pi/1
Im found a problem write() returns -1.
But i dont know why?
write(pipedes1[0], text.c_str(), text.length());
You are writing to the reading end of the pipe.
Except for this, your application is endangered by deadlock. What if you are attempting to write so much that the pipe buffer fills up, and the child produces so much data that its pipe buffer fills up as well? Then both processes are waiting for the other to drain the buffer, but they are each blocked in write!
I have this simple program where I am trying to protect a block of memory, and then read a file into that memory, releasing it when it segfaults..
first I thought there was only a problem if the file is a fifo.. but now it seems that even for a normal file it fails,
this is the code:
#include <errno.h>
#include <string.h>
#include <iostream>
#include <assert.h>
#include <malloc.h>
#include <sys/mman.h>
#include <unistd.h>
#include <map>
#include <algorithm>
#include <unistd.h>
#include <signal.h>
using namespace std;
#define BUFFER_SIZE 8000
#define handle_error(msg) \
do { cout << __LINE__ << endl ;perror(msg); exit(EXIT_FAILURE); } while (0)
volatile int fault_count = 0;
char* buffer = 0;
int size = 40960;
int my_fault_handler(void* addr, int serious) {
if (mprotect(buffer, size,
PROT_READ | PROT_WRITE) == -1)
handle_error("mprotect");
++fault_count;
cout << "Segfaulting" << endl;
return 1;
}
static void handler(int sig, siginfo_t *si, void *unused) {
my_fault_handler(si ->si_addr, sig);
}
int main (int argc, char *argv[])
{
long pagesize = sysconf(_SC_PAGESIZE);
struct sigaction sa;
sa.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = &handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
perror("sigaction");
cerr << "pageSize: " << pagesize << endl;
buffer = (char*)memalign(pagesize, size);
if (buffer == NULL)
handle_error("memalign");
if (mprotect(buffer, size, PROT_READ) == -1)
handle_error("mprotect");
FILE* file = fopen("test", "r");
cout << "File Open" << endl;
if (!file) {
cout << "Failed opening file " << strerror(errno) << endl;
return 0;
}
//*buffer = 0;
while(fread(buffer, pagesize*2, 1, file)) {
if (mprotect(buffer, size,
PROT_READ) == -1)
handle_error("mprotect");
}
cout << ' ' << strerror(errno) << endl;
return(0);
}
note the //*buffer = 0;, if I unmark this line the program segfaults and works correctly..
anyone has any idea?
the errno is bad address.
Thanks!
UPDATE:
It seems a similiar question was asked here:
Loading MachineCode From File Into Memory and Executing in C -- mprotect Failing
where posix_memalign was suggested, I have tried this and it didn't work.
The problem is that you're not checking for an error in the FILE handle after a short read.
What the system would tell you is that the first fread failed and didn't trigger the fault handler.
If you checked for ferror outside the loop (sloppy as an example):
while(fread(buffer, pagesize*2, 1, file)) {
if (mprotect(buffer, size,
PROT_READ) == -1)
handle_error("mprotect");
}
if (ferror(file) != 0) {
cout << "Error" << endl;
}
Why it failed is that the underlying read failed, and returned an errno of 14 (EFAULT), which is not quite what is documented to happen when read fails in this situation (it says that Buf points outside the allocated address space.)
You can only trust the signal handler to be triggered in the mprotect case when the code in question is running in the user context, most system calls will fail and return EFAULT in the case that the buffer is invalid or does not have the correct permissions.
I am struggling with process creation and piping the child process' output into a string of the parent process. I got it working on Windows (using CreatePipe and CreateProcess and ReadFile), but can't seem to get the exact analog on Unix to work. This is my code:
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
int exit_code;
int cout_pipe[2];
int cerr_pipe[2];
posix_spawn_file_actions_t action;
if(pipe(cout_pipe) || pipe(cerr_pipe))
cout << "pipe returned an error.\n";
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);
posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);
vector<string> argmem = {"bla"};
vector<char*> args = {&argmem[0][0], nullptr}; // I don't want to call new.
pid_t pid;
if(posix_spawnp(&pid, "echo", &action, NULL, &args[0], NULL) != 0)
cout << "posix_spawnp failed with error: " << strerror(errno) << "\n";
//close(cout_pipe[0]);
//close(cerr_pipe[0]);
close(cout_pipe[1]);
close(cerr_pipe[1]);
waitpid(pid,&exit_code,0);
cout << "exit code: " << exit_code << "\n";
// Read from pipes
const size_t buffer_size = 1024;
string buffer;
buffer.resize(buffer_size);
ssize_t bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
while ((bytes_read = read(cout_pipe[0], &buffer[0], buffer_size)) > 0)
{
cout << "read " << bytes_read << " bytes from stdout.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n";
bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
}
if(bytes_read == -1)
cout << "Failure reading from stdout pipe.\n";
while ((bytes_read = read(cerr_pipe[0], &buffer[0], buffer_size)) > 0)
{
cout << "read " << bytes_read << " bytes from stderr.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)+1) << "\n";
bytes_read = read(cout_pipe[0], &buffer[0], buffer_size);
}
if(bytes_read == -1)
cout << "Failure reading from stderr pipe.\n";
posix_spawn_file_actions_destroy(&action);
}
The output is:
exit code: 0
So I suppose everything is working except the actual piping. What is wrong here? I also wonder if there is a way to read the piped bytes in a waitpid loop, but when I try that, the parent process hangs infinitely.
posix_spawn is interesting and useful, which makes this question worth necromancing -- even if it is no longer relevant to the OP.
There are some significant bugs in the code as posted. I suspect that some of these were the result of hacking in desperation, but I don't know which was the original bug:
The args array does not include the argv[0] that would represent the executable name. This results in the echo program never seeing the intended argv[1] ("bla").
The read() function is called from different places in a way that just doesn't make sense. A correct way to do this would be to only call read as part of the control expression for the while loops.
waitpid() is called before reading from the pipes. This prevents the I/O from completing (in non-trivial cases at least).
A more subtle issue with this code is that attempts to read all of the child's stdout before reading anything from stderr. In principle, this could cause the child to block while attempting to write to stderr, thus preventing the program from completing. Creating an efficient solution to this is more complicated as it requires that you can read from whichever pipe has available data. I used poll() for this. Another approach would be to use multiple threads.
Additionally, I have used sh (the command shell, i.e. bash) as the child process. This provides a great deal of additional flexibility, such as running a pipeline instead of a single executable. In particular, though, using sh provides the simple convenience of not having to manage the parsing of the command-line.
/*BINFMTCXX: -std=c++11 -Wall -Werror
*/
#include <spawn.h> // see manpages-posix-dev
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
int exit_code;
int cout_pipe[2];
int cerr_pipe[2];
posix_spawn_file_actions_t action;
if(pipe(cout_pipe) || pipe(cerr_pipe))
cout << "pipe returned an error.\n";
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);
posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);
//string command = "echo bla"; // example #1
string command = "pgmcrater -width 64 -height 9 |pgmtopbm |pnmtoplainpnm";
string argsmem[] = {"sh","-c"}; // allows non-const access to literals
char * args[] = {&argsmem[0][0],&argsmem[1][0],&command[0],nullptr};
pid_t pid;
if(posix_spawnp(&pid, args[0], &action, NULL, &args[0], NULL) != 0)
cout << "posix_spawnp failed with error: " << strerror(errno) << "\n";
close(cout_pipe[1]), close(cerr_pipe[1]); // close child-side of pipes
// Read from pipes
string buffer(1024,' ');
std::vector<pollfd> plist = { {cout_pipe[0],POLLIN}, {cerr_pipe[0],POLLIN} };
for ( int rval; (rval=poll(&plist[0],plist.size(),/*timeout*/-1))>0; ) {
if ( plist[0].revents&POLLIN) {
int bytes_read = read(cout_pipe[0], &buffer[0], buffer.length());
cout << "read " << bytes_read << " bytes from stdout.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
}
else if ( plist[1].revents&POLLIN ) {
int bytes_read = read(cerr_pipe[0], &buffer[0], buffer.length());
cout << "read " << bytes_read << " bytes from stderr.\n";
cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
}
else break; // nothing left to read
}
waitpid(pid,&exit_code,0);
cout << "exit code: " << exit_code << "\n";
posix_spawn_file_actions_destroy(&action);
}