libgetargv(3) | Library Functions Manual | libgetargv(3) |
libgetargv
library
“libgetargv” — library
functions for getting and printing other processes' args
library “libgetargv”
#include
<libgetargv.h>
struct GetArgvOptions { int skip; pid_t pid; bool
nuls; };
struct ArgvArgcResult { char* buffer; char** argv; uint argc;
};
struct ArgvResult { char* buffer; char* start_pointer; char*
end_pointer; };
bool
get_argv_of_pid
(const
struct GetArgvOptions* options,
struct ArgvResult*
result);
bool
get_argv_and_argc_of_pid
(pid_t
pid, struct
ArgvArgcResult* result);
bool
print_argv_of_pid
(char*
start_pointer, char*
end_pointer);
void
free_ArgvResult
(struct
ArgvResult* result);
void
free_ArgvArgcResult
(struct
ArgvArgcResult* result);
The first and only library to provide a correct
interface to the KERN_PROCARGS2
sysctl. Every other program/library (including
Apple's ps(1)) incorrectly
indexes the arguments while parsing, in the case of empty leading arguments;
resulting in the inclusion of env vars in addition to arguments. This is
because while it is conventional to put the executable name in
argv[0], it is not enforced by xnu
(the kernel), however everyone else relies on it being true.
get_argv_of_pid
()
takes as arguments: a pointer to a
struct GetArgvOptions which controls:
whether the NUL
s which separate the arguments
in argv are replaced with
spaces (.nuls = true), how many leading arguments to
skip (.skip = #), and the
pid to target (.pid = #);
and a pointer to a struct
ArgvResult. The results of the function are populated in the
result struct:
(.start_pointer) is a pointer to the start of the argv
(skipped ahead by as many args as requested), (.end_pointer) is a pointer to
the end of the argv, (.buffer) is the entire raw
buffer which it is the caller's responsibility to
free(3) (you can use
free_ArgvResult
() to ensure you use
the matching free(3)
function to this library's
malloc(3)
function). The return value of the function is a
boolean indicating success (true) or failure (false); in case of failure
errno(2) is
set, and the buffer pointer in result, is set
to NULL
. In
the case where there are no arguments to return
(.argc == 0) or (.skip == argc) then .start_pointer, .end_pointer, and
.buffer are set to NULL
. Use
get_argv_and_argc_of_pid
() if you
want to inspect the results and not just print them.
get_argv_and_argc_of_pid
()
is an alternative to
get_argv_of_pid
() which returns
the argc and
argv of a
pid, with argv
formatted as a char**, just
like if it was passed to
main
().
It takes as arguments a pid_t
pid, and a pointer to an
ArgvArgcResult struct which will be
populated with the argc
and argv of
the pid, and the backing buffer from the
sysctl. It is the caller's responsibility to
free(3) the
backing buffer (.buffer) and the (.argv) array (you can use
free_ArgvArgcResult
() to ensure you
use the matching free(3)
function to this library's
malloc(3)
function), except when argc is 0 in which case the
buffer and argv array are set to NULL
.
The return value of the function is a
bool indicating success (true)
or failure (false); in case of failure
errno(2) is
set, and the pointers in result
are set to NULL
.
print_argv_of_pid
()
is a convenience function that will print to stdout every
byte from start_pointer
to end_pointer,
including NUL
s, in a
performant way. Its return value is a bool
indicating success (true) or failure (false); on
failure errno(2) is set. If
start_pointer or
end_pointer are
NULL pointers
(note: not NUL
bytes
), or both point to the same address, or
end_pointer is greater than
start_pointer then nothing is
printed.
free_ArgvResult
()
is a safe way to
free(3) the
pointer in a struct ArgvResult
using the matching
free(3)
function to the
malloc(3)
that allocated the buffer it holds a pointer to. This
function does not return a value.
free_ArgvArgcResult
()
is a safe way to
free(3) the
pointers in a struct ArgvArgcResult
using the matching
free(3)
function to the
malloc(3)
that allocated the buffers it holds pointers to. This
function does not return a value.
In addition to the errors documented for the
sysctl(3) and
write(2) system calls, the
functions in libgetargv
(except the free_Argv*Result
functions) may return false to indicate failure, and set
errno(2) as described
below:
EINVAL
]ESRCH
]EPERM
]ENAMETOOLONG
]ERANGE
]I believe
libgetargv
is thread-safe under the
following conditions:
errno(2) is
thread-local,
malloc
()
is thread safe,
sysctl
()
is thread safe, as is copying out of the buffer it
populates; these conditions are met on all macOS versions I've tested. Note:
the arguments passed into the functions in
libgetargv
must not be modified (or
freed) by other threads until after the functions return. Whether you use
thread-local storage, or mutexes, or some other synchronization primitive,
or separate blocks of shared memory, or some other approach, is up to you.
Also note that: calling print_argv_of_pid
()
from multiple threads may interleave the output, so you may
want to use
flockfile
(stdout)
to prevent this.
I've spent a lot of time optimizing the performance of this library. All functions use the most efficient algorithm possible, and the fewest memory allocations possible. I've also benchmarked the functions I use from the C std library in order to choose the most optimized versions available. I regularly benchmark my code using hyperfine to test for performance regressions.
The
get_argv_and_argc_of_pid
()
function may be slower than
get_argv_of_pid
() as it must
perform an additional malloc
(), though it has
fewer conditionals per loop so if there are few arguments then it could wind
up being faster. Both functions have the same time complexity:
O
(n)
where n is
the length of the arguments to pid in bytes.
The following programs show minimal use of the entire library API.
See the file man3/libtest1.c.3getargv.
See the file man3/libtest2.c.3getargv.
getargv(1), ps(1), errno(2), sysctl(3), stdbool.h(0p), free(3)
The libgetargv
library conforms to the xnu
kernel's KERN_PROCARGS2
sysctl.
Function | Description |
get_argv_of_pid |
get the argv of the specified pid as a buffer |
get_argv_and_argc_of_pid |
get the argc and argv of the specified pid in standard format |
print_argv_of_pid |
print a contiguous stretch of memory to stdout, including any NULs |
free_ArgvResult |
free the pointer in an ArgvResult struct using the correct free function |
free_ArgvArgcResult |
free the pointers in an ArgvArgcResult struct using the correct free function |
This library does not necessarily interact well
with other libraries due to unilaterally defining
bool if
<stdbool.h>
isn't available.
This is only a problem when compiling C in std < ISO/IEC
9899:1999 (“ISO C99”). If you do experience a
conflict, you can remove the definition of bool from the header, so long as
you only include the header after you define bool in your program.
print_argv_of_pid
()
does not facilitate printing to anything other
than stdout.get_argv_of_pid
()
and
get_argv_and_argc_of_pid
()
allocate memory, and do not facilitate alternate
allocators.get_argv_of_pid
()
and
get_argv_and_argc_of_pid
() assume
that you have not compiled your own xnu kernel with a custom
PID_MAX
; if you have, you will need to compile
libgetargv
with your
custom PID_MAX
set
thus:
$ make PID_MAX=__YOUR_CUSTOM_PID_MAX_HERE__ dylib
Also, it is currently a hard requirement
that PID_MAX
<
ARG_MAX
.
January 5, 2023 | macOS 14.6 |