| /* Determine the parent process of a given process. | |
| Copyright (C) 2019-2026 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_ppid_of.h" | |
| #include <stdio.h> | |
| #include <string.h> | |
| #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __gnu_hurd__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ || defined __minix || defined __sun /* Linux, GNU/kFreeBSD, GNU/Hurd, FreeBSD, NetBSD, 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 __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 <unistd.h> | |
| #endif | |
| pid_t | |
| get_ppid_of (pid_t pid) | |
| { | |
| #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __gnu_hurd__ /* Linux, GNU/kFreeBSD, GNU/Hurd */ | |
| /* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc | |
| file system. */ | |
| /* Read the contents of /proc/<pid>/status into memory. */ | |
| char filename[6 + 10 + 7 + 1]; | |
| sprintf (filename, "/proc/%u/status", (unsigned int) pid); | |
| int 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 *bufend = buf + nread; | |
| /* NUL-terminate the buffer. */ | |
| *bufend = '\0'; | |
| /* Search for a line that starts with "PPid:". */ | |
| for (char *p = buf;;) | |
| { | |
| if (bufend - p >= 5 && memeq (p, "PPid:", 5)) | |
| { | |
| unsigned int ppid = 0; | |
| if (sscanf (p + 5, "%u", &ppid) > 0) | |
| return ppid; | |
| } | |
| p = strchr (p, '\n'); | |
| if (p != NULL) | |
| p++; | |
| else | |
| break; | |
| } | |
| } | |
| } | |
| #endif | |
| #if defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* FreeBSD, NetBSD */ | |
| /* Read the contents of /proc/<pid>/status into memory. */ | |
| char filename[6 + 10 + 7 + 1]; | |
| sprintf (filename, "/proc/%u/status", (unsigned int) pid); | |
| int 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) | |
| { | |
| /* NUL-terminate the buffer. */ | |
| buf[nread] = '\0'; | |
| /* Search for the third space-separated field. */ | |
| char *p = strchr (buf, ' '); | |
| if (p != NULL) | |
| { | |
| p = strchr (p + 1, ' '); | |
| if (p != NULL) | |
| { | |
| unsigned int ppid = 0; | |
| if (sscanf (p + 1, "%u", &ppid) > 0) | |
| return ppid; | |
| } | |
| } | |
| } | |
| } | |
| #endif | |
| #if defined __minix /* Minix */ | |
| /* Read the contents of /proc/<pid>/psinfo into memory. */ | |
| char filename[6 + 10 + 7 + 1]; | |
| sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid); | |
| int 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) | |
| { | |
| /* NUL-terminate the buffer. */ | |
| buf[nread] = '\0'; | |
| /* Search for the 16th space-separated field. */ | |
| char *p = strchr (buf, ' '); | |
| for (int count = 1; p != NULL && count < 15; count++) | |
| p = strchr (p + 1, ' '); | |
| if (p != NULL) | |
| { | |
| unsigned int ppid = 0; | |
| if (sscanf (p + 1, "%u", &ppid) > 0) | |
| return ppid; | |
| } | |
| } | |
| } | |
| #endif | |
| #if defined __sun /* Solaris */ | |
| /* 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]; | |
| sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid); | |
| int 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 | |
| #else | |
| # define PSINFO_SIZE 336 | |
| #endif | |
| union { char all[PSINFO_SIZE]; unsigned int header[11]; } buf; | |
| ssize_t nread = read (fd, buf.all, sizeof (buf.all)); | |
| close (fd); | |
| if (nread >= (ssize_t) sizeof (buf.header)) | |
| return buf.header[3]; | |
| } | |
| #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 = sizeof (info); | |
| if (sysctl (info_path, 6, &info, &len, NULL, 0) >= 0 && len == sizeof (info)) | |
| return info.p_ppid; | |
| #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 info.pbsi_ppid; | |
| } | |
| # 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 info.pbi_ppid; | |
| } | |
| # 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 procs.pi_ppid; | |
| #endif | |
| #if defined __hpux /* HP-UX */ | |
| struct pst_status status; | |
| if (pstat_getproc (&status, sizeof status, 0, pid) > 0) | |
| return status.pst_ppid; | |
| 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) | |
| return *(unsigned long long *)(status64 + 24); | |
| # endif | |
| } | |
| #endif | |
| #if defined __CYGWIN__ /* Cygwin */ | |
| struct external_pinfo *info = | |
| (struct external_pinfo *) cygwin_internal (CW_GETPINFO, pid); | |
| if (info != NULL) | |
| return info->ppid; | |
| #endif | |
| #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */ | |
| if (pid == getpid ()) | |
| return getppid (); | |
| #endif | |
| return 0; | |
| } | |
| #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 ()); | |
| pid_t parent = get_ppid_of (pid); | |
| printf ("PID=%lu PPID=%lu\n", (unsigned long) pid, (unsigned long) parent); | |
| return 0; | |
| } | |
| /* | |
| * Local Variables: | |
| * compile-command: "gcc -ggdb -DTEST -Wall -I.. get_ppid_of.c" | |
| * End: | |
| */ | |
| #endif |