3

I have a very simple program :

void main()
{
    fgets(buf,133,stdin);
    printf( buf);
    system("/bin/dash");
}

When I launch the program, it all works fine, I can type whatever I want and then I have a shell. However if I do

$ echo 'blabla' | ./test

in order to automatically fill the buf (without typing anyting with the keyboard), the shell is executed, though, /bin/ls works fine for instance. (apparently the display commands work fine)

If I remove the fgets line, and I execute the same command, it works but takes the 'blabla' as an argument for /bin/dash. However adding a nul character or a return '\xd' in order to simulate the return of my keyboard in the fgets doesn't work

I'd like to understand what happens when I use the '|' symbol within my c++ program. I thought it was the solution to automatically fill scanfs and fgets without any human interaction, am I doing it wrong or is there an other solution ?

Thank you.

Debian, C++ g++

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
x4rkz
  • 513
  • 4
  • 19
  • 2
    In standard C++ main always returns an `int` – NathanOliver Sep 01 '15 at 16:26
  • 1
    http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem ? – Karoly Horvath Sep 01 '15 at 16:37
  • [`void main()`](http://stackoverflow.com/q/204476/995714) is wrong – phuclv Sep 01 '15 at 16:52
  • Your comments are quiet useless – x4rkz Sep 01 '15 at 18:53
  • The comment about the XY problem is actually very useful. I had a hard time answering because I wasn't sure what your goal was, so I didn't know what the kind of "solution" you were referring to in your question. Instead I just answered your first question about what exactly happens when the pipe is used. It would be better to start your question by saying exactly what you want to accomplish, and *then* describe what you have attempted and the problems with your attempts. – David Grayson Sep 02 '15 at 05:54
  • I was more referig toi people talking about void main – x4rkz Sep 02 '15 at 07:04
  • Maybe I didnt formulate well but I really had two questions, why didnt it work and if possible how to make it work or what other solution can you propose. Thanks for your advice I'll try to formulate it this way next time. – x4rkz Sep 02 '15 at 07:06

2 Answers2

5

When you execute echo 'blabla' | ./test in your shell, the shell will start an echo process and connect its standard output pipe to the standard input pipe of ./test. This has nothing to do with C++: these pipes are a feature of most operating systems and pretty much every process has them.

When your program executes system, you are creating a new process (or processes) that are connected to the same standard input pipe. So if there is some data in that pipe (from the echo command) that has not been read by test, it will be available to be read by the processes you started with system.

Using echo and a pipe is an OK way to provide input to fgets and scanf. An alternative way to pass data to your program would be to use environment variables or command-line arguments, but you would need to modify your code to check for those things.

David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • I knew that pipe was not c++ related, I only wanted to automacally fill scanf for any language but I wanted to give a concrete example. Thank you for your explanations, however the goal is to automatically fill scanf of programs not written by me so using the environment and modifying the code is not an option. – x4rkz Sep 01 '15 at 18:58
2

If I understand you correctly, you want to provide input both to your fgets and to your shell, but instead find that the shell doesn't receive any input when you pipe.

This is because libc will buffer input data for fgets.

Instead of reading the 7 bytes in blabla\n and passing the rest to the shell, it reads up to 4096 bytes (system dependent) and uses the remaining 4089 bytes for future fgets/f* calls on stdin. This data will be stored internally in your program and will not be available to other processes that read from the underlying stream, like the invoked shell.

When you run interactively and type on a keyboard, there's just 7 bytes available when you hit enter, so the buffer is only filled with 7 bytes. The rest of the data you type is therefore available to the shell. You could have simulated the same effect in your buggy program with strategically placed delays in the input:

{ echo "for fgets"; sleep 1; echo "ls"; } | ./foo

You can circumvent the issue by setting the buffer size to 1 byte, so that fgets never reads more than necessary:

#include <stdio.h>

char input_buffer[1];

void main(int argc, char** argv)
{
    char buf[133];
    setvbuf(stdin, input_buffer, _IOFBF, 1);
    fgets(buf,133,stdin);
    printf( buf);
    system("/bin/dash");
}

Now you can run it with a pipe and no delays:

$ echo -e "for fgets\nls"
for fgets
ls
$ gcc foo.c -o foo
$ echo -e "for fgets\nls" | ./foo
for fgets
Desktop    Downloads  foo.c  Pictures  Steam      Videos
Documents  foo        Music  Public    Templates  win
that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Thank you very much ! The thing is, I would like to automatically fulfil programs not written by me, so I can't use setvbuf, however thank you for teaching me how fgets works. I tried to sleep like you said and it worked. – x4rkz Sep 01 '15 at 18:55
  • Yay! However, if you're trying to automate input to a binary you can't modify I would recommend you post a new question saying you want to automate input to a binary you can't modify. You'll get way more elegant and robust answers that way. – that other guy Sep 01 '15 at 19:40