(root)/
gettext-0.22.4/
libtextstyle/
lib/
get_ppid_of.c
       1  /* Determine the parent process of a given process.
       2     Copyright (C) 2019-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2019.
       4  
       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.
       9  
      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.
      14  
      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/>.  */
      17  
      18  #include <config.h>
      19  
      20  /* Specification.  */
      21  #include "get_ppid_of.h"
      22  
      23  #include <stdio.h>
      24  #include <string.h>
      25  
      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
      30  
      31  #if defined __OpenBSD__                                     /* OpenBSD */
      32  # include <sys/sysctl.h> /* sysctl, struct kinfo_proc */
      33  #endif
      34  
      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
      50  
      51  #if defined _AIX                                            /* AIX */
      52  # include <procinfo.h>
      53  #endif
      54  
      55  #if defined __hpux                                          /* HP-UX */
      56  # include <unistd.h>
      57  # include <sys/param.h>
      58  # include <sys/pstat.h>
      59  #endif
      60  
      61  #if defined __sgi                                           /* IRIX */
      62  # include <unistd.h>
      63  # include <fcntl.h>
      64  # include <sys/procfs.h>
      65  #endif
      66  
      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
      72  
      73  #if defined __BEOS__ || defined __HAIKU__                   /* BeOS, Haiku */
      74  # include <unistd.h>
      75  #endif
      76  
      77  pid_t
      78  get_ppid_of (pid_t pid)
      79  {
      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.  */
      83  
      84    /* Read the contents of /proc/<pid>/status into memory.  */
      85    char filename[6 + 10 + 7 + 1];
      86    int fd;
      87  
      88    sprintf (filename, "/proc/%u/status", (unsigned int) pid);
      89    fd = open (filename, O_RDONLY | O_CLOEXEC);
      90    if (fd >= 0)
      91      {
      92        char buf[4096 + 1];
      93        ssize_t nread = read (fd, buf, sizeof (buf) - 1);
      94        close (fd);
      95        if (nread >= 0)
      96          {
      97            char *bufend = buf + nread;
      98            char *p;
      99  
     100            /* NUL-terminate the buffer.  */
     101            *bufend = '\0';
     102  
     103            /* Search for a line that starts with "PPid:".  */
     104            for (p = buf;;)
     105              {
     106                if (bufend - p >= 5 && memcmp (p, "PPid:", 5) == 0)
     107                  {
     108                    unsigned int ppid = 0;
     109                    if (sscanf (p + 5, "%u", &ppid) > 0)
     110                      return ppid;
     111                  }
     112                p = strchr (p, '\n');
     113                if (p != NULL)
     114                  p++;
     115                else
     116                  break;
     117              }
     118          }
     119      }
     120  
     121  #endif
     122  
     123  #if defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* FreeBSD, NetBSD */
     124  
     125    /* Read the contents of /proc/<pid>/status into memory.  */
     126    char filename[6 + 10 + 7 + 1];
     127    int fd;
     128  
     129    sprintf (filename, "/proc/%u/status", (unsigned int) pid);
     130    fd = open (filename, O_RDONLY | O_CLOEXEC);
     131    if (fd >= 0)
     132      {
     133        char buf[4096 + 1];
     134        ssize_t nread = read (fd, buf, sizeof (buf) - 1);
     135        close (fd);
     136        if (nread >= 0)
     137          {
     138            char *p;
     139  
     140            /* NUL-terminate the buffer.  */
     141            buf[nread] = '\0';
     142  
     143            /* Search for the third space-separated field.  */
     144            p = strchr (buf, ' ');
     145            if (p != NULL)
     146              {
     147                p = strchr (p + 1, ' ');
     148                if (p != NULL)
     149                  {
     150                    unsigned int ppid = 0;
     151                    if (sscanf (p + 1, "%u", &ppid) > 0)
     152                      return ppid;
     153                  }
     154              }
     155          }
     156      }
     157  
     158  #endif
     159  
     160  #if defined __minix                                         /* Minix */
     161  
     162    /* Read the contents of /proc/<pid>/psinfo into memory.  */
     163    char filename[6 + 10 + 7 + 1];
     164    int fd;
     165  
     166    sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
     167    fd = open (filename, O_RDONLY | O_CLOEXEC);
     168    if (fd >= 0)
     169      {
     170        char buf[4096 + 1];
     171        ssize_t nread = read (fd, buf, sizeof (buf) - 1);
     172        close (fd);
     173        if (nread >= 0)
     174          {
     175            char *p;
     176            int count;
     177  
     178            /* NUL-terminate the buffer.  */
     179            buf[nread] = '\0';
     180  
     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)
     186              {
     187                unsigned int ppid = 0;
     188                if (sscanf (p + 1, "%u", &ppid) > 0)
     189                  return ppid;
     190              }
     191          }
     192      }
     193  
     194  #endif
     195  
     196  #if defined __sun                                           /* Solaris */
     197  
     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;
     203  
     204    sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
     205    fd = open (filename, O_RDONLY | O_CLOEXEC);
     206    if (fd >= 0)
     207      {
     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];
     222      }
     223  
     224  #endif
     225  
     226  #if defined __OpenBSD__                                     /* OpenBSD */
     227  
     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;
     233  
     234    len = sizeof (info);
     235    if (sysctl (info_path, 6, &info, &len, NULL, 0) >= 0 && len == sizeof (info))
     236      return info.p_ppid;
     237  
     238  #endif
     239  
     240  #if defined __APPLE__ && defined __MACH__                   /* Mac OS X */
     241  # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
     242  
     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
     248      {
     249        struct proc_bsdshortinfo info;
     250  
     251        if (proc_pidinfo (pid, PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof (info))
     252            == sizeof (info))
     253          return info.pbsi_ppid;
     254      }
     255  #  endif
     256  
     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
     266      {
     267        struct proc_bsdinfo info;
     268  
     269        if (proc_pidinfo (pid, PROC_PIDTBSDINFO, 0, &info, 128) == 128)
     270          return info.pbi_ppid;
     271      }
     272  #  endif
     273  
     274  # endif
     275  #endif
     276  
     277  #if defined _AIX                                            /* AIX */
     278  
     279    /* Reference: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/getprocs.htm
     280    */
     281    struct procentry64 procs;
     282    if (getprocs64 (&procs, sizeof procs, NULL, 0, &pid, 1) > 0)
     283      return procs.pi_ppid;
     284  
     285  #endif
     286  
     287  #if defined __hpux                                          /* HP-UX */
     288  
     289    struct pst_status status;
     290    if (pstat_getproc (&status, sizeof status, 0, pid) > 0)
     291      return status.pst_ppid;
     292    else
     293      {
     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
     304      }
     305  
     306  #endif
     307  
     308  #if defined __sgi                                           /* IRIX */
     309  
     310    char filename[12 + 10 + 1];
     311    int fd;
     312  
     313    sprintf (filename, "/proc/pinfo/%u", pid);
     314    fd = open (filename, O_RDONLY | O_CLOEXEC);
     315    if (0 <= fd)
     316      {
     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;
     322      }
     323  
     324  #endif
     325  
     326  #if defined __CYGWIN__                                      /* Cygwin */
     327  
     328    struct external_pinfo *info =
     329      (struct external_pinfo *) cygwin_internal (CW_GETPINFO, pid);
     330    if (info != NULL)
     331      return info->ppid;
     332  
     333  #endif
     334  
     335  #if defined __BEOS__ || defined __HAIKU__                   /* BeOS, Haiku */
     336  
     337    if (pid == getpid ())
     338      return getppid ();
     339  
     340  #endif
     341  
     342    return 0;
     343  }
     344  
     345  #ifdef TEST
     346  
     347  #include <stdlib.h>
     348  #include <unistd.h>
     349  
     350  /* Usage: ./a.out
     351     or:    ./a.out PID
     352   */
     353  int
     354  main (int argc, char *argv[])
     355  {
     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;
     361  }
     362  
     363  /*
     364   * Local Variables:
     365   * compile-command: "gcc -ggdb -DTEST -Wall -I.. get_ppid_of.c"
     366   * End:
     367   */
     368  
     369  #endif