havelib: Fix for Solaris 11 OpenIndiana and Solaris 11 OmniOS.
[gnulib.git] / lib / get_ppid_of.c
blob5579cd9c5dbf59890340e592664e43abf9041040
1 /* Determine the parent process of a given process.
2 Copyright (C) 2019-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/>. */
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 __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 # include <libproc.h>
37 #endif
39 #if defined _AIX /* AIX */
40 # include <procinfo.h>
41 #endif
43 #if defined __hpux /* HP-UX */
44 # include <unistd.h>
45 # include <sys/param.h>
46 # include <sys/pstat.h>
47 #endif
49 #if defined __sgi /* IRIX */
50 # include <unistd.h>
51 # include <fcntl.h>
52 # include <sys/procfs.h>
53 #endif
55 #if defined __CYGWIN__ /* Cygwin */
56 # define WIN32_LEAN_AND_MEAN
57 # include <windows.h> /* needed to get 'struct external_pinfo' defined */
58 # include <sys/cygwin.h>
59 #endif
61 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
62 # include <unistd.h>
63 #endif
65 pid_t
66 get_ppid_of (pid_t pid)
68 #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ /* Linux, GNU/kFreeBSD, GNU/Hurd */
69 /* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc
70 file system. */
72 /* Read the contents of /proc/<pid>/status into memory. */
73 char filename[6 + 10 + 7 + 1];
74 int fd;
76 sprintf (filename, "/proc/%u/status", (unsigned int) pid);
77 fd = open (filename, O_RDONLY | O_CLOEXEC);
78 if (fd >= 0)
80 char buf[4096 + 1];
81 ssize_t nread = read (fd, buf, sizeof (buf) - 1);
82 close (fd);
83 if (nread >= 0)
85 char *bufend = buf + nread;
86 char *p;
88 /* NUL-terminate the buffer. */
89 *bufend = '\0';
91 /* Search for a line that starts with "PPid:". */
92 for (p = buf;;)
94 if (bufend - p >= 5 && memcmp (p, "PPid:", 5) == 0)
96 unsigned int ppid = 0;
97 if (sscanf (p + 5, "%u", &ppid) > 0)
98 return ppid;
100 p = strchr (p, '\n');
101 if (p != NULL)
102 p++;
103 else
104 break;
109 #endif
111 #if defined __FreeBSD__ || defined __NetBSD__ /* FreeBSD, NetBSD */
113 /* Read the contents of /proc/<pid>/status into memory. */
114 char filename[6 + 10 + 7 + 1];
115 int fd;
117 sprintf (filename, "/proc/%u/status", (unsigned int) pid);
118 fd = open (filename, O_RDONLY | O_CLOEXEC);
119 if (fd >= 0)
121 char buf[4096 + 1];
122 ssize_t nread = read (fd, buf, sizeof (buf) - 1);
123 close (fd);
124 if (nread >= 0)
126 char *p;
128 /* NUL-terminate the buffer. */
129 buf[nread] = '\0';
131 /* Search for the third space-separated field. */
132 p = strchr (buf, ' ');
133 if (p != NULL)
135 p = strchr (p + 1, ' ');
136 if (p != NULL)
138 unsigned int ppid = 0;
139 if (sscanf (p + 1, "%u", &ppid) > 0)
140 return ppid;
146 #endif
148 #if defined __minix /* Minix */
150 /* Read the contents of /proc/<pid>/psinfo into memory. */
151 char filename[6 + 10 + 7 + 1];
152 int fd;
154 sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
155 fd = open (filename, O_RDONLY | O_CLOEXEC);
156 if (fd >= 0)
158 char buf[4096 + 1];
159 ssize_t nread = read (fd, buf, sizeof (buf) - 1);
160 close (fd);
161 if (nread >= 0)
163 char *p;
164 int count;
166 /* NUL-terminate the buffer. */
167 buf[nread] = '\0';
169 /* Search for the 16th space-separated field. */
170 p = strchr (buf, ' ');
171 for (count = 1; p != NULL && count < 15; count++)
172 p = strchr (p + 1, ' ');
173 if (p != NULL)
175 unsigned int ppid = 0;
176 if (sscanf (p + 1, "%u", &ppid) > 0)
177 return ppid;
182 #endif
184 #if defined __sun /* Solaris */
186 /* Read the contents of /proc/<pid>/psinfo into memory.
187 Alternatively, we could read the contents of /proc/<pid>/status into
188 memory. But it contains a lot of information that we don't need. */
189 char filename[6 + 10 + 7 + 1];
190 int fd;
192 sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
193 fd = open (filename, O_RDONLY | O_CLOEXEC);
194 if (fd >= 0)
196 /* The contents is a 'struct psinfo'. But since 'struct psinfo'
197 has a different size in a 32-bit and a 64-bit environment, we
198 avoid it. Nevertheless, the size of this contents depends on
199 whether the process that reads it is 32-bit or 64-bit! */
200 #if defined __LP64__
201 # define PSINFO_SIZE 416
202 #else
203 # define PSINFO_SIZE 336
204 #endif
205 union { char all[PSINFO_SIZE]; unsigned int header[11]; } buf;
206 ssize_t nread = read (fd, buf.all, sizeof (buf.all));
207 close (fd);
208 if (nread >= (ssize_t) sizeof (buf.header))
209 return buf.header[3];
212 #endif
214 #if defined __OpenBSD__ /* OpenBSD */
216 /* Documentation: https://man.openbsd.org/sysctl.2 */
217 int info_path[] =
218 { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, sizeof (struct kinfo_proc), 1 };
219 struct kinfo_proc info;
220 size_t len;
222 len = sizeof (info);
223 if (sysctl (info_path, 6, &info, &len, NULL, 0) >= 0 && len == sizeof (info))
224 return info.p_ppid;
226 #endif
228 #if defined __APPLE__ && defined __MACH__ /* Mac OS X */
230 # if defined PROC_PIDT_SHORTBSDINFO
231 struct proc_bsdshortinfo info;
233 if (proc_pidinfo (pid, PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof (info))
234 == sizeof (info))
235 return info.pbsi_ppid;
236 # else
237 /* Note: The second part of 'struct proc_bsdinfo' differs in size between
238 32-bit and 64-bit environments, and the kernel of Mac OS X 10.5 knows
239 only about the 32-bit 'struct proc_bsdinfo'. Fortunately all the info
240 we need is in the first part, which is the same in 32-bit and 64-bit. */
241 struct proc_bsdinfo info;
243 if (proc_pidinfo (pid, PROC_PIDTBSDINFO, 0, &info, 128) == 128)
244 return info.pbi_ppid;
245 # endif
247 #endif
249 #if defined _AIX /* AIX */
251 /* Reference: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/getprocs.htm
253 struct procentry64 procs;
254 if (getprocs64 (&procs, sizeof procs, NULL, 0, &pid, 1) > 0)
255 return procs.pi_ppid;
257 #endif
259 #if defined __hpux /* HP-UX */
261 struct pst_status status;
262 if (pstat_getproc (&status, sizeof status, 0, pid) > 0)
263 return status.pst_ppid;
264 else
266 # if !defined __LP64__
267 /* Support for 32-bit programs running in 64-bit HP-UX.
268 The documented way to do this is to use the same source code
269 as above, but in a compilation unit where '#define _PSTAT64 1'
270 is in effect. I prefer a single compilation unit; the struct
271 size and the offsets are not going to change. */
272 char status64[1216];
273 if (__pstat_getproc64 (status64, sizeof status64, 0, pid) > 0)
274 return *(unsigned long long *)(status64 + 24);
275 # endif
278 #endif
280 #if defined __sgi /* IRIX */
282 char filename[12 + 10 + 1];
283 int fd;
285 sprintf (filename, "/proc/pinfo/%u", pid);
286 fd = open (filename, O_RDONLY | O_CLOEXEC);
287 if (0 <= fd)
289 prpsinfo_t buf;
290 int ioctl_ok = 0 <= ioctl (fd, PIOCPSINFO, &buf);
291 close (fd);
292 if (ioctl_ok)
293 return buf.pr_ppid;
296 #endif
298 #if defined __CYGWIN__ /* Cygwin */
300 struct external_pinfo *info =
301 (struct external_pinfo *) cygwin_internal (CW_GETPINFO, pid);
302 if (info != NULL)
303 return info->ppid;
305 #endif
307 #if defined __BEOS__ || defined __HAIKU__ /* BeOS, Haiku */
309 if (pid == getpid ())
310 return getppid ();
312 #endif
314 return 0;
317 #ifdef TEST
319 #include <stdlib.h>
320 #include <unistd.h>
322 /* Usage: ./a.out
323 or: ./a.out PID
326 main (int argc, char *argv[])
328 char *arg = argv[1];
329 pid_t pid = (arg != NULL ? atoi (arg) : getpid ());
330 pid_t parent = get_ppid_of (pid);
331 printf ("PID=%lu PPID=%lu\n", (unsigned long) pid, (unsigned long) parent);
332 return 0;
336 * Local Variables:
337 * compile-command: "gcc -ggdb -DTEST -Wall -I.. get_ppid_of.c"
338 * End:
341 #endif