C - Pipe input from one program to another program - c

I'm supposed to create two programs (main and aux), where main forks a child to execute aux. The parent takes input from the user, until blank line '\n', and the child executes aux, which is supposed to print the input back out. I'm able to get it to work in main with the commented code instead of execlp(), but cannot get execlp(aux) to work correctly. Any help is appreciated.
"main.c"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2], i;
char line[100], buffer[100];
pipe(fd);
pid_t pid = fork();
if (pid < 0) {
printf("Fork Failed\n");
exit(-1);
}
else if (pid > 0) {
close(fd[0]);
while(fgets(line, sizeof(line), stdin) && line[0] != '\n') {
write(fd[1], line, sizeof(line));
}
close(fd[1]);
}
else {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
//while(read(fd[0], buffer, sizeof(buffer)))
// printf("> %s", buffer);
execlp("./aux", "aux", (char *)0);
}
return 0;
}
"aux.c"
#include <stdio.h>
#include <stdlib.h>
int main() {
char data[100];
while(fgets(data, sizeof(data), stdin))
printf(">%s\n", data);
return 0;
}
sample input/output
this
>this
is a test
>
> test
only prints larger text with random \n
>
>ts larger text with random \n

Your call to write(2) is wrong (you always write 100 bytes even for shorter line-s):
write(fd[1], line, sizeof(line)); // WRONG
should probably be using strlen(3)
size_t ll = strlen(line);
ssize_t wc = write(fd[1], line, ll);
if (wc != ll)
fprintf(stderr, "write was wrong (only %d, wanted %d) - %s\n",
(int) wc, (int) ll, strerror(errno));
Since you want to write only the filled bytes of the line buffer, not always 100 bytes each time (some of them not being initialized).
In your case sizeof(data) is 100 since you declared char data[100];
Please read carefully the documentation of every used function (and also ALP or some other book on Unix/POSIX/Linux programming). The documentation of strerror(3) and of errno(3) tells that you need to add:
#include <string.h>
#include <errno.h>
Actually, if you want to use read(2) and write(2) directly (without stdio(3)) you should prefer using larger buffers (e.g. 4Kbytes each at least for efficiency) and you need to manage partial read-s and write-s and do your buffering by yourself.
BTW, compile with all warnings and debug info: gcc -Wall -Wextra -g and learn to use the gdb debugger and strace(1) (and valgrind). In general, be scared of undefined behavior (however, at a first glance, your program don't seem to have UB).
Notice that execlp(3) could fail. Consider adding some call to perror(3) after it.

Related

execvp() function returns -1 all the time

In my code, I'm trying to use the function execvp() to execute a command that I get in my shell but the function always returns -1 that indicates unsuccess, when I replace the function first argument by (for example) "ps" it works fine but when it is (command) it doesn't work, I've checked that command is fine by printing it after getting it from the input line and it is a fine string with no problems, but the function keeps returning me an error!!
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 100
int main(void)
{
close(2);
dup(1);
char command[BUFFER_SIZE];
while (1)
{
char *arg[3];
fprintf(stdout, "my-shell> ");
memset(command, 0, BUFFER_SIZE);
fgets(command, BUFFER_SIZE, stdin);
if(strncmp(command, "exit", 4) == 0)
{
break;
}
arg[0] = command;
arg[1] = "\0";
arg[2] = "\0";
i = execvp(command,arg);
printf("%d",i);
}
return 0;
}
I expect that the problem is in the way that command is passed in the function but after trying so much edites to the code, I still can't figure out what the problem really is!
There are 3 major problems and 1 minor one that can be picked out of the code shown (plus what I take to be an artefact of reducing your full code to the code in the question, plus some oddities):
The fgets() function includes the newline in the returned string unless the line is too long (a separate problem). You need to zap that newline:
command[strcspn(command, "\n")] = '\0';
The code does not parse the line that's entered, so only single word commands can sensibly be entered. To fix that, you'd have to be prepared to split the line into words using an appropriate algorithm, removing quotes where appropriate, expanding variables and so on. That will be part of the later stages of developing your shell.
The second argument to execvp() needs to be a NULL-terminated list of strings. You only provide the command name and two empty strings without the null terminator, which gives undefined behaviour.
The minor problem is that using "\0" instead of just "" is pointless.
The artefact is that there is no fork() in the code, so if the command is executed successfully, the 'shell' is replaced by the command and exits when the replacement exits.
The close(2); dup(1); sequence is weird — it means standard error refers to the same file descriptor as standard output. Those lines really aren't needed (or desirable). Leave the errors separate from standard output.
The memset() is superfluous too. Using fprintf(stdout, "my-shell> "); is a funny way of writing printf("my-shell> ");. Using strncmp(command, "exit", 4) means that if the user types exit-or-continue, you'll treat it the same as exit, which is far from ideal.
Putting most of those numerous changes into effect (omitting parsing the command line into separate arguments) leaves:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define BUFFER_SIZE 100
int main(void)
{
char command[BUFFER_SIZE];
while (1)
{
printf("my-shell> ");
fflush(stdout);
if (fgets(command, BUFFER_SIZE, stdin) != command)
break;
command[strcspn(command, "\n")] = '\0';
if(strcmp(command, "exit") == 0)
{
break;
}
int pid = fork();
if (pid < 0)
{
fprintf(stderr, "failed to fork()\n");
exit(EXIT_FAILURE);
}
if (pid == 0)
{
/* Child - execute command */
/* Should break line into command plus arguments */
char *arg[2];
arg[0] = command;
arg[1] = NULL;
execvp(command, arg);
fprintf(stderr, "failed to execute command '%s'\n", command);
exit(EXIT_FAILURE);
}
/* Parent - wait for child to finish */
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
{
if (corpse == pid)
break;
printf("PID %d exited with status 0x%.4X\n", corpse, status);
}
}
return 0;
}

What is the importance of adding "\n" to stdout when it's redirected to another process?

So, I'm playing with pipes in c, and I have an exercise where I call a program through command line as this: "./self 1" which then calls itself with execlp but with an argument 2: "./self 2" which further on calls itself with argument 3: "./self 3". The point of these processes is this: process1 takes a line from keyboard and puts it in pipe1, then process2 gets the line from pipe1 and puts it in pipe2, then process3 gets it from pipe2 and counts the number of space characters. This code never works if I dont print a newline character on the screen before taking inputs with fprintf(stdout,"\n"); . Why is that?
Here is the code:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
if (strcmp(argv[1], "1") == 0) {
int fdpipe1[2];
if (pipe(fdpipe1)) {
printf("Error pipe1\n");
return 0;
}
pid_t p;
p = fork();
if (p == 0) {
close(fdpipe1[1]);
dup2(fdpipe1[0], 0);
execlp("./self", "./self", "2", NULL);
} else {
close(fdpipe1[0]);
fprintf(stdout, "\n");
dup2(fdpipe1[1], 1);
char input[100];
gets(input);
puts(input);
wait(NULL);
}
}
else if (strcmp(argv[1], "2") == 0) {
int fdpipe2[2];
if (pipe(fdpipe2)) {
printf("Error pipe2\n");
return 0;
}
pid_t p;
p = fork();
if (p == 0) {
close(fdpipe2[1]);
dup2(fdpipe2[0], 0);
execlp("./self", "./self", "3", NULL);
} else {
close(fdpipe2[0]);
fprintf(stdout, "\n");
dup2(fdpipe2[1], 1);
char input[100];
gets(input);
puts(input);
wait(NULL);
}
}
else if (strcmp(argv[1], "3") == 0) {
char input[100];
gets(input);
int i = 0, counter = 0;
while (input[i] != '\0') {
if (input[i++] == ' ') counter++;
}
printf("%d\n", counter);
}
return;
}
In this kind of construct, when you connect stdout from a process to stdin of another process via unnamed pipe, a newline character is added usually to ensure the stream is sent, i.e. the stdout buffer is flushed, as a parallel example, when you use scanf, only when you hit enter (a newline is added to stdin) is the stream read, a similar principle applies here.
I would suggest you use STDIN_FILENO and STDOUT_FILENO
built in macros instead of the hard coded file descriptors, if not for anything else, it makes the code more readable for someone who is unfamiliar with the matter.
Please avoid using gets, this is a dangerous function, it does not check the bounds of the destination buffer, it can cause all kinds of trouble, so much so it was deprecated and later removed from the standard, though it still can be used with some compilers, for legacy reasons I would imagine, check this fantastic answer on a post about this topic:
Why is the gets function so dangerous that it should not be used?
The advice is to use fgets instead.

how to make read() in thread block on a pipe's file descriptor?

I'm experimenting on how to communicate between a thread and the main function in C
There is a behavior that I don't understand in the following code :
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
void* output(void* pipe1);
int main(int argc, const char *argv[])
{
pthread_t tid0;
int pipe1[2];
char buffer[200];
// Creating the pipe
pipe(pipe1);
// Creating the thread and passing the pipe as argument
pthread_create(&tid0, NULL, output, &pipe1);
// Input from user
scanf("%s", buffer);
// Writing to the pipe
write(pipe1[1], buffer, strlen(buffer));
return 0;
}
void* output(void* pipe1) {
char buffer[200];
// Reading the pipe and print the buffer
read(((int*)pipe1)[0], buffer, strlen(buffer));
printf("thread say: %s\n", buffer);
pthread_exit(NULL);
}
Why the read function doesn't block on the pipe's file descriptor ?
Maybe I should close the end of the pipe but since they share the same memory space, the error "bad file descriptor" is returned when I will call read or write.
Maybe you can guide me to other methods if pipe is really a bad solution (with an example it will be amazing ! :) )
Many thanks !
EDIT: SOLUTION
Many thank for your answer here is the code that have the expected behavior
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void* output(void* pipe1);
int main(int argc, const char *argv[])
{
pthread_t tid0;
int pipe1[2];
char buffer[200];
// Creating the pipe
pipe(pipe1);
// Creating the thread and passing the pipe as argument
pthread_create(&tid0, NULL, output, &pipe1);
// Input from user
scanf("%s", buffer);
// Writing to the pipe
if (write(pipe1[1], buffer, strlen(buffer)) < 0) {
perror("write");
exit(1);
}
// join so the main "wait" for the thread
pthread_join(tid0, NULL);
return 0;
}
void* output(void* pipe1) {
char buffer[200];
int nread;
// Reading the pipe and print the buffer
nread = read(((int*)pipe1)[0], buffer, sizeof buffer - 1);
if (nread < 0) {
fprintf(stderr, "ERROR\n");
perror("read");
exit(1);
}
buffer[nread] = '\0';
fprintf(stderr, "thread say: %s\n", buffer);
pthread_exit(NULL);
}
char buffer[200];
read(((int*)pipe1)[0], buffer, strlen(buffer));
You are calling strlen on an uninitialized buffer. This is allowed to crash your program. Instead, you got lucky, and all it did was tell read to read zero bytes, so read returned without doing anything.
What you actually want is
ssize_t nread = read(((int *)pipe1)[0], buffer, sizeof buffer - 1);
if (nread < 0) {
perror("read");
return 0;
}
buffer[nread] = '\0';
What read wants to be told is how much space you are giving it to read into, not the length of any string that may or may not already be in that space. That's sizeof buffer, minus one so we always have space to add a string terminator.
It's correct to use strlen when writing, because you only want to write the actual string, not any junk that might be beyond the end of the string; but then write doesn't write the string terminator to the pipe, so read doesn't read one, so you have to add it by hand. And, of course, always check for errors.
Also, keep in mind that the threads run simultaneously. Even after fixing this bug, the write may already have happened by the time the reader-thread calls read, and if it hasn't, it probably will happen very soon. If you want to observe the reader-thread actually blocking in read you need to delay before calling write.

Capture stdout to a string and output it back to stdout in C

C language is used. I have a function that writes to stdout.
I would like to capture that output, modify it a bit (replacing some strings). And than output it again to the stdout. So I want to start with:
char huge_string_buf[MASSIVE_SIZE];
freopen("NUL", "a", stdout); -OR- freopen("/dev/null", "a", stdout);
setbuf(stdout, huge_string_buffer);
/* modify huge_string_buffer */
The question is now, how do I output the huge_string_buffer back to the original stdout?
One idea is to mimic the functionality of the standard Unix utility tee, but to do so entirely within your program, without relying on outside redirection.
So I've written a simple function, mytee(), which seems to work. It uses shmget(), pipe(), fork(), and dup2():
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
static char *mytee(int size) {
int shmid = shmget(IPC_PRIVATE, size + 1, 0660 | IPC_CREAT);
int pipe_fds[2];
pipe(pipe_fds);
switch (fork()) {
case -1: // = error
perror("fork");
exit(EXIT_FAILURE);
case 0: { // = child
char *out = shmat(shmid, 0, 0), c;
int i = 0;
out[0] = 0;
dup2(pipe_fds[0], 0); // redirect pipe to child's stdin
setvbuf(stdout, 0, _IONBF, 0);
while (read(0, &c, 1) == 1 && i < size) {
printf("<%c>", c); // pass parent's stdout to real stdout,
out[i++] = c; // and then buffer in mycapture buffer
out[i] = 0; // (the extra <> are just for clarity)
}
_exit(EXIT_SUCCESS);
}
default: // = parent
dup2(pipe_fds[1], 1); // replace stdout with output to child
setvbuf(stdout, 0, _IONBF, 0);
return shmat(shmid, 0, 0); // return the child's capture buffer
}
}
My test program is:
int main(void) {
char *mycapture = mytee(100); // capture first 100 bytes
printf("Hello World"); // sample test string
sleep(1);
fprintf(stderr, "\nCaptured: <%s>\n", mycapture);
return 0;
}
The output is:
<H><e><l><l><o>< ><W><o><r><l><d>
Captured: <Hello World>
To use this in your application, in mytee() you'll need to replace the test statement printf("<%c>", c) with just write(1, &c, 1). And you may need to handle signals in the call to read. And after each of the two dup2()'s, you may want to add:
close(pipe_fds[0]);
close(pipe_fds[1]);
For a reference on this sort of stuff, see for example the excellent and short 27-year-old 220-page O'Reilly book Using C on the Unix System by Dave Curry.
The Unix way to do this is really to just write a little program that does the input processing you need, and then pipe the output of that other program to it on the command line.
If you insist on keeping it all in your C program, what I'd do instead is rewrite that function to have it send its output to a given char buffer (preferably returning the buffer's char *), so that it can be sent to stdout or processed as the client desires.
For example, the old way:
void usage () {
printf ("usage: frob sourcefile [-options]\n");
}
...and the new way:
char * usage(char * buffer) {
strcpy (buffer, "usage: frob sourcefile [-options]\n");
return buffer;
}
I really don't like tricky games with file descriptors. Can't you modify the function so that it returns its data some other way than by writing to stdout?
If you don't have access to the source code, and you can't do that, then I would suggest breaking out the code that writes to stdout into a small separate program, and run that as another process. It is easy and clean to redirect output from a process (maybe through a named pipe), and then you will have no problem with outputting to stdout from the process that receives the data.
Also, depending on the sort of editing you wish to do, you might be better off using a high-level language like Python to edit the data.
char huge_string_buf[MASSIVE_SIZE];
FILE stdout_ori=fdopen(stdout,"a");
freopen("NUL", "a", stdout); -OR- freopen("/dev/null", "a", stdout);
setbuf(stdout, huge_string_buffer);
/* modify huge_string_buffer */
//Write to stdout_fdopen
If you are on a unix system, you can use pipes (you don't need to use fork). I'll try to thoroughly comment the code below so it doesn't look like I am doing any "magic."
#include <stdio.h>
#include <unistd.h>
int main()
{
// Flush stdout first if you've previously printed something
fflush(stdout);
// Save stdout so it can be restored later
int temp_stdout;
temp_stdout = dup(fileno(stdout));
// Redirect stdout to a new pipe
int pipes[2];
pipe(pipes);
dup2(pipes[1], fileno(stdout));
// Do whatever here. stdout will be captured.
func_that_prints_something();
// Terminate captured output with a zero
write(pipes[1], "", 1);
// Restore stdout
fflush(stdout);
dup2(temp_stdout, fileno(stdout));
// Print the captured output
while (1)
{
char c;
read(pipes[0], &c, 1);
if (c == 0)
break;
putc(c, stdout);
}
// Alternatively, because the output is zero terminated, you could
// print using printf. You just need to make sure you read enough
// from the captured output
const int buffer_size = 1024;
char buffer[buffer_size];
read(pipes[0], buffer, buffer_size);
printf(buffer);
return 0;
}

Simple 30 char buffer PIPE prints non printable characters, no idea why

I used a simple fork() to simulate client / server then a very simple pipe to send / receive a char buffer of max 30 length, but it ends up printing non printable characters (small "?" and a box with 4 ones and zeroes) AFTER the desired word.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main () {
int pipefd[2];
int cpid;
char buf[31];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE)
}
cpid = fork();
if (cpid == -1) P
perror("cpid");
exit(EXIT_FAILURE);
}
if (cpid == 0) { // child reads from pipe
close (pipefd[1]); // close unused write end
read (pipefd[0], &buf, 30); // if I use 30 instead of strlen(buf) it prints Server transmit: Server receives. It does not wait for write. Makes no sense
printf ("Server receives: %s", buf);
close (pipefd[0])l
exit (EXIT_SUCCESS);
}
else { // parent writes to pipe
close (pipefd[0]); // closing unused read end;
char buf2[30];
printf("Server transmits: ");
scanf ("%s", buf2);
write (pipefd[1], buf2, strlen(buf2));
close(pipefd[1]);
wait(NULL);
exit(EXIT_SUCCESS);
}
return 0;
}
Also if I write more than one word it forgets about the second. In c++ I used getline (cin, string) but that's not an option here.
Also used read (pipefd[0], &buf, sizeof(buf));, now it prints in the correct order (no idea why strlen did not work) but I still get non printable characters at the end.
When you write (pipefd[1], buf2, strlen(buf2)); You neglect to put the '\0' in the stream. Change that to:
write (pipefd[1], buf2, strlen(buf2)+1);
And your string will now contain the null terminator, preventing the garbage at the end.
Using read (pipefd[0], &buf, strlen(buf)) did not work because buf is uninitialized. strlen is a simple function which looks for the terminating null at the end on the string, stopping when it's found. Unlike the length functions of C++ vectors, C functions have no way of accessing memory metadata. (sizeofis an operator)

Resources