1 /* Determine the program name of a given process.
2 Copyright (C) 2016-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2019.
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation, either version 3 of the
8 License, or (at your option) any later version.
10 This file is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
21 #include "get_progname_of.h"
27 #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __NetBSD__ || defined __FreeBSD__ /* Linux, GNU/kFreeBSD, GNU/Hurd, NetBSD, FreeBSD */
29 # if defined __ANDROID__
34 #if defined __minix || defined __sun /* Minix, Solaris */
39 #if defined __OpenBSD__ /* OpenBSD */
40 # include <sys/sysctl.h> /* sysctl, struct kinfo_proc */
43 #if defined __APPLE__ && defined __MACH__ /* Mac OS X */
44 /* Get MAC_OS_X_VERSION_MIN_REQUIRED, MAC_OS_X_VERSION_MAX_ALLOWED.
45 The version at runtime satisfies
46 MAC_OS_X_VERSION_MIN_REQUIRED <= version <= MAC_OS_X_VERSION_MAX_ALLOWED. */
47 # include <AvailabilityMacros.h>
48 # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
50 # if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
51 /* Mac OS X versions < 10.5 don't have this function. Therefore declare it as
52 weak, in order to avoid a runtime error when the binaries are run on these
54 extern int proc_pidinfo (int, int, uint64_t, void *, int) WEAK_IMPORT_ATTRIBUTE
;
59 #if defined _AIX /* AIX */
60 # include <procinfo.h>
63 #if defined __hpux /* HP-UX */
65 # include <sys/param.h>
66 # include <sys/pstat.h>
69 #if defined __sgi /* IRIX */
72 # include <sys/procfs.h>
75 #if defined __CYGWIN__ /* Cygwin */
76 # define WIN32_LEAN_AND_MEAN
77 # include <windows.h> /* needed to get 'struct external_pinfo' defined */
78 # include <sys/cygwin.h>
81 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
86 get_progname_of (pid_t pid
)
88 #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __NetBSD__ /* Linux, GNU/kFreeBSD, GNU/Hurd, NetBSD */
89 /* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc
92 /* Read the symlink /proc/<pid>/exe. */
94 char filename
[6 + 10 + 4 + 1];
95 char linkbuf
[1024 + 1];
98 sprintf (filename
, "/proc/%u/exe", (unsigned int) pid
);
99 linklen
= readlink (filename
, linkbuf
, sizeof (linkbuf
) - 1);
104 /* NUL-terminate the link. */
105 linkbuf
[linklen
] = '\0';
106 /* Find the portion after the last slash. */
107 slash
= strrchr (linkbuf
, '/');
108 return strdup (slash
!= NULL
? slash
+ 1 : linkbuf
);
112 # if defined __ANDROID__
113 /* But it may fail with "Permission denied". As a fallback,
114 read the contents of /proc/<pid>/cmdline into memory. */
116 char filename
[6 + 10 + 8 + 1];
119 sprintf (filename
, "/proc/%u/cmdline", (unsigned int) pid
);
120 fd
= open (filename
, O_RDONLY
| O_CLOEXEC
);
124 ssize_t nread
= read (fd
, buf
, sizeof (buf
) - 1);
130 /* NUL-terminate the buffer (just in case it does not have the
133 /* The program name and each argument is followed by a NUL byte. */
134 /* Find the portion after the last slash. */
135 slash
= strrchr (buf
, '/');
136 return strdup (slash
!= NULL
? slash
+ 1 : buf
);
144 #if defined __FreeBSD__ /* FreeBSD */
146 /* Read the symlink /proc/<pid>/file. */
147 char filename
[6 + 10 + 5 + 1];
148 char linkbuf
[1024 + 1];
151 sprintf (filename
, "/proc/%u/file", (unsigned int) pid
);
152 linklen
= readlink (filename
, linkbuf
, sizeof (linkbuf
) - 1);
157 /* NUL-terminate the link. */
158 linkbuf
[linklen
] = '\0';
159 /* Find the portion after the last slash. */
160 slash
= strrchr (linkbuf
, '/');
161 return strdup (slash
!= NULL
? slash
+ 1 : linkbuf
);
166 #if defined __minix /* Minix */
168 /* Read the contents of /proc/<pid>/psinfo into memory. */
169 char filename
[6 + 10 + 7 + 1];
172 sprintf (filename
, "/proc/%u/psinfo", (unsigned int) pid
);
173 fd
= open (filename
, O_RDONLY
| O_CLOEXEC
);
177 ssize_t nread
= read (fd
, buf
, sizeof (buf
) - 1);
184 /* NUL-terminate the buffer. */
187 /* Search for the 4th space-separated field. */
188 p
= strchr (buf
, ' ');
189 for (count
= 1; p
!= NULL
&& count
< 3; count
++)
190 p
= strchr (p
+ 1, ' ');
194 char *end
= strchr (p
+ 1, ' ');
198 return strdup (start
);
206 #if defined __sun /* Solaris */
208 /* Read the symlink /proc/<pid>/path/a.out.
209 When it succeeds, it doesn't truncate. */
211 char filename
[6 + 10 + 11 + 1];
212 char linkbuf
[1024 + 1];
215 sprintf (filename
, "/proc/%u/path/a.out", (unsigned int) pid
);
216 linklen
= readlink (filename
, linkbuf
, sizeof (linkbuf
) - 1);
221 /* NUL-terminate the link. */
222 linkbuf
[linklen
] = '\0';
223 /* Find the portion after the last slash. */
224 slash
= strrchr (linkbuf
, '/');
225 return strdup (slash
!= NULL
? slash
+ 1 : linkbuf
);
229 /* But it may fail with "Permission denied". As a fallback,
230 read the contents of /proc/<pid>/psinfo into memory.
231 Alternatively, we could read the contents of /proc/<pid>/status into
232 memory. But it contains a lot of information that we don't need. */
234 char filename
[6 + 10 + 7 + 1];
237 sprintf (filename
, "/proc/%u/psinfo", (unsigned int) pid
);
238 fd
= open (filename
, O_RDONLY
| O_CLOEXEC
);
241 /* The contents is a 'struct psinfo'. But since 'struct psinfo'
242 has a different size in a 32-bit and a 64-bit environment, we
243 avoid it. Nevertheless, the size of this contents depends on
244 whether the process that reads it is 32-bit or 64-bit! */
246 # define PSINFO_SIZE 416
247 # define PSINFO_FNAME_OFFSET 136
249 # define PSINFO_SIZE 336
250 # define PSINFO_FNAME_OFFSET 88
252 char buf
[PSINFO_SIZE
];
253 ssize_t nread
= read (fd
, buf
, sizeof (buf
));
255 if (nread
>= PSINFO_FNAME_OFFSET
+ 16)
257 /* Make sure it's NUL-terminated. */
258 buf
[PSINFO_FNAME_OFFSET
+ 16] = '\0';
259 return strdup (&buf
[PSINFO_FNAME_OFFSET
]);
266 #if defined __OpenBSD__ /* OpenBSD */
268 /* Documentation: https://man.openbsd.org/sysctl.2 */
270 { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
, sizeof (struct kinfo_proc
), 1 };
271 struct kinfo_proc info
;
275 if (sysctl (info_path
, 6, &info
, &len
, NULL
, 0) >= 0 && len
== sizeof (info
))
276 return strdup (info
.p_comm
);
280 #if defined __APPLE__ && defined __MACH__ /* Mac OS X */
281 # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
283 /* Mac OS X >= 10.7 has PROC_PIDT_SHORTBSDINFO. */
284 # if defined PROC_PIDT_SHORTBSDINFO
285 # if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
286 if (proc_pidinfo
!= NULL
) /* at runtime Mac OS X >= 10.5 ? */
289 struct proc_bsdshortinfo info
;
291 if (proc_pidinfo (pid
, PROC_PIDT_SHORTBSDINFO
, 0, &info
, sizeof (info
))
293 return strdup (info
.pbsi_comm
);
297 # if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
298 /* For older versions, use PROC_PIDTBSDINFO instead. */
299 /* Note: The second part of 'struct proc_bsdinfo' differs in size between
300 32-bit and 64-bit environments, and the kernel of Mac OS X 10.5 knows
301 only about the 32-bit 'struct proc_bsdinfo'. Fortunately all the info
302 we need is in the first part, which is the same in 32-bit and 64-bit. */
303 # if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
304 if (proc_pidinfo
!= NULL
) /* at runtime Mac OS X >= 10.5 ? */
307 struct proc_bsdinfo info
;
309 if (proc_pidinfo (pid
, PROC_PIDTBSDINFO
, 0, &info
, 128) == 128)
310 return strdup (info
.pbi_comm
);
317 #if defined _AIX /* AIX */
319 /* Reference: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/getprocs.htm
321 struct procentry64 procs
;
322 if (getprocs64 (&procs
, sizeof procs
, NULL
, 0, &pid
, 1) > 0)
323 return strdup (procs
.pi_comm
);
327 #if defined __hpux /* HP-UX */
330 struct pst_status status
;
331 if (pstat_getproc (&status
, sizeof status
, 0, pid
) > 0)
333 char *ucomm
= status
.pst_ucomm
;
334 char *cmd
= status
.pst_cmd
;
335 if (strlen (ucomm
) < PST_UCOMMLEN
- 1)
339 /* ucomm is truncated to length PST_UCOMMLEN - 1.
340 Look at cmd instead. */
341 char *space
= strchr (cmd
, ' ');
344 p
= strrchr (cmd
, '/');
349 if (strlen (p
) > PST_UCOMMLEN
- 1
350 && memcmp (p
, ucomm
, PST_UCOMMLEN
- 1) == 0)
351 /* p is less truncated than ucomm. */
360 # if !defined __LP64__
361 /* Support for 32-bit programs running in 64-bit HP-UX.
362 The documented way to do this is to use the same source code
363 as above, but in a compilation unit where '#define _PSTAT64 1'
364 is in effect. I prefer a single compilation unit; the struct
365 size and the offsets are not going to change. */
367 if (__pstat_getproc64 (status64
, sizeof status64
, 0, pid
) > 0)
369 char *ucomm
= status64
+ 288;
370 char *cmd
= status64
+ 168;
371 if (strlen (ucomm
) < PST_UCOMMLEN
- 1)
375 /* ucomm is truncated to length PST_UCOMMLEN - 1.
376 Look at cmd instead. */
377 char *space
= strchr (cmd
, ' ');
380 p
= strrchr (cmd
, '/');
385 if (strlen (p
) > PST_UCOMMLEN
- 1
386 && memcmp (p
, ucomm
, PST_UCOMMLEN
- 1) == 0)
387 /* p is less truncated than ucomm. */
403 #if defined __sgi /* IRIX */
405 char filename
[12 + 10 + 1];
408 sprintf (filename
, "/proc/pinfo/%u", pid
);
409 fd
= open (filename
, O_RDONLY
| O_CLOEXEC
);
413 int ioctl_ok
= 0 <= ioctl (fd
, PIOCPSINFO
, &buf
);
417 char *name
= buf
.pr_fname
;
418 size_t namesize
= sizeof buf
.pr_fname
;
419 /* It may not be NUL-terminated. */
420 char *namenul
= memchr (name
, '\0', namesize
);
421 size_t namelen
= namenul
? namenul
- name
: namesize
;
422 char *namecopy
= malloc (namelen
+ 1);
425 namecopy
[namelen
] = '\0';
426 return memcpy (namecopy
, name
, namelen
);
433 #if defined __CYGWIN__ /* Cygwin */
435 struct external_pinfo
*info
=
436 (struct external_pinfo
*) cygwin_internal (CW_GETPINFO
, pid
);
439 const char *name
= info
->progname
;
440 size_t namesize
= sizeof (info
->progname
);
441 /* It may not be NUL-terminated. */
442 const char *namenul
= memchr (name
, '\0', namesize
);
443 size_t namelen
= namenul
? namenul
- name
: namesize
;
445 /* Find the portion after the last backslash.
446 Cygwin does not have memrchr(). */
448 const char *backslash
= memchr (name
, '\\', namelen
);
449 if (backslash
!= NULL
)
451 const char *name_end
= name
+ namelen
;
454 const char *next_backslash
=
455 memchr (backslash
+ 1, '\\', name_end
- (backslash
+ 1));
456 if (next_backslash
== NULL
)
458 backslash
= next_backslash
;
460 name
= backslash
+ 1;
461 namelen
= name_end
- name
;
466 char *namecopy
= malloc (namelen
+ 1);
469 namecopy
[namelen
] = '\0';
470 return memcpy (namecopy
, name
, namelen
);
477 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
480 if (_get_team_info (pid
, &info
, sizeof (info
)) == B_OK
)
482 const char *name
= info
.args
;
483 size_t namesize
= sizeof (info
.args
);
484 /* It may not be NUL-terminated. */
485 const char *namenul
= memchr (name
, '\0', namesize
);
486 size_t namelen
= namenul
? namenul
- name
: namesize
;
488 /* Take the portion up to the first space. */
490 const char *space
= memchr (name
, ' ', namelen
);
492 namelen
= space
- name
;
495 /* Find the portion after the last slash. */
497 const char *slash
= memchr (name
, '/', namelen
);
500 const char *name_end
= name
+ namelen
;
503 const char *next_slash
=
504 memchr (slash
+ 1, '/', name_end
- (slash
+ 1));
505 if (next_slash
== NULL
)
510 namelen
= name_end
- name
;
515 char *namecopy
= malloc (namelen
+ 1);
518 namecopy
[namelen
] = '\0';
519 return memcpy (namecopy
, name
, namelen
);
538 main (int argc
, char *argv
[])
541 pid_t pid
= (arg
!= NULL
? atoi (arg
) : getpid ());
542 char *progname
= get_progname_of (pid
);
543 printf ("PID=%lu COMMAND=%s\n",
544 (unsigned long) pid
, progname
!= NULL
? progname
: "(null)");
551 * compile-command: "gcc -ggdb -DTEST -Wall -I.. get_progname_of.c"