1 /* Determine the program name of a given process.
2 Copyright (C) 2016-2020 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2019.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program 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 General Public License for more details.
15 You should have received a copy of the GNU 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 */
47 #if defined _AIX /* AIX */
48 # include <procinfo.h>
51 #if defined __hpux /* HP-UX */
53 # include <sys/param.h>
54 # include <sys/pstat.h>
57 #if defined __sgi /* IRIX */
60 # include <sys/procfs.h>
63 #if defined __CYGWIN__ /* Cygwin */
64 # define WIN32_LEAN_AND_MEAN
65 # include <windows.h> /* needed to get 'struct external_pinfo' defined */
66 # include <sys/cygwin.h>
69 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
74 get_progname_of (pid_t pid
)
76 #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __NetBSD__ /* Linux, GNU/kFreeBSD, GNU/Hurd, NetBSD */
77 /* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc
80 /* Read the symlink /proc/<pid>/exe. */
82 char filename
[6 + 10 + 4 + 1];
83 char linkbuf
[1024 + 1];
86 sprintf (filename
, "/proc/%u/exe", (unsigned int) pid
);
87 linklen
= readlink (filename
, linkbuf
, sizeof (linkbuf
) - 1);
92 /* NUL-terminate the link. */
93 linkbuf
[linklen
] = '\0';
94 /* Find the portion after the last slash. */
95 slash
= strrchr (linkbuf
, '/');
96 return strdup (slash
!= NULL
? slash
+ 1 : linkbuf
);
100 # if defined __ANDROID__
101 /* But it may fail with "Permission denied". As a fallback,
102 read the contents of /proc/<pid>/cmdline into memory. */
104 char filename
[6 + 10 + 8 + 1];
107 sprintf (filename
, "/proc/%u/cmdline", (unsigned int) pid
);
108 fd
= open (filename
, O_RDONLY
| O_CLOEXEC
);
112 ssize_t nread
= read (fd
, buf
, sizeof (buf
) - 1);
118 /* NUL-terminate the buffer (just in case it does not have the
121 /* The program name and each argument is followed by a NUL byte. */
122 /* Find the portion after the last slash. */
123 slash
= strrchr (buf
, '/');
124 return strdup (slash
!= NULL
? slash
+ 1 : buf
);
132 #if defined __FreeBSD__ /* FreeBSD */
134 /* Read the symlink /proc/<pid>/file. */
135 char filename
[6 + 10 + 5 + 1];
136 char linkbuf
[1024 + 1];
139 sprintf (filename
, "/proc/%u/file", (unsigned int) pid
);
140 linklen
= readlink (filename
, linkbuf
, sizeof (linkbuf
) - 1);
145 /* NUL-terminate the link. */
146 linkbuf
[linklen
] = '\0';
147 /* Find the portion after the last slash. */
148 slash
= strrchr (linkbuf
, '/');
149 return strdup (slash
!= NULL
? slash
+ 1 : linkbuf
);
154 #if defined __minix /* Minix */
156 /* Read the contents of /proc/<pid>/psinfo into memory. */
157 char filename
[6 + 10 + 7 + 1];
160 sprintf (filename
, "/proc/%u/psinfo", (unsigned int) pid
);
161 fd
= open (filename
, O_RDONLY
| O_CLOEXEC
);
165 ssize_t nread
= read (fd
, buf
, sizeof (buf
) - 1);
172 /* NUL-terminate the buffer. */
175 /* Search for the 4th space-separated field. */
176 p
= strchr (buf
, ' ');
177 for (count
= 1; p
!= NULL
&& count
< 3; count
++)
178 p
= strchr (p
+ 1, ' ');
182 char *end
= strchr (p
+ 1, ' ');
186 return strdup (start
);
194 #if defined __sun /* Solaris */
196 /* Read the symlink /proc/<pid>/path/a.out.
197 When it succeeds, it doesn't truncate. */
199 char filename
[6 + 10 + 11 + 1];
200 char linkbuf
[1024 + 1];
203 sprintf (filename
, "/proc/%u/path/a.out", (unsigned int) pid
);
204 linklen
= readlink (filename
, linkbuf
, sizeof (linkbuf
) - 1);
209 /* NUL-terminate the link. */
210 linkbuf
[linklen
] = '\0';
211 /* Find the portion after the last slash. */
212 slash
= strrchr (linkbuf
, '/');
213 return strdup (slash
!= NULL
? slash
+ 1 : linkbuf
);
217 /* But it may fail with "Permission denied". As a fallback,
218 read the contents of /proc/<pid>/psinfo into memory.
219 Alternatively, we could read the contents of /proc/<pid>/status into
220 memory. But it contains a lot of information that we don't need. */
222 char filename
[6 + 10 + 7 + 1];
225 sprintf (filename
, "/proc/%u/psinfo", (unsigned int) pid
);
226 fd
= open (filename
, O_RDONLY
| O_CLOEXEC
);
229 /* The contents is a 'struct psinfo'. But since 'struct psinfo'
230 has a different size in a 32-bit and a 64-bit environment, we
231 avoid it. Nevertheless, the size of this contents depends on
232 whether the process that reads it is 32-bit or 64-bit! */
234 # define PSINFO_SIZE 416
235 # define PSINFO_FNAME_OFFSET 136
237 # define PSINFO_SIZE 336
238 # define PSINFO_FNAME_OFFSET 88
240 char buf
[PSINFO_SIZE
];
241 ssize_t nread
= read (fd
, buf
, sizeof (buf
));
243 if (nread
>= PSINFO_FNAME_OFFSET
+ 16)
245 /* Make sure it's NUL-terminated. */
246 buf
[PSINFO_FNAME_OFFSET
+ 16] = '\0';
247 return strdup (&buf
[PSINFO_FNAME_OFFSET
]);
254 #if defined __OpenBSD__ /* OpenBSD */
256 /* Documentation: https://man.openbsd.org/sysctl.2 */
258 { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
, sizeof (struct kinfo_proc
), 1 };
259 struct kinfo_proc info
;
263 if (sysctl (info_path
, 6, &info
, &len
, NULL
, 0) >= 0 && len
== sizeof (info
))
264 return strdup (info
.p_comm
);
268 #if defined __APPLE__ && defined __MACH__ /* Mac OS X */
270 # if defined PROC_PIDT_SHORTBSDINFO
271 struct proc_bsdshortinfo info
;
273 if (proc_pidinfo (pid
, PROC_PIDT_SHORTBSDINFO
, 0, &info
, sizeof (info
))
275 return strdup (info
.pbsi_comm
);
277 /* Note: The second part of 'struct proc_bsdinfo' differs in size between
278 32-bit and 64-bit environments, and the kernel of Mac OS X 10.5 knows
279 only about the 32-bit 'struct proc_bsdinfo'. Fortunately all the info
280 we need is in the first part, which is the same in 32-bit and 64-bit. */
281 struct proc_bsdinfo info
;
283 if (proc_pidinfo (pid
, PROC_PIDTBSDINFO
, 0, &info
, 128) == 128)
284 return strdup (info
.pbi_comm
);
289 #if defined _AIX /* AIX */
291 /* Reference: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/getprocs.htm
293 struct procentry64 procs
;
294 if (getprocs64 (&procs
, sizeof procs
, NULL
, 0, &pid
, 1) > 0)
295 return strdup (procs
.pi_comm
);
299 #if defined __hpux /* HP-UX */
302 struct pst_status status
;
303 if (pstat_getproc (&status
, sizeof status
, 0, pid
) > 0)
305 char *ucomm
= status
.pst_ucomm
;
306 char *cmd
= status
.pst_cmd
;
307 if (strlen (ucomm
) < PST_UCOMMLEN
- 1)
311 /* ucomm is truncated to length PST_UCOMMLEN - 1.
312 Look at cmd instead. */
313 char *space
= strchr (cmd
, ' ');
316 p
= strrchr (cmd
, '/');
321 if (strlen (p
) > PST_UCOMMLEN
- 1
322 && memcmp (p
, ucomm
, PST_UCOMMLEN
- 1) == 0)
323 /* p is less truncated than ucomm. */
332 # if !defined __LP64__
333 /* Support for 32-bit programs running in 64-bit HP-UX.
334 The documented way to do this is to use the same source code
335 as above, but in a compilation unit where '#define _PSTAT64 1'
336 is in effect. I prefer a single compilation unit; the struct
337 size and the offsets are not going to change. */
339 if (__pstat_getproc64 (status64
, sizeof status64
, 0, pid
) > 0)
341 char *ucomm
= status64
+ 288;
342 char *cmd
= status64
+ 168;
343 if (strlen (ucomm
) < PST_UCOMMLEN
- 1)
347 /* ucomm is truncated to length PST_UCOMMLEN - 1.
348 Look at cmd instead. */
349 char *space
= strchr (cmd
, ' ');
352 p
= strrchr (cmd
, '/');
357 if (strlen (p
) > PST_UCOMMLEN
- 1
358 && memcmp (p
, ucomm
, PST_UCOMMLEN
- 1) == 0)
359 /* p is less truncated than ucomm. */
375 #if defined __sgi /* IRIX */
377 char filename
[12 + 10 + 1];
380 sprintf (filename
, "/proc/pinfo/%u", pid
);
381 fd
= open (filename
, O_RDONLY
| O_CLOEXEC
);
385 int ioctl_ok
= 0 <= ioctl (fd
, PIOCPSINFO
, &buf
);
389 char *name
= buf
.pr_fname
;
390 size_t namesize
= sizeof buf
.pr_fname
;
391 /* It may not be NUL-terminated. */
392 char *namenul
= memchr (name
, '\0', namesize
);
393 size_t namelen
= namenul
? namenul
- name
: namesize
;
394 char *namecopy
= malloc (namelen
+ 1);
397 namecopy
[namelen
] = '\0';
398 return memcpy (namecopy
, name
, namelen
);
405 #if defined __CYGWIN__ /* Cygwin */
407 struct external_pinfo
*info
=
408 (struct external_pinfo
*) cygwin_internal (CW_GETPINFO
, pid
);
411 const char *name
= info
->progname
;
412 size_t namesize
= sizeof (info
->progname
);
413 /* It may not be NUL-terminated. */
414 const char *namenul
= memchr (name
, '\0', namesize
);
415 size_t namelen
= namenul
? namenul
- name
: namesize
;
417 /* Find the portion after the last backslash.
418 Cygwin does not have memrchr(). */
420 const char *backslash
= memchr (name
, '\\', namelen
);
421 if (backslash
!= NULL
)
423 const char *name_end
= name
+ namelen
;
426 const char *next_backslash
=
427 memchr (backslash
+ 1, '\\', name_end
- (backslash
+ 1));
428 if (next_backslash
== NULL
)
430 backslash
= next_backslash
;
432 name
= backslash
+ 1;
433 namelen
= name_end
- name
;
438 char *namecopy
= malloc (namelen
+ 1);
441 namecopy
[namelen
] = '\0';
442 return memcpy (namecopy
, name
, namelen
);
449 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
452 if (_get_team_info (pid
, &info
, sizeof (info
)) == B_OK
)
454 const char *name
= info
.args
;
455 size_t namesize
= sizeof (info
.args
);
456 /* It may not be NUL-terminated. */
457 const char *namenul
= memchr (name
, '\0', namesize
);
458 size_t namelen
= namenul
? namenul
- name
: namesize
;
460 /* Take the portion up to the first space. */
462 const char *space
= memchr (name
, ' ', namelen
);
464 namelen
= space
- name
;
467 /* Find the portion after the last slash. */
469 const char *slash
= memchr (name
, '/', namelen
);
472 const char *name_end
= name
+ namelen
;
475 const char *next_slash
=
476 memchr (slash
+ 1, '/', name_end
- (slash
+ 1));
477 if (next_slash
== NULL
)
482 namelen
= name_end
- name
;
487 char *namecopy
= malloc (namelen
+ 1);
490 namecopy
[namelen
] = '\0';
491 return memcpy (namecopy
, name
, namelen
);
510 main (int argc
, char *argv
[])
513 pid_t pid
= (arg
!= NULL
? atoi (arg
) : getpid ());
514 char *progname
= get_progname_of (pid
);
515 printf ("PID=%lu COMMAND=%s\n",
516 (unsigned long) pid
, progname
!= NULL
? progname
: "(null)");
523 * compile-command: "gcc -ggdb -DTEST -Wall -I.. get_progname_of.c"