PDA

View Full Version : Read output from a program live



B-Con
July 10th, 2007, 11:53 PM
How could I stream/pipe/whatever live output from one program to another?

I want to write a C program that processes data from tcpdump and displays stats live. Ex, you start the program then the program starts tcpdump and reads data from its output in chunks, processes some stats, and then displays those stats.

(I read this thread (http://ubuntuforums.org/showthread.php?t=471040) but it wasn't much help.)

I assume I could do this with a pipe? What would I do, "$ tcpdump | myprog" and then read data from stdin with fread(), or something?

slavik
July 10th, 2007, 11:57 PM
then simply read from stdin ... you can use read or even fscanf or fgets or even gets, ie: any function that can be used to get input from a person :)

Mr. C.
July 11th, 2007, 03:07 AM
Sure, you can do this.

Tcpdump outputs packets in a raw binary format (unless you tell it otherwise). In that case, you'll want to perform read(2) operations rather than using the line buffered fscanf, etc. Read the man pages for read(2) and select(2).

Be sure to set the -U -w - options in tcpdump, so that unbuffered output is written to STDOUT.

Out of curiosity, what types of "stats" are you looking for ? Do you know about SNMP and the standard network stats available ?

MrC

Wybiral
July 11th, 2007, 06:52 PM
Look up: pipe, fork, dup2, and exec.


This opens bc (the little calculator program) and sends it some data via stdin (as if someone typed it in). You can also pipe the stdout of the program back into your program.

Pipes are incredibly useful for program-to-program communication... Unfortunately they are *nix specific.



#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
pid_t pid;
int ret, my_pipe[2];

if(pipe(my_pipe))
{
fprintf(stderr,"Pipe error!\n");
exit(1);
}

pid=fork();

if(pid == -1)
{
printf(stderr,"Fork error. Exiting.\n");
exit(1);
}
else if(pid)
{ // Parent
dup2(my_pipe[1], 1); // Set stdout to my_pipe[1]
close(my_pipe[0]);
setvbuf(stdout, (char*)NULL, _IONBF, 0);
printf("(10 + 2) / 3\n");
printf("quit\n");
wait(&ret);
}
else
{ // Child
dup2(my_pipe[0], 0); // Set stdin to my_pipe[0]
close(my_pipe[1]);
if(execl("/usr/bin/bc", "bc", NULL) == -1)
{
fprintf(stderr,"execl Error!");
exit(1);
}
}
return 0;
}

slavik
July 12th, 2007, 04:47 AM
wyb, may I suggest doing a switch on fork? (saw it in these forums, been doing it since)


switch(fork()) {
case -1: error; break;
case 0: child; break;
default: parent; break;
}

B-Con
July 12th, 2007, 06:00 AM
@ MrC: I want to monitor all ARP packets entering and exiting the computer and parse them.

@ Wybiral: Thanks, that's what I'm looking for. But I want to do the opposite of what you did. I want the parent to recieve the output from the child, and the output to be generated by the execl() command. Ex:


#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
pid_t pid;
int ret, my_pipe[2];
char str[256];

if(pipe(my_pipe))
{
fprintf(stderr,"Pipe error!\n");
exit(1);
}

pid=fork();

if(pid == -1)
{
printf(stderr,"Fork error. Exiting.\n");
exit(1);
}
else if(pid)
{ // Parent
dup2(my_pipe[0], 0); // Set stdout to my_pipe[1]
close(my_pipe[1]);
gets(str);
puts(str);

wait(&ret);
}
else
{ // Child
dup2(my_pipe[1], 1); // Set stdin to my_pipe[0]
close(my_pipe[0]);
setvbuf(stdout, (char*)NULL, _IONBF, 0);
if (execl("/bin/echo 'hi'", "echo", NULL) == -1)
{
fprintf(stderr,"execl Error!");
exit(1);
}
//puts("hi");
}
return 0;
}
If I comment out the child process execl() call and replace it with the puts() command, it works fine and the output gets piped to the parent process. But if I make the execl() call then it fails.

What's messing up the the execl() call?

slavik
July 12th, 2007, 06:36 AM
exec() switches the current process with a new one. exec() doesn't return ^^

Wybiral
July 12th, 2007, 06:46 AM
It's because you don't pass arguments to execl like that...

The arguments go in order as parameters terminated by a NULL.


Using slavik's suggestion on the switch case...



#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
int ret, my_pipe[2];

if(pipe(my_pipe))
{
fprintf(stderr,"Pipe error!\n");
exit(1);
}

switch(fork())
{
case -1:
fprintf(stderr,"Fork error!\n");
exit(1);
break;

case 0:
dup2(my_pipe[1], 1);
close(my_pipe[0]);
if(execl("/bin/echo", "echo", "Testing...", NULL) == -1)
{
fprintf(stderr,"execl Error!");
exit(1);
}
break;

default:
dup2(my_pipe[0], 0);
close(my_pipe[1]);
char x[1024];
scanf("%s", x);
printf("Output: # %s #\n", x);
wait(&ret);
break;
}
return 0;
}


Before someone tried to point out the obvious, yes, I'm aware that this will overflow with input larger then 1023 bytes (it was just an example).

Mr. C.
July 12th, 2007, 09:15 AM
@ MrC: I want to monitor all ARP packets entering and exiting the computer and parse them.

Does arpwatch not meet your needs?
http://www-nrg.ee.lbl.gov/

MrC

the_unforgiven
July 12th, 2007, 10:40 AM
Another option would be to directly use the pcap (Packet CAPture) API from libpcap - tcpdump internally uses the same. So, you won't need to bother about managing processes :)
This will give you much more control over how you set up the capture filters and what you do with the captured data.
Here is a tutorial on how to use libpcap:
http://www.tcpdump.org/pcap.htm

And libpcap is available from tcpdump site itself - http://www.tcpdump.org

HTH ;)

B-Con
July 14th, 2007, 02:00 AM
Oh, duh, thanks Wybiral.

@ MrC: That only monitors the local ARP tables. I want to build a tool that will provide a rough IP->MAC map of a network based on captured ARP packets. Mainly for when you want to build a map of a network when you're behind a switch.

@ unforgiven: I briefly thought about doing that but figured piping it from tcpdump would be the simplest. But I still may give it a try. Thanks for the link.

cylon359
July 15th, 2007, 05:19 PM
Use the popen function....