Read a string from pipe - c

I have the following code:
int pfds[2], pid;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pfds) < 0) {
goto error;
}
if ((pid = fork()) == -1)
goto error;
if (pid == 0) {
/* child */
close(pfds[1]);
dup2(pfds[0], 1);
close(pfds[0]);
const char *argv[5];
int i = 0;
argv[i++] = "/bin/sh";
argv[i++] = "/sbin/script.sh";
argv[i++] = NULL;
execvp(argv[0], (char **) argv);
exit(ESRCH);
}
close(pfds[0]);
s.stream.string_data = true;
s.stream.notify_read = client_read_cb;
ustream_fd_init(&s, pfds[1]);
The script return a string (printed with echo command) that is mark the end of script loading.
How can I read the string returned by the /sbin/script.sh ?

Basically you are missing the differentiation between child process which executes the execv and the parent process which will do the reading:
char readbuffer[100];
if(pid == 0)
...
else
{
close(pdfs[0];
nbytes = read(pfds[1], readbuffer, sizeof(readbuffer));
}
But if you just want to wait for the end of the script, you can just do a wait_pid on the child process.

Related

Pipe communication (parent -> child -> parent)

I'm having the following code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
exit(-1);
}
int pfd[2]; // pipe
int pfd2[2]; // pipe2
pid_t pid; // child
if (pipe(pfd) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
if (pipe(pfd2) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
// child process
if (pid == 0) {
close(pfd[1]);
int v[100] = {0};
int k = 0;
int r;
char buf[128];
while ((r = read(pfd[0], buf, sizeof(buf)))) {
buf[r] = '\0';
// some processing here vor the 'v' variable
}
close(pfd[0]); // close the reading end 1st pipe
dup2(pfd2[1], 1); // redirect
close(pfd2[1]);
execlp("bash", "bash", "script.sh", v, NULL);
exit(-1);
} else {
// parent code
close(pfd2[1]);
dup2(pfd2[0], 0);
char buf[16];
int r = 0;
while ((r = read(pfd2[0], buf, sizeof(buf)))) {
buf[r] = '\0';
// read here from second pipe
}
close(pfd2[0]); // close reading end from 2pnd pipe
close(pfd[0]); // close reading end from 1st pipe
dup2(pfd[1], 1); // redirect
execlp("cat", "cat", argv[1], NULL); // exec 'cat' on the given arg
exit(-1);
}
return 0;
}
That respect the following flow: the parent process executes the 'cat' command on the given arg file, then pass it to the child -> the child does some processing and then stores values in the 'v' variable -> then executes the 'script.sh' script with the values taken from the 'v' variable and then passes the output to the parent which will print to stdout the output based on the result from the child.
I'm unsure where should the code for the 2nd pipe reading should go, I'm pretty sure that's the problem right now.
Can anyone take a look at it and point where the problem is? Thanks!

Creating Shell in C - Piping and External Commands

I am trying to create a small shell using C. At the moment I am trying to figure out piping and external commands. I got stuck in them both even after looking at various youtube videos.
I referred to MAN and even Advanced Linux Programming
What can I change to improve and make the implementation work?
This a part of the checking of commands, args := tokenisation by whitespace, commLHS := will be used to store args before | and commRHS will be used to store args after | and indexT refers to the number of arguments inputted
else if((check4pipe(args, commLHS, commRHS, indexT) != 0))
{
return runPipeComm(commLHS, commRHS);
//fprintf(stderr, "%s: command not found\n", args[0]);
}
This will execute External Commands
void externalCommands(char **args)
{
// fork-plus-exec pattern
// https://www.percona.com/community-blog/2021/01/04/fork-exec-wait-and-exit/
/*
First we Fork
Then we Exec
Then we Wait
Then we Exit
*/
int status;
pid_t pip = fork();
if (pip == -1)
{
perror("Error - fork()");
}
else if (pip == 0)
{
//If PID is the child process
//Launches the process.
if (execvp(args[0], args) < 0)
{
perror("Error - execvp()");
}
}
else
{ //If PID is the parent process.
//Waits for the child process and returns exit code if waitpid() is successful.
if(waitpid(pip, &status, WUNTRACED) == -1)
{
perror("Error occured during waitpi");
}
else
{
//set_exitcode(status); //Sets the exitcode environment variable.
}
}
}
This is to check for | in args inputted by user after tokenisation.
int check4pipe(char **args, char **pipeLHS, char **pipeRHS, int indexT)
{
bool foundPipe = false;
for(int i = 0; i < indexT; i++)
{
if(strcmp(args[i], "|") == 0)
{
foundPipe = true;
memcpy(pipeLHS, args, (i+1) * sizeof(char*));
pipeLHS[i] = NULL;
memcpy(pipeLHS, args+(i+1), ((indexT-i)+1) * sizeof(char*));
pipeRHS[i]= NULL;
}
else
{
continue;
}
}
if(foundPipe == true)
{
return 1;
}
else
{
return 0;
}
}
This will run the pipe commands
int runPipeComm(char **commLHS, char **commRHS)
{
int userPipe[2];
pid_t pip1; // Pipe ID 1
pid_t pip2; // Pipe ID 2
if(pipe(userPipe) < 0)
{
perror("Error Occurred while piping: ");
}
// Start Process
pip1 = fork();
if(pip1 == -1)
{
perror("Error Occurred while forking: ");
}
else if(pip1 == 0)
{
dup2(userPipe[1], STDOUT_FILENO);
close(userPipe[1]);
//run
exit(0);
}
else
{
pip2 = fork();
if(pip2 == -1)
{
perror("Error Occurred while forking: ");
}
else if(pip2 == 0)
{
dup2(userPipe[0], STDOUT_FILENO);
close(userPipe[1]);
//run
exit(0);
}
else
{
close(userPipe[0]);
close(userPipe[1]);
wait(NULL);
wait(NULL);
}
}
return 1;
}
You forgot to change some things when copying snippets within your program.
wrong:
memcpy(pipeLHS, args+(i+1), ((indexT-i)+1) * sizeof(char*));
pipeRHS[i]= NULL;
right:
memcpy(pipeRHS, args+(i+1), (indexT-(i+1)) * sizeof (char*));
pipeRHS[indexT-(i+1)] = NULL;
wrong:
dup2(userPipe[0], STDOUT_FILENO);
close(userPipe[1]);
right:
dup2(userPipe[0], STDIN_FILENO);
close(userPipe[0]);
After the first //run line, add the missing
execvp(*commLHS, commLHS);
perror("Error - execvp()");
After the second //run line, add the missing
execvp(*commRHS, commRHS);
perror("Error - execvp()");
Finally, the write end of the pipe must be closed in the parent process, so move the close() there:
close(userPipe[1]);
pip2 = fork();

Trying to build a shell with pipelines and background

Hi I am trying to recreate a shell and I am having two major problems:
1. After executing one single command it finishes the program
2. The pipeline doesn´t work
Here is the part of the code that deals with the pipes, redirections ...
int pfd[2];
if (pipe(pfd) < 0) exit(-1);
for (int i = 0; i < cmd.pipes; i++) {
pid_t pid;
pid = fork();
int fd;
if (pid < 0) exit(-1);
else if (pid == 0) {
close(pfd[0]);
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
if (cmd.filev[0] != NULL && i == 0) {
fd = open(cmd.filev[0], O_RDONLY, 0);
dup2(fd, STDIN_FILENO);
close(fd);
}
if (cmd.filev[2] != NULL) {
fd = creat(cmd.filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
if (execvp(cmd.argv[i][0], cmd.argv[i]) < 0)
levenshtein(cmd.argv[i][0], commands);
} else if (pid > 0) {
if (cmd.bg > 0) wait(NULL);
close(pfd[1]);
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
if (cmd.filev[1] != NULL && i == (cmd.pipes - 1)) {
fd = creat(cmd.filev[1], 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
if (cmd.filev[2] != NULL) {
fd = creat(cmd.filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
if (execvp(cmd.argv[i][0], cmd.argv[i]) < 0)
levenshtein(cmd.argv[i][0], commands);
}
}
PD:
Levenshtein is a function to deal when there is a misspell by the user
This needs a bit of refactoring ...
If there are N pipeline stages, we need N-1 separate pipe calls. The code only has one.
Each pipeline stage can have its own/private stderr diversion (e.g.):
cmd0 | cmd1 2>cmd1_stderr | cmd2 2>cmd2_stderr | cmd3
But, the code assumes all stderr will be the same.
And, it assumes that it has to open stderr [at all]. The child should only open stderr if we have something like above. Otherwise, each child should inherit the parent's stderr [and do nothing].
The parent process is doing things that only the child should do (e.g.): change stdin/stdout/stderr and execute the command. It should mostly just facilitate the pipe.
The parent is doing a wait in the creation/fork loop [for each child]. This causes the parent to block at each step.
The parent should only do the wait in a second loop and wait for all children at once.
Because your struct definition for cmd was not posted, I had to guess a bit as to intent. But, I think you should have an array of the structs, one for each command, rather than putting all arguments for all commands in a single struct.
I've created two versions of the code. One with annotations for the bugs. A second that is cleaned up and restructured. Both are untested but should give you some ideas.
Here's the annotated version:
// NOTE/BUG: each command in the pipeline can have its own/private
// stderr diversion
#if 0
struct cmd {
int pipes;
char *filev[3];
char **argv[MAXCMD][MAXARG];
};
#else
struct cmd {
char *stderr;
char *argv[100];
};
#endif
// NOTE/BUG: we need separate filev [and argv] for each pipeline stage
// so we need an _array_ of structs
int cmdcnt;
struct cmd cmdlist[30];
void
pipeline(void)
{
int pfd[2];
// NOTE/BUG: this only creates a single pipe -- we need N-1 pipes
#if 0
if (pipe(pfd) < 0)
exit(-1);
#endif
// NOTE/BUG: if child does _not_ have a private stderr it should just
// use the parent's stderr _unchanged_
for (int i = 0; i < cmdcnt; i++) {
pid_t pid;
// NOTE/BUG: here is the correct place to create the pipe
pid = fork();
int fd;
if (pid < 0)
exit(-1);
char **argv = cmd->argv;
char **filev = cmd->filev;
// child process
if (pid == 0) {
close(pfd[0]);
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
// NOTE/BUG: this does _not_ connect the input of cmd[N] to cmd[N-1]
#if 0
if (filev[0] != NULL && i == 0) {
fd = open(filev[0], O_RDONLY, 0);
dup2(fd, STDIN_FILENO);
close(fd);
}
#endif
if (filev[2] != NULL) {
fd = creat(filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
levenshtein(cmd->argv[i][0], commands);
}
// parent process
if (pid > 0) {
// NOTE/BUG: parent should _not_ wait in the middle of the creation
// loop
if (cmd->bg > 0)
wait(NULL);
// NOTE/BUG: _parent_ should _not_ change its stdin/stderr/stdout
close(pfd[1]);
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
if (filev[1] != NULL && i == (cmd->pipes - 1)) {
fd = creat(filev[1], 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
if (filev[2] != NULL) {
fd = creat(filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
// NOTE/BUG: _parent_ should _not_ execute the command
if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
levenshtein(cmd->argv[i][0], commands);
}
}
}
Here's the refactored version:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
struct cmd {
char *stderr;
char *argv[100];
};
char *filev[3];
#define CLOSEME(_fd) \
do { \
if (_fd < 0) \
break; \
close(_fd); \
_fd = -1; \
} while (0)
int run_in_background;
int cmdcnt;
struct cmd cmdlist[30];
int
pipeline(void)
{
int pfd[2] = { -1, -1 };
struct cmd *cmd;
char *file;
int oldfd;
pid_t pid;
pid_t lastpid = -1;
// open the common stderr
int errfd = -1;
if (filev[2] != NULL)
errfd = open(filev[2],O_APPEND | O_CREAT);
// start all commands
for (int i = 0; i < cmdcnt; i++) {
int iam_first = (i == 0);
int iam_last = (i == (cmdcnt - 1));
cmd = &cmdlist[i];
// get previous stage pipe descriptor
oldfd = pfd[0];
// create the pipe to the next stage
if (! iam_last) {
if (pipe(pfd) < 0)
exit(1);
}
pid = fork();
lastpid = pid;
int fd;
if (pid < 0)
exit(-1);
char **argv = cmd->argv;
// parent process
if (pid > 0) {
CLOSEME(pfd[1]);
continue;
}
// child process ...
// open stdin for _first_ command
fd = -1;
if (iam_first) {
file = filev[0];
if (file != NULL) {
fd = open(file, O_RDONLY, 0);
}
}
// connect stdin to previous stage pipe
else {
fd = oldfd;
oldfd = -1;
}
// connect stdin to correct source
if (fd >= 0) {
dup2(fd, STDIN_FILENO);
close(fd);
}
CLOSEME(oldfd);
// connect to stderr
file = cmd->stderr;
if (file != NULL) {
fd = creat(file, 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
else {
if (errfd >= 0)
dup2(errfd, STDERR_FILENO);
}
CLOSEME(errfd);
// connect stdout
// NOTE: does _not_ handle ">> outf" [only does "> outf"]
fd = -1;
if (iam_last) {
file = filev[1];
if (file != NULL) {
fd = open(file, O_WRONLY | O_CREAT, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
}
// execute the command
execvp(argv[0], argv);
exit(9);
}
CLOSEME(errfd);
int status;
int last_status = 0;
// parent waits for all pipeline stages to complete
if (! run_in_background) {
while (1) {
pid = wait(&status);
if (pid <= 0)
break;
if (pid == lastpid) {
last_status = status;
break;
}
}
}
return last_status;
}

C - Executing bash command with 2 pipes

I have an assignment for which I have to create C programm which has to do the following: The mother proccess has to send via pipe to the child proccess the result of the command ps hax -o user | sort | uniq -c. The child proccess has to take this result and write it to a file users.log.
This is the code I have written:
int main(int argc, char **argv)
{
int file_number;
pid_t pid, pid_2, pid_3, pid_4;
int n, pd[2],fd[2], zd[2];
char line[MAXLINE];
if ( pipe(pd) < 0 || pipe(fd) < 0 || pipe(zd) < 0)
{
perror("cannot create pipe."); // failure on create pipe
exit(1);
}
if ( (pid = fork()) < 0)
{
perror("cannot fork"); // failure on creating child proccess
exit(1);
}
else if ( pid > 0) // mother proccess
{
dup2(zd[1], STDOUT_FILENO);
close(zd[0]);
close(zd[1]);
if ( (pid_4 = fork()) < 0)
{
perror("cannot fork.");
exit(1);
}
else if (pid_4 > 0)
{
dup2(pd[1], STDOUT_FILENO);
close(pd[0]);
close(pd[1]);
close(fd[0]);
close(fd[1]);
execl("/bin/ps", "ps", "hax", "-o", "user", (char *)0);
}
if ( (pid_2 = fork()) < 0)
{
perror("cannot fork.");
exit(1);
}
else if (pid_2 == 0)
{
dup2(pd[0], 0);
dup2(fd[1], 1);
close(pd[0]);
close(pd[1]);
close(fd[0]);
close(fd[1]);
execl("/bin/sort", "sort", "", (char *)0);
}
if ( (pid_3 = fork()) < 0)
{
perror("cannot fork.");
exit(1);
}
else if (pid_3 == 0)
{
dup2(fd[0], 0);
close(pd[0]);
close(pd[1]);
close(fd[0]);
close(fd[1]);
execl("/bin/uniq", "uniq", "-c", (char *)0);
}
close(pd[0]);
close(pd[1]);
close(fd[0]);
close(fd[1]);
pid_t terminated;
int stat;
while ((terminated = wait(&stat)) > 0)
{
printf("Child %d terminated.\n", terminated);
}
}
else // child proccess
{
FILE *fptr = fopen("users.log", "a+");
if (fptr == NULL)
fprintf(stderr, "could not open file.\n");
file_number = fileno(fptr); // getting the file descriptor of fptr
dup2(zd[0], STDIN_FILENO);
close(zd[1]);
close(zd[0]);
n = read(zd[0], line, MAXLINE);
printf("Output: (%.*s)\n", n, line);
write(file_number, line, n);
}
return 0;
}
Running the code seems not to produce the desired result. Nothing is written to the file and I get this error:
Signal 17 (CHLD) caught by hax (procps-ng version 3.3.10).
hax:display.c:66: please report this bug
What am I missing here?

Writing data to fork pipe does not work

I have the following code in which I use fork to launch my script. The script is listening on the stdin. I try to send data via pipe to myscript but the scipt did not get the data from C. Am I missing something in my code?
static int pfds_in[2], pfds_out[2];
void external_init()
{
int pid;
if (pipe(pfds_in) < 0)
return;
if (pipe(pfds_out) < 0)
return;
if ((pid = fork()) == -1)
goto error;
if (pid == 0) {
/* child */
close(pfds_in[0]);
dup2(pfds_in[1], 1);
close(pfds_in[1]);
close(pfds_out[0]);
dup2(pfds_out[1], 0);
close(pfds_out[1]);
const char *argv[5];
int i = 0;
argv[i++] = "/bin/sh";
argv[i++] = fc_script;
argv[i++] = "--json";
argv[i++] = "json_continuous_input";
argv[i++] = NULL;
execvp(argv[0], (char **) argv);
exit(ESRCH);
}
close(pfds_in[1]);
close(pfds_out[1]);
return;
error:
exit(EXIT_FAILURE);
}
static void external_write_pipe_output(const char *msg)
{
char *value = NULL;
int i=0, len;
asprintf(&value, "%s\n", msg);
if (write(pfds_out[0], value, strlen(value)) == -1) {
perror("Error occured when trying to write to the pipe");
}
free(value);
}
int main()
{
external_init();
external_write_pipe_output("any");
}
You mismatched the both file descriptors you get from pipe(). pfds_in[0] is for reading, so you have to use dup2( pfds_in[0], 0 ) in your child and in the parent you write into the pipe using pfds_in[1].
Btw: What did you want to achieve by dup( ..., 1 ) in your child?. If you want to redirect child's stdout into a pipe to your parent you have to create another pipe
You've got your pipe ends in a muddle.
In the child you should have:
close(pfds_in[0]);
dup2(pfds_in[1], 1);
close(pfds_in[1]);
close(pfds_out[1]);
dup2(pfds_out[0], 0);
close(pfds_out[0]);
And in the parent:
close(pfds_in[1]);
close(pfds_out[0]);

Resources