exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / get_ppid_of.c
blob5eda4f4a76421bf4315d33b68c4cb60eddd4c8bb
1 /* Determine the parent process of a given process.
2 Copyright (C) 2019-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/>. */
18 #include <config.h>
20 /* Specification. */
21 #include "get_ppid_of.h"
23 #include <stdio.h>
24 #include <string.h>
26 #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ || defined __minix || defined __sun /* Linux, GNU/kFreeBSD, GNU/Hurd, FreeBSD, NetBSD, Minix, Solaris */
27 # include <fcntl.h>
28 # include <unistd.h>
29 #endif
31 #if defined __OpenBSD__ /* OpenBSD */
32 # include <sys/sysctl.h> /* sysctl, struct kinfo_proc */
33 #endif
35 #if defined __APPLE__ && defined __MACH__ /* Mac OS X */
36 /* Get MAC_OS_X_VERSION_MIN_REQUIRED, MAC_OS_X_VERSION_MAX_ALLOWED.
37 The version at runtime satisfies
38 MAC_OS_X_VERSION_MIN_REQUIRED <= version <= MAC_OS_X_VERSION_MAX_ALLOWED. */
39 # include <AvailabilityMacros.h>
40 # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
41 # include <libproc.h>
42 # if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
43 /* Mac OS X versions < 10.5 don't have this function. Therefore declare it as
44 weak, in order to avoid a runtime error when the binaries are run on these
45 older versions. */
46 extern int proc_pidinfo (int, int, uint64_t, void *, int) WEAK_IMPORT_ATTRIBUTE;
47 # endif
48 # endif
49 #endif
51 #if defined _AIX /* AIX */
52 # include <procinfo.h>
53 #endif
55 #if defined __hpux /* HP-UX */
56 # include <unistd.h>
57 # include <sys/param.h>
58 # include <sys/pstat.h>
59 #endif
61 #if defined __sgi /* IRIX */
62 # include <unistd.h>
63 # include <fcntl.h>
64 # include <sys/procfs.h>
65 #endif
67 #if defined __CYGWIN__ /* Cygwin */
68 # define WIN32_LEAN_AND_MEAN
69 # include <windows.h> /* needed to get 'struct external_pinfo' defined */
70 # include <sys/cygwin.h>
71 #endif
73 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
74 # include <unistd.h>
75 #endif
77 pid_t
78 get_ppid_of (pid_t pid)
80 #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ /* Linux, GNU/kFreeBSD, GNU/Hurd */
81 /* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc
82 file system. */
84 /* Read the contents of /proc/<pid>/status into memory. */
85 char filename[6 + 10 + 7 + 1];
86 int fd;
88 sprintf (filename, "/proc/%u/status", (unsigned int) pid);
89 fd = open (filename, O_RDONLY | O_CLOEXEC);
90 if (fd >= 0)
92 char buf[4096 + 1];
93 ssize_t nread = read (fd, buf, sizeof (buf) - 1);
94 close (fd);
95 if (nread >= 0)
97 char *bufend = buf + nread;
98 char *p;
100 /* NUL-terminate the buffer. */
101 *bufend = '\0';
103 /* Search for a line that starts with "PPid:". */
104 for (p = buf;;)
106 if (bufend - p >= 5 && memcmp (p, "PPid:", 5) == 0)
108 unsigned int ppid = 0;
109 if (sscanf (p + 5, "%u", &ppid) > 0)
110 return ppid;
112 p = strchr (p, '\n');
113 if (p != NULL)
114 p++;
115 else
116 break;
121 #endif
123 #if defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* FreeBSD, NetBSD */
125 /* Read the contents of /proc/<pid>/status into memory. */
126 char filename[6 + 10 + 7 + 1];
127 int fd;
129 sprintf (filename, "/proc/%u/status", (unsigned int) pid);
130 fd = open (filename, O_RDONLY | O_CLOEXEC);
131 if (fd >= 0)
133 char buf[4096 + 1];
134 ssize_t nread = read (fd, buf, sizeof (buf) - 1);
135 close (fd);
136 if (nread >= 0)
138 char *p;
140 /* NUL-terminate the buffer. */
141 buf[nread] = '\0';
143 /* Search for the third space-separated field. */
144 p = strchr (buf, ' ');
145 if (p != NULL)
147 p = strchr (p + 1, ' ');
148 if (p != NULL)
150 unsigned int ppid = 0;
151 if (sscanf (p + 1, "%u", &ppid) > 0)
152 return ppid;
158 #endif
160 #if defined __minix /* Minix */
162 /* Read the contents of /proc/<pid>/psinfo into memory. */
163 char filename[6 + 10 + 7 + 1];
164 int fd;
166 sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
167 fd = open (filename, O_RDONLY | O_CLOEXEC);
168 if (fd >= 0)
170 char buf[4096 + 1];
171 ssize_t nread = read (fd, buf, sizeof (buf) - 1);
172 close (fd);
173 if (nread >= 0)
175 char *p;
176 int count;
178 /* NUL-terminate the buffer. */
179 buf[nread] = '\0';
181 /* Search for the 16th space-separated field. */
182 p = strchr (buf, ' ');
183 for (count = 1; p != NULL && count < 15; count++)
184 p = strchr (p + 1, ' ');
185 if (p != NULL)
187 unsigned int ppid = 0;
188 if (sscanf (p + 1, "%u", &ppid) > 0)
189 return ppid;
194 #endif
196 #if defined __sun /* Solaris */
198 /* Read the contents of /proc/<pid>/psinfo into memory.
199 Alternatively, we could read the contents of /proc/<pid>/status into
200 memory. But it contains a lot of information that we don't need. */
201 char filename[6 + 10 + 7 + 1];
202 int fd;
204 sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
205 fd = open (filename, O_RDONLY | O_CLOEXEC);
206 if (fd >= 0)
208 /* The contents is a 'struct psinfo'. But since 'struct psinfo'
209 has a different size in a 32-bit and a 64-bit environment, we
210 avoid it. Nevertheless, the size of this contents depends on
211 whether the process that reads it is 32-bit or 64-bit! */
212 #if defined __LP64__
213 # define PSINFO_SIZE 416
214 #else
215 # define PSINFO_SIZE 336
216 #endif
217 union { char all[PSINFO_SIZE]; unsigned int header[11]; } buf;
218 ssize_t nread = read (fd, buf.all, sizeof (buf.all));
219 close (fd);
220 if (nread >= (ssize_t) sizeof (buf.header))
221 return buf.header[3];
224 #endif
226 #if defined __OpenBSD__ /* OpenBSD */
228 /* Documentation: https://man.openbsd.org/sysctl.2 */
229 int info_path[] =
230 { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, sizeof (struct kinfo_proc), 1 };
231 struct kinfo_proc info;
232 size_t len;
234 len = sizeof (info);
235 if (sysctl (info_path, 6, &info, &len, NULL, 0) >= 0 && len == sizeof (info))
236 return info.p_ppid;
238 #endif
240 #if defined __APPLE__ && defined __MACH__ /* Mac OS X */
241 # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
243 /* Mac OS X >= 10.7 has PROC_PIDT_SHORTBSDINFO. */
244 # if defined PROC_PIDT_SHORTBSDINFO
245 # if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
246 if (proc_pidinfo != NULL) /* at runtime Mac OS X >= 10.5 ? */
247 # endif
249 struct proc_bsdshortinfo info;
251 if (proc_pidinfo (pid, PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof (info))
252 == sizeof (info))
253 return info.pbsi_ppid;
255 # endif
257 # if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
258 /* For older versions, use PROC_PIDTBSDINFO instead. */
259 /* Note: The second part of 'struct proc_bsdinfo' differs in size between
260 32-bit and 64-bit environments, and the kernel of Mac OS X 10.5 knows
261 only about the 32-bit 'struct proc_bsdinfo'. Fortunately all the info
262 we need is in the first part, which is the same in 32-bit and 64-bit. */
263 # if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
264 if (proc_pidinfo != NULL) /* at runtime Mac OS X >= 10.5 ? */
265 # endif
267 struct proc_bsdinfo info;
269 if (proc_pidinfo (pid, PROC_PIDTBSDINFO, 0, &info, 128) == 128)
270 return info.pbi_ppid;
272 # endif
274 # endif
275 #endif
277 #if defined _AIX /* AIX */
279 /* Reference: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/getprocs.htm
281 struct procentry64 procs;
282 if (getprocs64 (&procs, sizeof procs, NULL, 0, &pid, 1) > 0)
283 return procs.pi_ppid;
285 #endif
287 #if defined __hpux /* HP-UX */
289 struct pst_status status;
290 if (pstat_getproc (&status, sizeof status, 0, pid) > 0)
291 return status.pst_ppid;
292 else
294 # if !defined __LP64__
295 /* Support for 32-bit programs running in 64-bit HP-UX.
296 The documented way to do this is to use the same source code
297 as above, but in a compilation unit where '#define _PSTAT64 1'
298 is in effect. I prefer a single compilation unit; the struct
299 size and the offsets are not going to change. */
300 char status64[1216];
301 if (__pstat_getproc64 (status64, sizeof status64, 0, pid) > 0)
302 return *(unsigned long long *)(status64 + 24);
303 # endif
306 #endif
308 #if defined __sgi /* IRIX */
310 char filename[12 + 10 + 1];
311 int fd;
313 sprintf (filename, "/proc/pinfo/%u", pid);
314 fd = open (filename, O_RDONLY | O_CLOEXEC);
315 if (0 <= fd)
317 prpsinfo_t buf;
318 int ioctl_ok = 0 <= ioctl (fd, PIOCPSINFO, &buf);
319 close (fd);
320 if (ioctl_ok)
321 return buf.pr_ppid;
324 #endif
326 #if defined __CYGWIN__ /* Cygwin */
328 struct external_pinfo *info =
329 (struct external_pinfo *) cygwin_internal (CW_GETPINFO, pid);
330 if (info != NULL)
331 return info->ppid;
333 #endif
335 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
337 if (pid == getpid ())
338 return getppid ();
340 #endif
342 return 0;
345 #ifdef TEST
347 #include <stdlib.h>
348 #include <unistd.h>
350 /* Usage: ./a.out
351 or: ./a.out PID
354 main (int argc, char *argv[])
356 char *arg = argv[1];
357 pid_t pid = (arg != NULL ? atoi (arg) : getpid ());
358 pid_t parent = get_ppid_of (pid);
359 printf ("PID=%lu PPID=%lu\n", (unsigned long) pid, (unsigned long) parent);
360 return 0;
364 * Local Variables:
365 * compile-command: "gcc -ggdb -DTEST -Wall -I.. get_ppid_of.c"
366 * End:
369 #endif