facf0c92e0
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
556 lines
16 KiB
C
556 lines
16 KiB
C
/* Determine the program name of a given process.
|
|
Copyright (C) 2016-2024 Free Software Foundation, Inc.
|
|
Written by Bruno Haible <bruno@clisp.org>, 2019.
|
|
|
|
This file is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This file is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
#include <config.h>
|
|
|
|
/* Specification. */
|
|
#include "get_progname_of.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __NetBSD__ || defined __FreeBSD__ /* Linux, GNU/kFreeBSD, GNU/Hurd, NetBSD, FreeBSD */
|
|
# include <unistd.h>
|
|
# if defined __ANDROID__
|
|
# include <fcntl.h>
|
|
# endif
|
|
#endif
|
|
|
|
#if defined __minix || defined __sun /* Minix, Solaris */
|
|
# include <fcntl.h>
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#if defined __OpenBSD__ /* OpenBSD */
|
|
# include <sys/sysctl.h> /* sysctl, struct kinfo_proc */
|
|
#endif
|
|
|
|
#if defined __APPLE__ && defined __MACH__ /* Mac OS X */
|
|
/* Get MAC_OS_X_VERSION_MIN_REQUIRED, MAC_OS_X_VERSION_MAX_ALLOWED.
|
|
The version at runtime satisfies
|
|
MAC_OS_X_VERSION_MIN_REQUIRED <= version <= MAC_OS_X_VERSION_MAX_ALLOWED. */
|
|
# include <AvailabilityMacros.h>
|
|
# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
|
|
# include <libproc.h>
|
|
# if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
|
/* Mac OS X versions < 10.5 don't have this function. Therefore declare it as
|
|
weak, in order to avoid a runtime error when the binaries are run on these
|
|
older versions. */
|
|
extern int proc_pidinfo (int, int, uint64_t, void *, int) WEAK_IMPORT_ATTRIBUTE;
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
#if defined _AIX /* AIX */
|
|
# include <procinfo.h>
|
|
#endif
|
|
|
|
#if defined __hpux /* HP-UX */
|
|
# include <unistd.h>
|
|
# include <sys/param.h>
|
|
# include <sys/pstat.h>
|
|
#endif
|
|
|
|
#if defined __sgi /* IRIX */
|
|
# include <unistd.h>
|
|
# include <fcntl.h>
|
|
# include <sys/procfs.h>
|
|
#endif
|
|
|
|
#if defined __CYGWIN__ /* Cygwin */
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# include <windows.h> /* needed to get 'struct external_pinfo' defined */
|
|
# include <sys/cygwin.h>
|
|
#endif
|
|
|
|
#if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
|
|
# include <OS.h>
|
|
#endif
|
|
|
|
char *
|
|
get_progname_of (pid_t pid)
|
|
{
|
|
#if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __NetBSD__ /* Linux, GNU/kFreeBSD, GNU/Hurd, NetBSD */
|
|
/* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc
|
|
file system. */
|
|
|
|
/* Read the symlink /proc/<pid>/exe. */
|
|
{
|
|
char filename[6 + 10 + 4 + 1];
|
|
char linkbuf[1024 + 1];
|
|
ssize_t linklen;
|
|
|
|
sprintf (filename, "/proc/%u/exe", (unsigned int) pid);
|
|
linklen = readlink (filename, linkbuf, sizeof (linkbuf) - 1);
|
|
if (linklen > 0)
|
|
{
|
|
char *slash;
|
|
|
|
/* NUL-terminate the link. */
|
|
linkbuf[linklen] = '\0';
|
|
/* Find the portion after the last slash. */
|
|
slash = strrchr (linkbuf, '/');
|
|
return strdup (slash != NULL ? slash + 1 : linkbuf);
|
|
}
|
|
}
|
|
|
|
# if defined __ANDROID__
|
|
/* But it may fail with "Permission denied". As a fallback,
|
|
read the contents of /proc/<pid>/cmdline into memory. */
|
|
{
|
|
char filename[6 + 10 + 8 + 1];
|
|
int fd;
|
|
|
|
sprintf (filename, "/proc/%u/cmdline", (unsigned int) pid);
|
|
fd = open (filename, O_RDONLY | O_CLOEXEC);
|
|
if (fd >= 0)
|
|
{
|
|
char buf[4096 + 1];
|
|
ssize_t nread = read (fd, buf, sizeof (buf) - 1);
|
|
close (fd);
|
|
if (nread >= 0)
|
|
{
|
|
char *slash;
|
|
|
|
/* NUL-terminate the buffer (just in case it does not have the
|
|
expected format). */
|
|
buf[nread] = '\0';
|
|
/* The program name and each argument is followed by a NUL byte. */
|
|
/* Find the portion after the last slash. */
|
|
slash = strrchr (buf, '/');
|
|
return strdup (slash != NULL ? slash + 1 : buf);
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#if defined __FreeBSD__ /* FreeBSD */
|
|
|
|
/* Read the symlink /proc/<pid>/file. */
|
|
char filename[6 + 10 + 5 + 1];
|
|
char linkbuf[1024 + 1];
|
|
ssize_t linklen;
|
|
|
|
sprintf (filename, "/proc/%u/file", (unsigned int) pid);
|
|
linklen = readlink (filename, linkbuf, sizeof (linkbuf) - 1);
|
|
if (linklen > 0)
|
|
{
|
|
char *slash;
|
|
|
|
/* NUL-terminate the link. */
|
|
linkbuf[linklen] = '\0';
|
|
/* Find the portion after the last slash. */
|
|
slash = strrchr (linkbuf, '/');
|
|
return strdup (slash != NULL ? slash + 1 : linkbuf);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined __minix /* Minix */
|
|
|
|
/* Read the contents of /proc/<pid>/psinfo into memory. */
|
|
char filename[6 + 10 + 7 + 1];
|
|
int fd;
|
|
|
|
sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
|
|
fd = open (filename, O_RDONLY | O_CLOEXEC);
|
|
if (fd >= 0)
|
|
{
|
|
char buf[4096 + 1];
|
|
ssize_t nread = read (fd, buf, sizeof (buf) - 1);
|
|
close (fd);
|
|
if (nread >= 0)
|
|
{
|
|
char *p;
|
|
int count;
|
|
|
|
/* NUL-terminate the buffer. */
|
|
buf[nread] = '\0';
|
|
|
|
/* Search for the 4th space-separated field. */
|
|
p = strchr (buf, ' ');
|
|
for (count = 1; p != NULL && count < 3; count++)
|
|
p = strchr (p + 1, ' ');
|
|
if (p != NULL)
|
|
{
|
|
char *start = p + 1;
|
|
char *end = strchr (p + 1, ' ');
|
|
if (end != NULL)
|
|
{
|
|
*end = '\0';
|
|
return strdup (start);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined __sun /* Solaris */
|
|
|
|
/* Read the symlink /proc/<pid>/path/a.out.
|
|
When it succeeds, it doesn't truncate. */
|
|
{
|
|
char filename[6 + 10 + 11 + 1];
|
|
char linkbuf[1024 + 1];
|
|
ssize_t linklen;
|
|
|
|
sprintf (filename, "/proc/%u/path/a.out", (unsigned int) pid);
|
|
linklen = readlink (filename, linkbuf, sizeof (linkbuf) - 1);
|
|
if (linklen > 0)
|
|
{
|
|
char *slash;
|
|
|
|
/* NUL-terminate the link. */
|
|
linkbuf[linklen] = '\0';
|
|
/* Find the portion after the last slash. */
|
|
slash = strrchr (linkbuf, '/');
|
|
return strdup (slash != NULL ? slash + 1 : linkbuf);
|
|
}
|
|
}
|
|
|
|
/* But it may fail with "Permission denied". As a fallback,
|
|
read the contents of /proc/<pid>/psinfo into memory.
|
|
Alternatively, we could read the contents of /proc/<pid>/status into
|
|
memory. But it contains a lot of information that we don't need. */
|
|
{
|
|
char filename[6 + 10 + 7 + 1];
|
|
int fd;
|
|
|
|
sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
|
|
fd = open (filename, O_RDONLY | O_CLOEXEC);
|
|
if (fd >= 0)
|
|
{
|
|
/* The contents is a 'struct psinfo'. But since 'struct psinfo'
|
|
has a different size in a 32-bit and a 64-bit environment, we
|
|
avoid it. Nevertheless, the size of this contents depends on
|
|
whether the process that reads it is 32-bit or 64-bit! */
|
|
#if defined __LP64__
|
|
# define PSINFO_SIZE 416
|
|
# define PSINFO_FNAME_OFFSET 136
|
|
#else
|
|
# define PSINFO_SIZE 336
|
|
# define PSINFO_FNAME_OFFSET 88
|
|
#endif
|
|
char buf[PSINFO_SIZE];
|
|
ssize_t nread = read (fd, buf, sizeof (buf));
|
|
close (fd);
|
|
if (nread >= PSINFO_FNAME_OFFSET + 16)
|
|
{
|
|
/* Make sure it's NUL-terminated. */
|
|
buf[PSINFO_FNAME_OFFSET + 16] = '\0';
|
|
return strdup (&buf[PSINFO_FNAME_OFFSET]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined __OpenBSD__ /* OpenBSD */
|
|
|
|
/* Documentation: https://man.openbsd.org/sysctl.2 */
|
|
int info_path[] =
|
|
{ CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, sizeof (struct kinfo_proc), 1 };
|
|
struct kinfo_proc info;
|
|
size_t len;
|
|
|
|
len = sizeof (info);
|
|
if (sysctl (info_path, 6, &info, &len, NULL, 0) >= 0 && len == sizeof (info))
|
|
return strdup (info.p_comm);
|
|
|
|
#endif
|
|
|
|
#if defined __APPLE__ && defined __MACH__ /* Mac OS X */
|
|
# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
|
|
|
|
/* Mac OS X >= 10.7 has PROC_PIDT_SHORTBSDINFO. */
|
|
# if defined PROC_PIDT_SHORTBSDINFO
|
|
# if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
|
if (proc_pidinfo != NULL) /* at runtime Mac OS X >= 10.5 ? */
|
|
# endif
|
|
{
|
|
struct proc_bsdshortinfo info;
|
|
|
|
if (proc_pidinfo (pid, PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof (info))
|
|
== sizeof (info))
|
|
return strdup (info.pbsi_comm);
|
|
}
|
|
# endif
|
|
|
|
# if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
|
|
/* For older versions, use PROC_PIDTBSDINFO instead. */
|
|
/* Note: The second part of 'struct proc_bsdinfo' differs in size between
|
|
32-bit and 64-bit environments, and the kernel of Mac OS X 10.5 knows
|
|
only about the 32-bit 'struct proc_bsdinfo'. Fortunately all the info
|
|
we need is in the first part, which is the same in 32-bit and 64-bit. */
|
|
# if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
|
if (proc_pidinfo != NULL) /* at runtime Mac OS X >= 10.5 ? */
|
|
# endif
|
|
{
|
|
struct proc_bsdinfo info;
|
|
|
|
if (proc_pidinfo (pid, PROC_PIDTBSDINFO, 0, &info, 128) == 128)
|
|
return strdup (info.pbi_comm);
|
|
}
|
|
# endif
|
|
|
|
# endif
|
|
#endif
|
|
|
|
#if defined _AIX /* AIX */
|
|
|
|
/* Reference: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/getprocs.htm
|
|
*/
|
|
struct procentry64 procs;
|
|
if (getprocs64 (&procs, sizeof procs, NULL, 0, &pid, 1) > 0)
|
|
return strdup (procs.pi_comm);
|
|
|
|
#endif
|
|
|
|
#if defined __hpux /* HP-UX */
|
|
|
|
char *p;
|
|
struct pst_status status;
|
|
if (pstat_getproc (&status, sizeof status, 0, pid) > 0)
|
|
{
|
|
char *ucomm = status.pst_ucomm;
|
|
char *cmd = status.pst_cmd;
|
|
if (strlen (ucomm) < PST_UCOMMLEN - 1)
|
|
p = ucomm;
|
|
else
|
|
{
|
|
/* ucomm is truncated to length PST_UCOMMLEN - 1.
|
|
Look at cmd instead. */
|
|
char *space = strchr (cmd, ' ');
|
|
if (space != NULL)
|
|
*space = '\0';
|
|
p = strrchr (cmd, '/');
|
|
if (p != NULL)
|
|
p++;
|
|
else
|
|
p = cmd;
|
|
if (strlen (p) > PST_UCOMMLEN - 1
|
|
&& memcmp (p, ucomm, PST_UCOMMLEN - 1) == 0)
|
|
/* p is less truncated than ucomm. */
|
|
;
|
|
else
|
|
p = ucomm;
|
|
}
|
|
p = strdup (p);
|
|
}
|
|
else
|
|
{
|
|
# if !defined __LP64__
|
|
/* Support for 32-bit programs running in 64-bit HP-UX.
|
|
The documented way to do this is to use the same source code
|
|
as above, but in a compilation unit where '#define _PSTAT64 1'
|
|
is in effect. I prefer a single compilation unit; the struct
|
|
size and the offsets are not going to change. */
|
|
char status64[1216];
|
|
if (__pstat_getproc64 (status64, sizeof status64, 0, pid) > 0)
|
|
{
|
|
char *ucomm = status64 + 288;
|
|
char *cmd = status64 + 168;
|
|
if (strlen (ucomm) < PST_UCOMMLEN - 1)
|
|
p = ucomm;
|
|
else
|
|
{
|
|
/* ucomm is truncated to length PST_UCOMMLEN - 1.
|
|
Look at cmd instead. */
|
|
char *space = strchr (cmd, ' ');
|
|
if (space != NULL)
|
|
*space = '\0';
|
|
p = strrchr (cmd, '/');
|
|
if (p != NULL)
|
|
p++;
|
|
else
|
|
p = cmd;
|
|
if (strlen (p) > PST_UCOMMLEN - 1
|
|
&& memcmp (p, ucomm, PST_UCOMMLEN - 1) == 0)
|
|
/* p is less truncated than ucomm. */
|
|
;
|
|
else
|
|
p = ucomm;
|
|
}
|
|
p = strdup (p);
|
|
}
|
|
else
|
|
# endif
|
|
p = NULL;
|
|
}
|
|
if (p != NULL)
|
|
return strdup (p);
|
|
|
|
#endif
|
|
|
|
#if defined __sgi /* IRIX */
|
|
|
|
char filename[12 + 10 + 1];
|
|
int fd;
|
|
|
|
sprintf (filename, "/proc/pinfo/%u", pid);
|
|
fd = open (filename, O_RDONLY | O_CLOEXEC);
|
|
if (0 <= fd)
|
|
{
|
|
prpsinfo_t buf;
|
|
int ioctl_ok = 0 <= ioctl (fd, PIOCPSINFO, &buf);
|
|
close (fd);
|
|
if (ioctl_ok)
|
|
{
|
|
char *name = buf.pr_fname;
|
|
size_t namesize = sizeof buf.pr_fname;
|
|
/* It may not be NUL-terminated. */
|
|
char *namenul = memchr (name, '\0', namesize);
|
|
size_t namelen = namenul ? namenul - name : namesize;
|
|
char *namecopy = malloc (namelen + 1);
|
|
if (namecopy)
|
|
{
|
|
namecopy[namelen] = '\0';
|
|
return memcpy (namecopy, name, namelen);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined __CYGWIN__ /* Cygwin */
|
|
|
|
struct external_pinfo *info =
|
|
(struct external_pinfo *) cygwin_internal (CW_GETPINFO, pid);
|
|
if (info != NULL)
|
|
{
|
|
const char *name = info->progname;
|
|
size_t namesize = sizeof (info->progname);
|
|
/* It may not be NUL-terminated. */
|
|
const char *namenul = memchr (name, '\0', namesize);
|
|
size_t namelen = namenul ? namenul - name : namesize;
|
|
|
|
/* Find the portion after the last backslash.
|
|
Cygwin does not have memrchr(). */
|
|
{
|
|
const char *backslash = memchr (name, '\\', namelen);
|
|
if (backslash != NULL)
|
|
{
|
|
const char *name_end = name + namelen;
|
|
for (;;)
|
|
{
|
|
const char *next_backslash =
|
|
memchr (backslash + 1, '\\', name_end - (backslash + 1));
|
|
if (next_backslash == NULL)
|
|
break;
|
|
backslash = next_backslash;
|
|
}
|
|
name = backslash + 1;
|
|
namelen = name_end - name;
|
|
}
|
|
}
|
|
|
|
{
|
|
char *namecopy = malloc (namelen + 1);
|
|
if (namecopy)
|
|
{
|
|
namecopy[namelen] = '\0';
|
|
return memcpy (namecopy, name, namelen);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
|
|
|
|
team_info info;
|
|
if (_get_team_info (pid, &info, sizeof (info)) == B_OK)
|
|
{
|
|
const char *name = info.args;
|
|
size_t namesize = sizeof (info.args);
|
|
/* It may not be NUL-terminated. */
|
|
const char *namenul = memchr (name, '\0', namesize);
|
|
size_t namelen = namenul ? namenul - name : namesize;
|
|
|
|
/* Take the portion up to the first space. */
|
|
{
|
|
const char *space = memchr (name, ' ', namelen);
|
|
if (space != NULL)
|
|
namelen = space - name;
|
|
}
|
|
|
|
/* Find the portion after the last slash. */
|
|
{
|
|
const char *slash = memchr (name, '/', namelen);
|
|
if (slash != NULL)
|
|
{
|
|
const char *name_end = name + namelen;
|
|
for (;;)
|
|
{
|
|
const char *next_slash =
|
|
memchr (slash + 1, '/', name_end - (slash + 1));
|
|
if (next_slash == NULL)
|
|
break;
|
|
slash = next_slash;
|
|
}
|
|
name = slash + 1;
|
|
namelen = name_end - name;
|
|
}
|
|
}
|
|
|
|
{
|
|
char *namecopy = malloc (namelen + 1);
|
|
if (namecopy)
|
|
{
|
|
namecopy[namelen] = '\0';
|
|
return memcpy (namecopy, name, namelen);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef TEST
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
/* Usage: ./a.out
|
|
or: ./a.out PID
|
|
*/
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
char *arg = argv[1];
|
|
pid_t pid = (arg != NULL ? atoi (arg) : getpid ());
|
|
char *progname = get_progname_of (pid);
|
|
printf ("PID=%lu COMMAND=%s\n",
|
|
(unsigned long) pid, progname != NULL ? progname : "(null)");
|
|
free (progname);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Local Variables:
|
|
* compile-command: "gcc -ggdb -DTEST -Wall -I.. get_progname_of.c"
|
|
* End:
|
|
*/
|
|
|
|
#endif
|