Search code examples
pythonlinuxpsutilmuslcmdline-args

How can I extract only executable path and args from cmdline in linux?


I am writing a python application, which uses Popen to start other exectutables. I then use Psutil to collect information about them.

One of the things I need is the exact CMD I used to start the application. For example:

>>> p = psutil.Popen(
            ['/path/to/binary', 'arg1', 'arg2'],
            env=env,
            cwd=cwd,
            start_new_session=True,
        )

I then need psutil to show me ['/pat/to/binary', 'arg1', 'arg2']. Note that I need to be able to get this line across main app restarts, so I cannot just save the command I used in a variable in my app. Only other option to do that is to write it to file, which is what I would like to avoid.

However, it looks like the kernel is changing the proctitle because:

>>> p.cmdline()
['ld-linux-x86-64.so.2', '--argv0', '/path/to/binary', '--preload', '/lib/libgcompat.so.0 ', '--', '/path/to/binary', 'arg1', 'arg2']

If I manually read /proc/339/cmdline I also get the same thing, so it is not a problem with psutil.

And it looks like ld-linux-x86-64.so.2 is not int PATH so I cannot just execute this CMD to run the executable.

I can obviously parse this and just take whatever comes after -- but that would work only in this particular case and will break when a process cmdline is normal (without the ld-linux-x86-64.so.2 ... stuff) and contains -- in its arguments list.

Is there a way to get just the command and args I used originally without having to save the command I used to a file?


Solution

  • Looks like I would have to answer my own question. I wished there was a better way to do this but looks like there isn't.

    The workaround I ended up doing assumes that the cmdline list ends with the arguments of the executable, preceded by the absolute executable path (which I know). So I use this to extract it from the rest:

    >>> cmdline = p.cmdline()
    >>> cmdline
    ['ld-linux-x86-64.so.2', '--argv0', '/path/to/binary', '--preload', '/lib/libgcompat.so.0 ', '--', '/path/to/binary', 'arg1', 'arg2']
    >>> cmdline.reverse()
    >>> first_arg_index = len(cmdline) - cmdline.index("/path/to/binary")
    >>> cmdline.reverse()
    >>> cmdline[first_arg_index:]
    ['arg1', 'arg2']
    

    This should also work for normal cmdlines i.e. /path/to/binary arg1 arg2, but will break if the dynamic linker changes the way it starts the executable.