libgetargv++
is a library that allows you to get the arguments that were passed to another running process on macOS. It is intended to provide roughly the same functionality as reading from /proc/<pid>/cmdline
on Linux. On macOS this is done by parsing the output of the KERN_PROCARGS2
sysctl, which is very often implemented incorrectly, due to the overlooked possibility of leading empty arguments passed to the target process.
This is a library providing a more idiomatic C++ API wrapping the C API provided by libgetargv.
libgetargv++
can only see processes running as the same user by default, so be sure your process runs as the desired user (setuid
, launchd.plist
, sudo
) or can elevate privileges; n.b. elevating privileges safely is extremely complicated, and will be a target of privilege escalation attacks on macOS so be extremely careful if you go this route, better to defer to the user to elevate privileges for you as needed.
You can download a source bundle, or an installer package from GitHub.
The simplest way to install this lib is via Homebrew, just run brew tap getargv/tap
and then brew install libgetargv++
. If you don't use Homebrew, then the next easiest way to install this lib is if you downloaded the installer package, which you can simply double click to be guided through the installation via a wizard. If you want to have absolute control over the installation you can unpack the pre-built library downloaded from GitHub and put it somewhere your compiler will pick it up, such as /usr/local/lib
and put the headers somewhere like /usr/local/include/libgetargv++
.
Be sure that your compiler can find this lib by checking the library and header search paths:
echo | clang++ -c -v -x c++ - 2>&1 | sed -Ee '/search starts here/,/End of search list/!d;/End of search list/q'
Important
On Apple Silicon (ARM) Macs, Homebrew installs native packages in /opt
, and puts Intel libraries in /usr/local
, so your compiler will only automatically pick up Intel libraries, which is almost certainly not what you want. You need to explicitly exclude /usr/local
from your search paths in this case and add /opt/
, the CPATH
environment variable might be helpful to you in doing this.
macOS is required as this is a macOS specific sysctl
, even BSD does not implement it. Your system must support sysctl
and KERN_PROCARGS2
, which probably means macOS 10.3 or later, though I haven't tested versions older than 10.7. You'll also need a non-ancient Clang++ (C++11 is required, C++20 is the default standard on Clang++ versions > 13, otherwise C++17) you can override the C++ std by setting CXXFLAGS="--std=c++11 -O3 -Iinclude"
.
To make libgetargv++
:
- Install
libgetargv
to your system (see below). - Clone this repo and run
make
.
To make libgetargv
Clone the repo and run make dylib
.
I've built libgetargv
on macOS 10.7-14, using only the CLT package, not the full Xcode install. If you need to override variables, do so inside the make
command, eg: make EXTRA_CPPFLAGS=-DMACRO EXTRA_CFLAGS=-std=c17 dylib
. If you are trying to build on a version of macOS earlier than 10.7, let me know how it goes.
Run make -C test
.
I've tested libgetargv++ on macOS 10.7-14, and run CI against all available GitHub hosted macOS runners, with plans to standup a CI cluster of VMs once I acquire appropriate hardware.
Do you just want to print the arguments to stdout
or look at the bytes of the arguments? Then you probably want the Argv
struct, if you want to look at or parse the arguments, then you probably want the ArgvArgc
struct. If you need to pass along the arguments to other functions that expect standard C++ types, then there are functions to give you those, however they are a bit less efficient as they involve additional copies.
If you want to get a C++ type then Argv::as_string()
or ArgvArgc::as_string_array()
are your friends, if you want just the bytes, then Argv()
or Argv::as_bytes()
are for you, and if you want to look at the args individually then ArgvArgc()
or ArgvArgc::as_array()
are what you want.
Once you have a struct, you can iterate over its contents in the usual way, for example:
auto args = ArgvArgc(getpid());
for (auto arg : args) {
std::cout << arg << "\n";
}
auto bytes = Argv(getpid());
for (auto byte : bytes) {
std::cout << byte;
}
This library attempts to provide guardrails where possible to help avoid undefined behaviour, but C++ is not very well suited to this task, so care must still be taken.