I'm trying to implement piping in C++. My method piped takes in an array of arguments along the line of yes | head -10 | wc. It creates pipes number of processes and changes the image of the processes to, following that example, yes, head -10, and wc. Then it creates pipes-2 pipes and redirects the input and output of the processes to the pipes.
It's not working correctly, and just prints each command separately without redirecting input/output.
#include "piped.h"
using namespace std;
void piped(char *stringList[], int pipes)
{
//stringList[] is list of arguments seperated by |
//pipes = number of arguments
pid_t processList[256];
char *curList[256];
//Create a new process for each job using fork
for(int i = 0; i < pipes; i++){
processList[i] = fork();
if (processList[i] < 0) {
cerr << "Couldn't create process\n";
exit(1);
}
}
int j = 0;
//Replace the image of each process with the appropriate commands
for(int i = 0; i < pipes; i++){
pid_t pid = processList[i];
if(pid == 0){
char *check = stringList[j];
int k = 0;
while(true){
curList[k] = check;
j+=1;
k+=1;
check = stringList[j];
if(check == NULL || strcmp(check, "|") == 0){
break;
}
}
j+=1;
curList[k] = NULL;
execvp(curList[0], curList);
cout << "Exec error!\n";
exit(1);
}
}
//Piping
//create n-2 pipes
//redirect the output of job i to the write end of pipe i
//redirect the input of job i to the read end of pipe i
for(int i = 0; i < pipes-1; i++){
int pipefd[2];
if(pipe(pipefd) < 0){
cout << "Pipe error!\n";
}
int pid1 = processList[i];
int pid2 = processList[i+1];
if(0 == pid2){
//Child 2
close(pipefd[1]);
dup2(pipefd[0],1);
close(pipefd[0]);
}
if(0 == pid1){
close(pipefd[0]);
dup2(pipefd[1], 0);
close(pipefd[1]);
}
}
//Wait for all jobs to terminate
for(int i = 0; i < pipes; i++){
pid_t pid = processList[i];
waitpid(pid, NULL, 0);
}
}
Related
I have this code which runs the command I give to it:
pid_t Utils::RunCommand(std::string cmd, int * infp, int * outfp)
{
std::cout << cmd << std::endl;
const char* command = cmd.c_str();
int p_stdin[2];
int p_stdout[2];
pid_t pid;
if (pipe(p_stdin) == -1)
return -1;
if (pipe(p_stdout) == -1) {
close(p_stdin[0]);
close(p_stdin[1]);
return -1;
}
pid = fork();
if (pid < 0) {
close(p_stdin[0]);
close(p_stdin[1]);
close(p_stdout[0]);
close(p_stdout[1]);
return pid;
}
else if (pid == 0) {
close(p_stdin[1]);
dup2(p_stdin[0], 0);
close(p_stdout[0]);
dup2(p_stdout[1], 1);
dup2(::open("/dev/null", O_RDONLY), 2);
/// Close all other descriptors for the safety sake.
for (int i = 3; i < 4096; ++i)
::close(i);
setsid();
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(1);
}
close(p_stdin[0]);
close(p_stdout[1]);
if (infp == NULL) {
close(p_stdin[1]);
}
else {
*infp = p_stdin[1];
}
if (outfp == NULL) {
close(p_stdout[0]);
}
else {
*outfp = p_stdout[0];
}
return pid; }
The problem I am having with this code is the returned process ID is for the shell process that runs my command which prevents me from checking if the command I ran is still running. How can I either modify this function to instead return to me the created process or find the child's PID from the parent PID?
I am writing a code where I have to read from input files and then read the output. One of the files is large and is around 38 MB. I'm not very good at programming so by looking at some tutorials and xillybux programming guide, I wrote the following code to read from two files, write them to the logic and then read them. My motive is to calculate the time of reading and writing for my future project. But when I try to run the program I get the following error
Failed to open Xillybus device file(s), device resource busy
I don't know what I have done wrong. The code which I had written is below
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/time.h>
#include <math.h>
#include <string>
#include <cstdlib>
#define N ((cols+1)*rows + cols)
int main(int argc, char *argv[])
{
int fdr, fdw;
FILE *ftime_rw;
FILE *fp;
FILE *U, *V, *RI;
int rc, donebytes;
int *tologic, *fromlogic;
pid_t pid;
int i,j;
struct timeval tv1, tv2;
double time_rw;
char *buf;
int temp;
int rows= 575;
int cols= 18689;
fdr = open("/dev/xillybus_read_32", O_RDONLY);
fdw = open("/dev/xillybus_write_32", O_WRONLY);
if ((fdr < 0) || (fdw < 0))
{
perror("Failed to open Xillybus device file(s)");
exit(1);
}
U = fopen("U.txt", "r");
V = fopen("V.txt", "r");
RI = fopen("RI.txt", "a");
if(U==NULL || V==NULL || RI==NULL )
{
printf("Write in their respective files!\n");
exit(1);
}
for (i=0; i<rows; i++) // Read
{
fscanf(U,"%d", &temp);
fprintf(RI,"%d\n", temp);
}
for(j=0; j<rows; j++)
{
for (i=0; i<cols; i++) // Read
{
fscanf(V,"%d", &temp);
fprintf(RI,"%d ", temp);
}
fclose(U);
fclose(V);
fclose(RI);
tologic = (int*)malloc(sizeof(int)* N);
if (!tologic)
{
fprintf(stderr, "failed to allocate memory\n");
exit(1);
}
RI = fopen("RI.txt", "r");
for(j=0; j<rows; j++)
{
for (i=0; i<cols; i++)
fscanf(RI,"%d", &tologic[N]);
fclose(RI);
pid = fork(); // writer + reader
if (pid < 0)
{
perror("Failed to fork()");
exit(1);
}
if (pid) // writer process
{
close(fdr);
buf = (char *)tologic;
donebytes=0;
gettimeofday(&tv1, NULL); // start count time
while (donebytes < sizeof(int)*N) // write N integers
{
rc = write(fdw, tologic + donebytes, sizeof(int)*N - donebytes);
if ((rc < 0) && (errno == EINTR))
continue;
if (rc <= 0)
{
perror("write() failed");
exit(1);
}
donebytes += rc;
}
gettimeofday(&tv2, NULL); // stop count time
time_rw = (double) (tv2.tv_usec-tv1.tv_usec);
printf("Writer. Total time = %f usec\n", time_rw);
if(close(fdw)==-1)
printf("ERROR closing write_32 file!\n");
return 0;
}
else // reader process
{
close(fdw);
fromlogic =(int*)malloc(sizeof(int)* N);
if (! fromlogic)
{
fprintf(stderr, "failed to allocate memory\n");
exit(1);
}
buf = (char *)fromlogic;
donebytes = 0;
donebytes = 0;
gettimeofday(&tv1, NULL); // start count time
while (donebytes < sizeof(int)*rows) // read num_rows integers
{
rc = read(fdr, fromlogic + donebytes, sizeof(int)*rows - donebytes);
if ((rc < 0) && (errno == EINTR))
continue;
if (rc < 0)
{
perror("read() failed");
exit(1);
}
if (rc == 0)
{
fprintf(stderr, "Reached read EOF!? Should never happen.\n");
exit(0);
}
donebytes += rc;
}
gettimeofday(&tv2, NULL); // stop count time
time_rw = (double) (tv2.tv_usec-tv1.tv_usec);
printf("Reader. Total time = %f usec\n", time_rw);
for (i=0; i<rows; i++) // Integers read from logic
printf("Reader process : %d\n", fromlogic[i]);
sleep(1); // Let debug output drain (if used)
if(close(fdr)==-1)
printf("ERROR closing read_32 file!\n");
return 0;
}
}
}
}
My goal is to get the output of the first command and use it as the input for the second command, but the problem is that it is only going through my parent condition, (pid > 0), and never the child. I have tried duping in the parent, but it didnt work. Any help will be appreciated! In case you were wondering, I am doing this in C:
int pipe_a1[2];
int pipe_a2[2];
command1 = {'ls','-a'}
command2 = {'cat','newFile'}
if (pipe(pipe_a1) < 0 || pipe(pipe_a2) < 0) {
cout << "pipe failed." << endl;
exit(1);
}
pid = fork()
if (pid == 0) {
close(pipe_a1[1]);
close(pipe_a2[0]);
dup2(pipe_a1[0], STDIN_FILENO);
dup2(pipe_a2[1], STDOUT_FILENO);
execvp(command2[0],command2)
} else if(pid > 0) {
close(pipe_a1[0]);
close(pipe_a2[1]);
execvp(command1[0],command1)
} else {
printf("Fork Failed");
}
The following program is written to receive clients' messages and read user inputs from keyboard:
FD_ZERO(&masterfds);
FD_SET(udp_con, &masterfds);
FD_SET(0, &masterfds);
maxfds = udp_con;
while(exit == false)
{
FD_ZERO(&readfds);
readfds = masterfds;
selectFunc = select(maxfds+1, &readfds, NULL, NULL, &tv);
if(selectFunc < 0) {
message("error in select");
exit = true;
} else {
// The server has received something from a client
for(i = 0; i <= maxfds; i++) {
if(FD_ISSET(i, &readfds)) {
if(FD_ISSET(0, &readfds)) {
fgets(userInput, sizeof(userInput), stdin);
int len = strlen(userInput) - 1;
if (userInput[len] == '\n') {
userInput[len] = '\0';
}
cout<<"The user said: "<<userInput<<endl;
} else if(i == udp_con) {
cout<<"Datagram received"<<endl;
// After reading the user input, it never reaches here
}
}
}
}
}
The problem is that when I press the 'enter' key on the keyboard and the '0' file descriptor activates, the program will never activate any other file descriptor and it locks on the '0' file descriptor. How can I fix this bug?
You need to FD_CLR(0, readfds) after you test it with FD_ISSET(0, &readfds), or else it will always take that branch.
But you can simplify the algorithm by rewriting it. It's a good idea to get into the habit of formatting your code to make it easily readable.
FD_ZERO(&masterfds);
FD_SET(udp_con, &masterfds);
FD_SET(0, &masterfds);
maxfds = udp_con;
while (true) {
readfds = masterfds;
selectFunc = select(maxfds + 1, &readfds, NULL, NULL, &tv);
if (selectFunc < 0) {
message("error in select");
break;
}
// Check for input on stdin (fd 0).
if (FD_ISSET(0, &readfds)) {
fgets(userInput, sizeof(userInput), stdin);
int len = strlen(userInput) - 1;
if (userInput[len] == '\n') {
userInput[len] = '\0';
}
cout << "The user said: '" << userInput << "'" << endl;
}
// Check for input on the udp_con fd.
if (FD_ISSET(udp_con, &readfds)) {
cout << "Datagram received" << endl;
}
}
In my program I'm redirecting output of child process to pipe and in parent getting this result and doing something (this is not important). But my program doesn't work when I'm using tail -f test.txt command and doesn't recieve any data during tail is running, and getting this data only after tail is finished (or killed).
At first I have thought that problem was that tail -f doesn't flushing and that's why no data I can recieve, but when I have tried to redirect output of tail -f to some file the data were in this file even when tail were not finished.
//the code of creating child and redirecting data (important part)
//only core is here so please don't tell me that maybe pipe() or fork() is failed
pid_t pid;
int outpipe[2]; //pipe for reading from stdout
int errpipe[2]; //pipe for reading from stderr
// Createing pipes for childs stdout and stderr streams
pipe(outpipe);
pipe(errpipe);
pid = fork();
if(pid == 0)
{
// This is the child process. Closing read end of pipes and duplicating stdout and stderr streams
close(outpipe[0]);
dup2(outpipe[1], STDOUT_FILENO);
close(errpipe[0]);
dup2(errpipe[1], STDERR_FILENO);
if(execvp(argv[0], (char * const *)argv) == -1)
{
fprintf(stderr, "Failed to execute command %s: %s", argv[0], strerror(errno));
_exit(EXIT_FAILURE);
}
_exit(EXIT_SUCCESS);
}
else if (pid != -1)
{
// This is the parent process, Closing write end of pipes and opening fds as FILE
close(outpipe[1]);
*child_stdout_stream=fdopen(outpipe[0], "rt");
close(errpipe[1]);
*child_stderr_stream=fdopen(errpipe[0], "rt");
*child_pid=pid;
}
Then I'm reading from child_stderr_stream and child_stdout_stream which were passed as parameters to function the part above is from what.
For reading I'm using select() to not block program until reading from one of the streams.
Adding part of select and read
int select_and_read(FILE **files, bool *is_eof, char *chars, int *mask, int nfiles, int timeout, pid_t child_pid)
{
int max_fd_plus_1 = 0;
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
for(int i = 0; i < nfiles; ++i)
{
if(is_eof[i]==false)
{
FD_SET(fileno(files[i]), &rfds);
max_fd_plus_1 = (max_fd_plus_1 > fileno(files[i])) ? max_fd_plus_1 : fileno(files[i]);
}
}
++max_fd_plus_1;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
int retval = select(max_fd_plus_1, &rfds, NULL, NULL, &tv);
if(retval > 0)
{
*mask = 0;
for(int i = 0; i < nfiles; ++i)
{
if(is_eof[i]==false)
{
if(FD_ISSET(fileno(files[i]), &rfds))
{
*mask |= 1 << i;
chars[i] = fgetc(files[i]);
}
}
}
}
else
{
kill(child_pid, SIGKILL);
}
return retval;
}
This strange problem have been solved very strangely. I have just set buffers of files to 0 this way:
else if (pid != -1)
{
// This is the parent process, Closing write end of pipes and opening fds as FILE
close(outpipe[1]);
*child_stdout_stream=fdopen(outpipe[0], "rt");
setbuf(*child_stdout_stream, NULL);
close(errpipe[1]);
*child_stderr_stream=fdopen(errpipe[0], "rt");
setbuf(*child_stderr_stream, NULL);
*child_pid=pid;
}
This is very strange, that this helps, but in any case my program is now working well.