The files in /proc are not ordinary files. For most of them, stat()
et al. return .st_size == 0
.
In particular, /proc/PID/exe
is not really a symlink or a hardlink, but a special pseudofile, which behaves mostly like a symlink.
(If you need to, you can detect procfs files checking the .st_dev
field. Compare to .st_dev
obtained from lstat("/proc/self/exe",..)
, for example.)
To obtain the path to a specific execubtable based on its PID, I recommend an approach relying on the return value of readlink()
instead:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
/* Creative Commons CC0: Public Domain dedication
* (In jurisdictions without public domain, this example program
* is licensed under the Creative Commons CC0 license.)
*
* To the extent possible under law, Nominal Animal has waived all
* copyright and related or neighboring rights to this example program.
*
* In other words, you are free to use it in any way you wish,
* but if it breaks something, you get to keep all the pieces.
*/
/** exe_of() - Obtain the executable path a process is running
* @pid: Process ID
* @sizeptr: If specified, the allocated size is saved here
* @lenptr: If specified, the path length is saved here
* Returns the dynamically allocated pointer to the path,
* or NULL with errno set if an error occurs.
*/
char *exe_of(const pid_t pid, size_t *const sizeptr, size_t *const lenptr)
{
char *exe_path = NULL;
size_t exe_size = 1024;
ssize_t exe_used;
char path_buf[64];
int path_len;
path_len = snprintf(path_buf, sizeof path_buf, "/proc/%ld/exe", (long)pid);
if (path_len < 1 || path_len >= sizeof path_buf) {
errno = ENOMEM;
return NULL;
}
while (1) {
exe_path = malloc(exe_size);
if (!exe_path) {
errno = ENOMEM;
return NULL;
}
exe_used = readlink(path_buf, exe_path, exe_size - 1);
if (exe_used == (ssize_t)-1)
return NULL;
if (exe_used < (ssize_t)1) {
/* Race condition? */
errno = ENOENT;
return NULL;
}
if (exe_used < (ssize_t)(exe_size - 1))
break;
free(exe_path);
exe_size += 1024;
}
/* Try reallocating the exe_path to minimum size.
* This is optional, and can even fail without
* any bad effects. */
{
char *temp;
temp = realloc(exe_path, exe_used + 1);
if (temp) {
exe_path = temp;
exe_size = exe_used + 1;
}
}
if (sizeptr)
*sizeptr = exe_size;
if (lenptr)
*lenptr = exe_used;
exe_path[exe_used] = '\0';
return exe_path;
}
int main(int argc, char *argv[])
{
int arg;
char *exe;
long pid;
char dummy;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
printf("\n");
printf("Usage: %s [ -h | --help ]\n", argv[0]);
printf(" %s PID [ PID ... ]\n", argv[0]);
printf("\n");
return 0;
}
for (arg = 1; arg < argc; arg++)
if (sscanf(argv[arg], " %ld %c", &pid, &dummy) == 1 && pid > 0L) {
exe = exe_of((pid_t)pid, NULL, NULL);
if (exe) {
printf("Process %ld runs '%s'.\n", pid, exe);
free(exe);
} else
printf("Process %ld: %s.\n", pid, strerror(errno));
} else {
printf("%s: Invalid PID.\n", argv[arg]);
return 1;
}
return 0;
}
Above, the exe_of()
function returns a dynamically allocated copy of where the pseudo-symlink /proc/PID/exe
points to, optionally storing the allocated size and/or the path length too. (The example program above doesn't need them, so they're NULL.)
The idea is very simple: Allocate an initial dynamic pointer that is large enough for most cases, but not ridiculously large. Reserve the last byte for the end-of-string NUL byte. If the size returned by readlink()
is the same as the buffer length given to it -- it does not add a terminating end-of-string NUL byte itself --, then the buffer might have been too short; discard it, allocate a larger buffer, and retry.
Similarly, if you wish to read the full contents of a pseudo-file under /proc/
, you cannot use lstat()
/stat()
first to find out how large a buffer you might need; you need to allocate a buffer, read as much as you can, and when necessary, just reallocate a larger buffer. (I could show example code for that, too.)
Questions?