C++ pipes between multiple children - c++

I am working on a project and I got it mostly figured out except for one minor(big) problem. I can't seem to figure out how to create pipes between any number of children.
for example I am taking in command line arguments to determine how many children will be produced. The first child doesn't have input but has output and the last child outputs to STD output. I need to pass values into the first child and into each child after that in order. Here is what i got:
#include <errno.h>
#include <cstdio>
#include <iostream>
#include <sys/wait.h>
using namespace std;
int main(int argc, char *argv[]) {
pid_t childpid;
int x2ypipe[2];
pipe(x2ypipe);
if(x2ypipe==0) {
cout<<"ERROR:"<<errno<<endl;
}
int y2zpipe[2];
pipe(y2zpipe);
if(y2zpipe==0) {
cout<<"ERROR:"<<errno<<endl;
}
pid_t xchild =fork();
if(xchild==0) {
dup2(x2ypipe[1],STDOUT_FILENO);
close(x2ypipe[0]);
close(x2ypipe[1]);
int a=execl(argv[1],argv[1], (char*)NULL);
if(a==-1) {
perror("The following error occurred at A");
}
}
for(int i=2; i<(argc-1); i++) {
childpid =fork();
if(childpid==0) {
dup2(x2ypipe[0],STDIN_FILENO);
close(x2ypipe[0]);
close(x2ypipe[1]);
//direct y2z pipe to standard output and replace the child with the program part2
dup2(x2ypipe[1],y2zpipe[1]);
dup2(y2zpipe[1],STDOUT_FILENO);
close(y2zpipe[0]);
close(y2zpipe[1]);
int b=execl(argv[i],argv[i],(char *)NULL);
if(b==-1) {
perror("The following error occurred at B");
}
}
}
pid_t zchild =fork();
if(zchild==0) {
dup2(y2zpipe[0],STDIN_FILENO);
close(y2zpipe[0]);
close(y2zpipe[1]);
int c=execl(argv[argc-1],argv[argc-1],(char *)NULL);
if(c==-1) {
perror("The following error occurred at C");
}
}
close(x2ypipe[0]);
close(x2ypipe[1]);
wait(NULL);
wait(NULL);
wait(NULL);
}
now right now I am only passing in three programs in to the argv[] and it works fine. I will have to add a if statement in my for loop to check for the last/highest possible value of i to connect the y2z pipe to the zchild. What I am having trouble doing it connecting the children to each other within the for loop. How would I go about creating a new pipe for each child from the last child?

Maybe this will help. Notice how I call pipe() inside my for loop, so I don't have to think of new "x2y", "y2z", "z2omega", etc, etc names for the pipe pairs.
Also notice how I used a variable prevfd from outside the for loop to carry the previous iterations's pipe file descriptor into the next iteration. And how it points to "/dev/null" to start with.
Finally, notice how I call wait() precisely as many times as I need to, in a loop, rather than writing it 3 (or 4 or 5 or ... 1,397) times.
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdlib>
#include <cstdio>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
int prevfd;
prevfd = open("/dev/null", O_RDONLY);
if(prevfd < 0) {
perror("/dev/null");
exit(1);
}
for(int i = 1; i < argc; ++i) {
int pipefd[2];
int kid;
if(i != argc-1 && pipe(pipefd)) {
perror("pipe");
break;
}
if(!fork()) {
dup2(prevfd, 0);
close(prevfd);
if(i != argc-1) {
dup2(pipefd[1], 1);
close(pipefd[0]);
close(pipefd[1]);
}
execl(argv[i], argv[i], (char*)0);
perror(argv[i]);
exit(1);
}
close(prevfd);
prevfd = pipefd[0];
close(pipefd[1]);
}
while(wait((int*)0) != -1)
;
return 0;
}

You need a separate pipe between each pair of connected processes.

Related

Trying to make a scalable pipe and execvp program using loop

This program is trying to any number of commands greater than one and use pipes, execvp, and fork to chain them together much like a shell would. In this code I have a hard coded "ls" "wc" and "less" that should come out like running "ls | wc | less" on a shell. For some reason, the pipes are not working as intended. I have a big block of comments explaining what I think the problem is on line 99 (starting with "The read end of the..." ). I know there is no error checking, any help is appreciated.
#include <iostream>
#include <string.h>
#include <string>
#include <vector>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
#define READ 0
#define WRITE 1
//This program will do three different commands ls, wc, then less.
int main(){
pid_t pid;
int cmd=3;
//One less pipe than command is required.
int fd[cmd-1][2];
//The pipes are created in a for loop.
for(int i=0; i<(cmd-1); i++){
if(pipe(fd[i])==-1){
cout<<"Help"<<endl;
}
}
//The commands are put in c.
char* c[3];
c[0]="ls";
c[1]="wc";
c[2]="less";
//First fork
pid=fork();
if(pid==0){
//Pipe 0 is linked up.
close(fd[0][READ]);
dup2(fd[0][WRITE], 1);
close(fd[0][WRITE]);
//Remaining pipes are closed.
for(int i=1; i<(cmd-1); i++){
close(fd[i][READ]);
close(fd[i][WRITE]);
}
//The command is prepared and then execvp is executed.
char* temp[2];
temp[0]=c[0];
temp[1]=NULL;
char* x=temp[0];
execvp(x, temp);
}
//This for loop executes two times less than the number of commands.
for(int i=0; i<(cmd-2); i++){
pid=fork();
if(pid==0){
//I link up the read connection with pipe 0, I am fairly certain that
//this part is working. You can put a cout after this pipe and it will
//print that of command 1.
close(fd[i][WRITE]);
dup2(fd[i][READ], 0);
close(fd[i][READ]);
//This is the linking of pipe 1.
close(fd[i+1][READ]);
dup2(fd[i+1][WRITE], 1);
close(fd[i+1][WRITE]);
//This closes the remaining pipes, in this case there are none.
for(int j=0; j<(cmd-1); j++){
if(j==i || j==(i+1)){
continue;
}
close(fd[j][READ]);
close(fd[j][WRITE]);
}
//The command is prepared and executed
char* temp[2];
temp[0]=c[i+1];
temp[1]=NULL;
char* x=temp[0];
execvp(x, temp);
}
}
pid=fork();
if(pid==0){
//The read end of the final pipe is linked here.
//THIS IS WERE THE PROBLEM IS! For some reason after dup2, I can no longer
//use cin. Inbetween the linking of pipe 0 and pipe 1 (line 66), I can
//use cin to make sure that the first execvp works and put its output in the
//pipe. I also know that the second execvp works as intended. I just need to
//know why dup2 messes up my program here.
close(fd[cmd-2][WRITE]);
dup2(fd[cmd-2][READ], 0);
close(fd[cmd-2][READ]);
//closes the remaining pipes.
for(int i=0; i<(cmd-2); i++){
close(fd[i][READ]);
close(fd[i][WRITE]);
}
//Preps next command.
char* temp[2];
temp[0]=c[cmd];
temp[1]=NULL;
char* x=temp[0];
execvp(x, temp);
//}
//closes all pipes.
for(int i=0; i<(cmd-1); i++){
close(fd[i][READ]);
close(fd[i][WRITE]);
}
return 0;
}
Your code has multiple problems
e.g. you've not allocated memory to commands and your code doesn't seem to be properly enclosed within brackets
I've modified your code as follows :
#include <iostream>
#include <string.h>
#include <string>
#include <vector>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
//This program will do three different commands ls, wc, then less.
int main(){
pid_t pid = 0;
int cmd=3, i;
//One less pipe than command is required.
int fd[cmd-1][2];
//The pipes are created in a for loop.
for(int i=0; i<(cmd-1); i++){
if(pipe(fd[i])==-1){
cout<<"Help"<<endl;
}
}
//The commands are put in c.
char c[3][8] = {{'l', 's', '\0'}, {'w', 'c', '\0'}, {'l','e','s','s', '\0'}}, *temp[2];
for(i = 0; i < cmd-1; i ++){
pid = fork();
if(pid == 0){
if(i != 0){
// read from previous fd
close(fd[i-1][1]);
dup2(fd[i-1][0], STDIN_FILENO);
close(fd[i-1][0]);
}
// write to current fd
close(fd[i][0]);
dup2(fd[i][1], STDOUT_FILENO);
close(fd[i][1]);
temp[0] = c[i];
temp[1] = NULL;
execvp(c[i], temp);
exit(0);
}
else{
if(i != 0){
// close unnecessary fds in parent
close(fd[i-1][0]);
close(fd[i-1][1]);
}
}
}
// the last command i.e. less here
if(i > 0){
close(fd[i-1][1]);
dup2(fd[i-1][0], STDIN_FILENO);
close(fd[i-1][0]);
}
temp[0] = c[i];
temp[1] = NULL;
execvp(c[i], temp);
return 0;
}
Let me know if it works for you!

Open TTY to use with execlp and dup

I am trying to create a minimal code to use pipe/fork/execlp.
So far so good, I am using execlp with bash -c, so if I do.
echo asd |./a.out cat
> asd
So it is working as expected.
But if I try to use anything that needs a TTY, it does not work.
Like ./a.out vim, I get "Vim: Warning: Input is not from a terminal"
And the vim that was open does not works as expected.
I tried to find on the internet an example on how to open a TTY, the only one that I found was:
http://www.danlj.org/lad/src/minopen.c
My Code, so far is:
#include <iostream>
#include <cstdio>
#include <string.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
typedef struct pCon{
int fout[2];
int fin[2];
int fd[2];
int pid1, pid2;
} connectionManager;
std::string command = "";
/*
* Implementation
*/
void childFork(connectionManager *cm);
int main(int argc, char *argv[]) {
int size;
if(argc < 2) exit(1);
else command = argv[1];
connectionManager *cm = new connectionManager;
pipe(cm->fd);
if((cm->pid1 = fork()) == -1)exit(1);
if (cm->pid1 == 0)
{
const unsigned int RCVBUFSIZE = 2000;
char echoString[RCVBUFSIZE];
while((size = read(fileno(stdin),echoString,RCVBUFSIZE)) > 0)
write(cm->fd[1], echoString, size);
close(cm->fd[1]);
}
else
childFork(cm);
return 0;
}
void childFork(connectionManager *cm){
char *buffer = new char[2000];
int size;
close(cm->fd[1]);
dup2(cm->fd[0], 0);
close(cm->fd[0]);
pipe(cm->fout);
if((cm->pid2 = fork()) == -1)exit(1);
if (cm->pid2 == 0)
{
close(cm->fout[0]);
int returnCode = execlp("bash", "bash", "-c", command.c_str(), NULL);
if(returnCode!=0)
std::cerr << "Error starting the bash program" << std::endl;
}
else
{
close(cm->fout[1]);
while((size = read(cm->fout[0], buffer, 2000 )) > 0 )
write(fileno(stdout), buffer, size);
}
}
I tried to keep the minimal necessary code to make it work.
Is there any way to implement TTY on this code, I know that does not seems to be such trivial task.
Can someone help me with that?
I also tried to open the tty and dup it, but no luck so far.
Try to use pseudo terminal. You can use opentty. For your purpose you can use forkpty which combines pty with fork. I've created a small example for you. About the same as your program, just it works. I've kept it simple, so I don't handle the terminal control characters.
#include <pty.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/select.h>
int main(int argc, char *argv[])
{
if (argc<1) return 1;
int master;
pid_t pid = forkpty(&master, NULL, NULL, NULL); // opentty + login_tty + fork
if (pid < 0) {
return 1; // fork with pseudo terminal failed
}
else if (pid == 0) { // child
char *args[] = { argv[1], argv[2], NULL }; // prg + 1 argument
execvp(argv[1], args); // run the program given in first param
}
else { // parent
struct termios tios;
tcgetattr(master, &tios);
tios.c_lflag &= ~(ECHO | ECHONL);
tcsetattr(master, TCSAFLUSH, &tios);
while(1) {
fd_set read_fd, write_fd, err_fd;
FD_ZERO(&read_fd);
FD_ZERO(&write_fd);
FD_ZERO(&err_fd);
FD_SET(master, &read_fd);
FD_SET(STDIN_FILENO, &read_fd);
select(master+1, &read_fd, &write_fd, &err_fd, NULL);
if (FD_ISSET(master, &read_fd))
{
char ch;
int c;
if (c=read(master, &ch, 1) != -1) // read from program
write(STDOUT_FILENO, &ch, c); // write to tty
else
break; // exit when end of communication channel with program
}
if (FD_ISSET(STDIN_FILENO, &read_fd))
{
char ch;
int c=read(STDIN_FILENO, &ch, 1); // read from tty
write(master, &ch, c); // write to program
}
}
}
return 0;
}
For compiling use -lutil .
While running a new tty device appears in /dev/pts .
vim accepts it as a terminal.

Programming language to choose to execute bash commands

For my school project I must make a C++ program. The program has to use already installed applications on the Linux OS. To get familiar with managing other processes from within a C++ application I want to make a program that sets a wlan interface to monitor mode. The code that I have written so far is pretty long and doesn't seems efficient. Are there any ways to make my code more compact and efficient? At the end of the program I want to execute iwconfig to check if the wlan really is in monitor mode. What is the best way to do that?
#include <unistd.h>
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main(int argc, char *argv[])
{
string wlan = "wlan1";
pid_t ifconfigDown;
ifconfigDown = fork();
if(ifconfigDown == 0)//ifconfig
{
execl("/sbin/ifconfig", "ifconfig", wlan.c_str(), "down",(char*)0 );
}
else //parent
{
usleep(500000);
pid_t iwconfigMode;
iwconfigMode = fork();
if(iwconfigMode == 0)//ifconfig
{
execl("/sbin/iwconfig","iwconfig",wlan.c_str(),"mode","monitor",(char*)0 );
}
else//parent
{
usleep(500000);
pid_t ifconfigUp;
ifconfigUp = fork();
if(ifconfigUp == 0)//ifconfig
{
execl("/sbin/ifconfig", "ifconfig", wlan.c_str(), "up", (char*)0 );
}
else//parent
{
usleep(500000);
pid_t iwconfig;
iwconfig = fork();
if(iwconfig == 0)//iwconfig
{
execl("/sbin/iwconfig", "iwconfig", (char*)0 );
//check if wlan1 is in monitor mode
}
}
}
waitpid(-1, NULL, 0);
return 0;
}
}
string rPopenEnd (string cmd)
{
FILE *fp = popen(cmd.c_str(), "r");
if (fp == NULL)
{
return "ERROR";
}
else
{
uint16_t line_size = 20;
char line[line_size];
string result;
while (fgets(line, line_size, fp))
result += line;
wait(NULL);
return result;
}
}
int main(int argc, char *argv[])
{
rPopenEnd("iwconfig");
}

C++ character array not being read right through pipe

I'm trying to check that the cmd variable is set to "LISTALL" but it isn't when I try printing it out.
#include <stdio.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <sys/wait.h>
int main(int argc, char **argv)
{
pid_t cPid = fork();
int P2C[2];
int C2P[2];
pipe(P2C);
pipe(C2P);
char cmd[50];
char* listOfProcesses = new char[1024];
if (cPid == 0)
{
...
read(P2C[0], cmd, 50);
printf("%s\n", cmd);
if(strcmp(cmd,"LISTALL") == 0)
{
//printf("Executing the command: %s", cmd);
write(C2P[1], getlistOfProcesses("ps -ax -o pid,cmd"), 1024);
...
}
}
else if (cPid > 0)
{
...
write(P2C[1], "LISTALL", 50);
wait(NULL);
read(C2P[0], listOfProcesses,1024);
...
}
else
{
// fork failed
printf("Forking failed!\n");
exit(1);
}
return 0;
}
What I get from that is a mini box symbol with 00 at the top and 01 or 02 at the bottom. I tried pasting the symbol here but it doesn't show.
You create 4 pipes: two in the parent process and two in the child process.
Create the pipes before forking! Then fork, then check whether you are in the parent process or in the child process.
That way you have only two pipes, both processes know about these pipes and can communicate by reading or writing to the appropriate file descriptors of the pipes.

Message queue bad file descriptor error

I write a simple application to understand POSIX message queues. But the app continuously gives "Bad file descriptor" error.
Thanks to stackoverflow users. We find the solution. Below is the updated code.
#include <mqueue.h>
#include <string.h>
#include <iostream>
#include <errno.h>
using namespace std;
int main(int argc, char *argv[])
{
mqd_t messageQueue;
mq_attr attr;
messageQueue = mq_open("/test",O_RDWR|O_CREAT,0664,&attr);
attr.mq_maxmgs = 10;
attr.mq_msgsize = 4;
char c;
int pid = fork();
//client
if(pid == 0) {
if(mq_receive(messageQueue,&c,1,0) == -1)
cout<<"Error:"<<strerror(errno)<<"\n";
cout<<"Received:"<<c<<"\n";
}
//server
else if(pid > 0) {
c = 'a';
if(mq_send(messageQueue,&c,1,0) == -1)
cout<<"Error:"<<strerror(errno)<<"\n";
cout<<"Send:"<<c<<"\n";
mq_close(messageQueue);
}
else {
cout<<"Fork error\n";
}
return 0;
}
Since you provide O_CREAT flag and an attribute list to mq_open, you should explicitly set attr.mq_maxmsg and attr.mq_msgsize to a positive value.
error exist because the receiving buffer size is not greater than mq_msgsize attribute.
just do two things make an array say char c1[50] and a pointer to it say char *ptr=c1;
pass this pointer in receive method and when u print message print c1[0] that's it.
Also update the size in receive method to 50 and not 1.