0

I would like to read the continuous output stream of commands like tcpdump from within a c++ program.

#include <iostream>
#include <string>
#include <stdio.h>


std::string outputcmd(std::string cmd){
    std::string data;
    char singlelinedata[1024];
    FILE * streamdata;
    streamdata = popen(cmd.c_str(), "r");
    cmd.append(" 2>&1");
    std::cout << feof(streamdata);
    if(streamdata){
        while(!feof(streamdata)){
            // if(fgets(singlelinedata, 1024, streamdata)  != NULL ){
            //     data.append(singlelinedata);
            // }
            if(fread(singlelinedata, 1024, sizeof(singlelinedata)/sizeof(singlelinedata),streamdata)){
                data.append(singlelinedata);
            }
        }
        pclose(streamdata);
    }
    return data;
}

int main(){

    std::string outp;
    outp = outputcmd("sudo tcpdump -i any -v");
    std::cout << outp;
    return 0;
}

It is giving me only the first line output of tcpdump -i any -v

enter image description here

I dont want to Write to file and read again like following. Make tcpdump -i any -v -w myfile to get it written on file and read continously

abdul rashid
  • 611
  • 7
  • 20
  • Maybe you need to retry after reaching `EOF` as the stream needs time to buffer? – Avin Kavish Jun 02 '19 at 08:38
  • 1
    Unrelated to your problem, but `sizeof(singlelinedata)/sizeof(singlelinedata)` could be written more simply as `1`. – Miles Budnek Jun 02 '19 at 08:40
  • https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong – melpomene Jun 02 '19 at 08:45
  • `data.append(singlelinedata);` is invalid. `singlelinedata` is not a C string. You need to actually use `fread`'s return value. – melpomene Jun 02 '19 at 08:46
  • Your code doesn't give you any output as far as I can tell. It just sits there accumulating data, waiting for `tcpdump` to exit, which it never does. I don't understand what you're asking. – melpomene Jun 02 '19 at 08:50
  • @melpomene May be you are right, i have same thought, but do you know if thats true, What to do to collect the data from command output ? i am thinking to use pipe() and use pipefd[0] which is read, will that give stream of data? – abdul rashid Jun 02 '19 at 09:00
  • 1
    You already have a stream of data: `streamdata`. – melpomene Jun 02 '19 at 09:00
  • 1
    `cmd.append(" 2>&1");` after `popen` does nothing, the command is already running without it –  Jun 02 '19 at 09:02
  • @jakub_d true, it was part of experiments. – abdul rashid Jun 02 '19 at 09:03
  • Your program will only print something once tcpdump has exited, try a `killall tcpdump`, you should see some results. –  Jun 02 '19 at 09:12
  • @jakub_d I am getting only the First line, and Last summary lines (Last Summary lines gets printed which i press CTRL + C SIGNINT). [https://imgur.com/a/02R4hKU](https://imgur.com/a/02R4hKU) – abdul rashid Jun 02 '19 at 10:39

1 Answers1

1

Try something like this:

#include <iostream>
#include <string>
#include <stdio.h>


void outputcmd(std::string cmd){
    char buffer[1024];
    FILE * pipef = popen(cmd.c_str(), "r");
    if(pipef){
        while(!feof(pipef)){
            int res;
            if((res = fread(buffer, /*note order here*/ 1, sizeof(buffer), pipef)) > 0){
                std::string block(buffer, res);
                std::cout << "read from pipe: [" << block << "] size " << block.size() << std::endl;
            }
        }
        pclose(pipef);
    }
}

int main(){
    outputcmd("sudo tcpdump -i any -v");
    return 0;
}

One note from the manpage

Note that output popen() streams are block buffered by default.

so you will get the results with some delay and in big chunks, not line by line.

  • @jakub_b do you know when the popen() streams block is revoked during the execution?, (is it only when `tcpdump` sends close signal? ) or i can trigger forcefully to stop buffering and read buffer after every X seconds – abdul rashid Jun 11 '19 at 08:52
  • @abdulrashid the note about block buffering means that you will get the data in blocks of 4096(?) bytes –  Jun 11 '19 at 09:10
  • @abdulrashid if you want a line-buffered analog of popen, you may need to make your own using fork&exec as seen here https://stackoverflow.com/questions/6743771/popen-alternative –  Jun 11 '19 at 09:12
  • Thanks @jakub_d it worked. Also I found string has a constructor `std::string abc("My String")` from your reply – abdul rashid Jul 07 '19 at 06:36