0

Basically what I want to do is to instantiate a program in a Linux shell and get the outputs that it emits to stdout into a string or list of strings (per line, depending). The program I want to run in my c++ application is just a ps aux.

  • All the "solutions" I've found didn't completely solved my problem. Most of them fell under the error described in this answer. Basically popen fails and doesn't return the complete output from the shell execution. Let's call this "error 1" for the sake of this question.

What I've tried so far:

1) Using boost, I tried this following their documentation:

#include <boost/process.hpp>
namespace bp = boost::process;
bool is_process_running(std::string p_name){
    string cmd = "ps aux";
    bp::ipstream out;
    std::string line;
    bp::child c(cmd, bp::std_out > out);

    // the while is supposed to read each line of the output
    // but the execution doesn't even enter the while
    while(c.running() && std::getline(out, line) && !line.empty())
    {
        if(line.find(p_name) != std::string::npos)
        {
            return true;
        }
    }
    c.wait();
    return false;
}

2) I also tried:

bool is_process_running(std::string p_name){
    string cmd = "ps aux";
    std::array<char, 256> buffer;
    std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) throw std::runtime_error("popen() failed!");
    while (!feof(pipe.get())) {
        if (fgets(buffer.data(), 256, pipe.get()) != nullptr)
        {
            // here the line comes incomplete
            std::string line(buffer.data());
            if(line.find(p_name) != std::string::npos)
            {
                return true;
            }

        }
    }
    return false;
}

But this last one also fell into "error 1".

3) This code snippet fell into the "error 1"
4) This one also fell into "error 1"
I don't this it is necessary to put the codes for 3) and 4) because it is literally what is in these answers, I didn't change anything, but if you guys need I can edit my question. So my question is just this, how to get the command output in a way that will work? Thanks in advance.

Edit: I've tried with the code snipped provided:

bool is_process_running(std::string p_name)
    FILE* p = popen(cmd.c_str(), "r");
    if (!p) { cerr << "oops"; return 2; }
    char line[500];
    while (fgets(line, 500, p))
    {
        std::string linha(line);
        if(linha.find(p_name) != std::string::npos)
            return true;
    }
    fclose(p);
    return false;
}

In this case, here is an example of the truncated output from popen/fgets:

[fabio ~]$ ps aux | grep 391
root       391  0.0  0.1  48580 12864 ?        Ss   Sep03   0:06 /usr/lib/systemd/systemd-journald
fabio 15435  0.0  0.0 112548   960 pts/2    S+   15:40   0:00 grep --color=auto 391

The line for the process 391 is as this one but at runtime it only returns me "root 391 0.0 0.1 48580 12856 ? Ss Sep03 0:06 /usr/lib/system\n"

Fnr
  • 2,096
  • 7
  • 41
  • 76
  • 1
    In a nutshell: Launch your process (plain `fork`/`exec*`). Connect a `pipe` from the `stdout` of the new process to your process. `read` data from the pipe when available and store it in some container/buffer in your process. When the child process terminates, parse the data in your container (the output from the child). That's *one*, fairly simple, way of doing it. – Jesper Juhl Sep 05 '18 at 18:13
  • 1
    No. I'm not going to write up an example for you that you should be able to easily find online yourself based on the keywords given - this is fairly basic stuff with lots of material covering it online, if you do a bit of research. SO is not a free code writing service (nor am I). – Jesper Juhl Sep 05 '18 at 18:29
  • 2
    http://coliru.stacked-crooked.com/a/0a055ba0ca0d8043 what truncated output do you mean? Please show your code, its expected output, and its actual (truncated) output. – n. m. could be an AI Sep 05 '18 at 18:30
  • 1
    For code snippet 1) you state `"execution doesn't even enter the while"`. Why not? Have you run it under a debugger? Note that the code shown in 1) is not quite the same as for your [previous question](https://stackoverflow.com/questions/52187867/how-to-run-a-process-and-get-its-output-using-c-libboost) -- in that question you also had shell redirection `2>&1` which could cause problems. The code shown in 1) here works exactly as expected for me. – G.M. Sep 05 '18 at 18:31
  • @G.M. yes I've debugged it and that's how I acknowledge that it is not entering the while loop, now if you ask me why, I don't know. Indeed I removed the `2>&1` to see if the problem stops. You are not the first one who told me that my examples works for you, the only thing I may conclude this is that either my system or my libs are broken or too outdated because this all makes no sense – Fnr Sep 05 '18 at 18:33
  • You should explain *why* you run `ps aux` and what processes do you really care about. For example, if you are searching some process, you should explain that (and what are you searching for). I see no gain in running `ps aux` programmatically – Basile Starynkevitch Sep 06 '18 at 04:50
  • @BasileStarynkevitch I don't think this is the scope of the question but since you insist: I need to know if some processes are running given both its name and arguments. I can't know this using `pidof` or `pregp` because these don't consider the arguments passed. For the same reason I can't grep inside `proc` folder, running `find /proc | grep my_process` and returned zero results – Fnr Sep 06 '18 at 10:16
  • But why can't you code (without creating any process) some functions accessing `/proc/` directly (as mentioned in my answer)? – Basile Starynkevitch Sep 06 '18 at 10:23
  • 1
    @Fabiotk: next time you ask some question, please *motivate it* and *give some context* in the question itself, not in some comments for it. Do you understand that the initial form of your question was some [XY problem](http://xyproblem.info) ? – Basile Starynkevitch Sep 06 '18 at 12:40

1 Answers1

2

Use the relevant system calls directly: pipe(2), fork(2), dup2(2), close(2), execve(2), waitpid(2). Read some good Linux programming book (perhaps the old ALP which has several chapters related to your question). Look for inspiration into the source code of other free software programs dealing with pipes (e.g. of some simple shell like sash) and/or into your libc (or musl-libc, it has very readable code) for the code of popen.

We don't have the space and time to explain in details, but you'll find many resources on the Web, and ALP is freely downloadable; of course execve is the last system call that usually runs in the child process, since it is re-initializing the virtual address space.

To read from the pipe without limiting the line length. Either use read(2) manually (and care about the buffering), or consider getline(3) (see also fdopen(3))

You might be interested by frameworks like POCO or Qt.

You probably want to avoid popen(3) (and you don't need something using /bin/sh, unless your command needs to be expanded for globbing; see glob(7)).

Be aware of signal(7) and signal-safety(7) (and perhaps poll(2) ...)


The program I want to run in my c++ application is just a ps aux

Then you probably should not run any external process, but instead access /proc/ directly (see proc(5); you'll need opendir(3), readdir(3) etc...) or perhaps thru some libproc. Of course ps aux is accessing /proc/ itself (so you don't gain anything except some simplicity by running ps, and you lose some performance and some robustness by using it). See also this, this, this, this, this answers.

In a comment you mention

I need to know if some processes are running given both its name and arguments.

That is a good case for accessing /proc/ programmatically: loop on /proc/ directory with opendir, looping readdir, enddir. For each entry given by readdir check that it has a numerical name (starting with a digit) otherwise skip it. With that numerical name (for example 1234) construct a path like /proc/1234/cmdline and open it then parse its content (it has separating NUL bytes). This is probably not harder than starting a ps aux process, getting and parsing its output, and is certainly more efficient. (To understand details, run od -cx /proc/self/cmdline then od -cx /proc/$(pidof systemd-journald)/cmdline and understand its output).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • The problem with scanning the /proc directory is that it changes while you read it. – stark Sep 05 '18 at 19:20
  • the examples are not clear in how to write into the pipe the outputs from `execve` since execve will halt and only return if an error happens – Fnr Sep 05 '18 at 19:33
  • But `ps aux` is scanning `/proc` and it is also changing while `ps` runs – Basile Starynkevitch Sep 06 '18 at 04:08
  • @Fabiotk: take several hours or days to read more. That matter is very well explained. – Basile Starynkevitch Sep 06 '18 at 05:22
  • I see it, the fact that it has separating null bytes kind makes things harder since passing to `strcmp`, `strstr` only compares until the end of the string (ie, the null byte). But thanks for the help, now the problem is easy to solve – Fnr Sep 06 '18 at 12:31
  • No, that should make things *easier*. You just fill a buffer with all the content of `/proc/1234/cmdline` (e.g. looping till `fread` fails), and you can use pointers *inside* that buffer (and pass such internal pointers to `strcmp`) – Basile Starynkevitch Sep 06 '18 at 12:33