>From 7d91b8f795d82309fbf3aa74faf7ed7374ec7403 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Wed, 14 Aug 2019 01:52:43 +0200 Subject: [PATCH 2/2] get_progname_of: New module. * lib/get_progname_of.h: New file. * lib/get_progname_of.c: New file, based on lib/getprogname.c. * modules/get_progname_of: New file. --- ChangeLog | 7 + lib/get_progname_of.c | 471 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/get_progname_of.h | 35 ++++ modules/get_progname_of | 24 +++ 4 files changed, 537 insertions(+) create mode 100644 lib/get_progname_of.c create mode 100644 lib/get_progname_of.h create mode 100644 modules/get_progname_of diff --git a/ChangeLog b/ChangeLog index f7a8d7a..7597a4b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2019-08-13 Bruno Haible + get_progname_of: New module. + * lib/get_progname_of.h: New file. + * lib/get_progname_of.c: New file, based on lib/getprogname.c. + * modules/get_progname_of: New file. + +2019-08-13 Bruno Haible + get_ppid_of: New module. * lib/get_ppid_of.h: New file. * lib/get_ppid_of.c: New file. diff --git a/lib/get_progname_of.c b/lib/get_progname_of.c new file mode 100644 index 0000000..20daf0b --- /dev/null +++ b/lib/get_progname_of.c @@ -0,0 +1,471 @@ +/* Determine the program name of a given process. + Copyright (C) 2016-2019 Free Software Foundation, Inc. + Written by Bruno Haible , 2019. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +/* Specification. */ +#include "get_progname_of.h" + +#include +#include + +#if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __NetBSD__ || defined __FreeBSD__ /* Linux, GNU/kFreeBSD, GNU/Hurd, NetBSD, FreeBSD */ +# include +# if defined __ANDROID__ +# include +# endif +#endif + +#if defined __minix || defined __sun /* Minix, Solaris */ +# include +# include +#endif + +#if defined __OpenBSD__ /* OpenBSD */ +# include /* sysctl, struct kinfo_proc */ +#endif + +#if defined __APPLE__ && defined __MACH__ /* Mac OS X */ +# include +#endif + +#if defined _AIX /* AIX */ +# include +#endif + +#if defined __hpux /* HP-UX */ +# include +# include +# include +#endif + +#if defined __sgi /* IRIX */ +# include +# include +# include +# include +#endif + +#if defined __CYGWIN__ /* Cygwin */ +# define WIN32_LEAN_AND_MEAN +# include /* needed to get 'struct external_pinfo' defined */ +# include +#endif + +#if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */ +# include +#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//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//cmdline into memory. */ + { + char filename[6 + 10 + 8 + 1]; + int fd; + + sprintf (filename, "/proc/%u/cmdline", (unsigned int) pid); + fd = open (filename, O_RDONLY); + 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//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//psinfo into memory. */ + char filename[6 + 10 + 7 + 1]; + int fd; + + sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid); + fd = open (filename, O_RDONLY); + if (fd >= 0) + { + char buf[4096 + 1]; + ssize_t nread = read (fd, buf, sizeof (buf) - 1); + close (fd); + if (nread >= 0) + { + char *bufend = buf + nread; + int count; + char *p; + + /* NUL-terminate the buffer. */ + *bufend = '\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//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//psinfo into memory. + Alternatively, we could read the contents of /proc//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); + 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 defined PROC_PIDT_SHORTBSDINFO + struct proc_bsdshortinfo info; + + if (proc_pidinfo (pid, PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof (info)) + == sizeof (info)) + return strdup (info.pbsi_comm); +# else + /* 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. */ + struct proc_bsdinfo info; + + if (proc_pidinfo (pid, PROC_PIDTBSDINFO, 0, &info, 128) == 128) + return strdup (info.pbi_comm); +# 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); + 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; + 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) + { + char *backslash; + + /* Make sure it's NUL-terminated. */ + info->progname[sizeof (info->progname)] = '\0'; + /* Find the portion after the last backslash. */ + backslash = strrchr (info->progname, '\\'); + return strdup (backslash != NULL ? backslash + 1 : info->progname); + } + +#endif + +#if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */ + + team_info info; + if (_get_team_info (pid, &info, sizeof (info)) == B_OK) + { + char *space; + char *slash; + + /* Make sure it's NUL-terminated. */ + info.args[sizeof (info.args)] = '\0'; + /* Take the portion up to the first space. */ + space = strchr (info.args, ' '); + if (space != NULL) + *space = '\0'; + /* Find the portion after the last slash. */ + slash = strrchr (info.args, '/'); + return strdup (slash != NULL ? slash + 1 : info.args); + } + +#endif + + return NULL; +} + +#ifdef TEST + +#include +#include + +/* 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)"); + return 0; +} + +/* + * Local Variables: + * compile-command: "gcc -ggdb -DTEST -Wall -I.. get_progname_of.c" + * End: + */ + +#endif diff --git a/lib/get_progname_of.h b/lib/get_progname_of.h new file mode 100644 index 0000000..1f135e7 --- /dev/null +++ b/lib/get_progname_of.h @@ -0,0 +1,35 @@ +/* Determine the program name of a given process. + Copyright (C) 2019 Free Software Foundation, Inc. + Written by Bruno Haible , 2019. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _GET_PROGNAME_OF_H +#define _GET_PROGNAME_OF_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Returns the base name of the program that executes the given process, + or NULL if it cannot be determined. */ +extern char *get_progname_of (pid_t pid); + +#ifdef __cplusplus +} +#endif + +#endif /* _GET_PROGNAME_OF_H */ diff --git a/modules/get_progname_of b/modules/get_progname_of new file mode 100644 index 0000000..012cc59 --- /dev/null +++ b/modules/get_progname_of @@ -0,0 +1,24 @@ +Description: +Determine the program name of a given process. + +Files: +lib/get_progname_of.h +lib/get_progname_of.c + +Depends-on: +extensions +unistd + +configure.ac: + +Makefile.am: +lib_SOURCES += get_progname_of.h get_progname_of.c + +Include: +"get_progname_of.h" + +License: +LGPL + +Maintainer: +all -- 2.7.4