(root)/
gettext-0.22.4/
libtextstyle/
lib/
get_progname_of.c
       1  /* Determine the program name of a given process.
       2     Copyright (C) 2016-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_progname_of.h"
      22  
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  
      27  #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __NetBSD__ || defined __FreeBSD__ /* Linux, GNU/kFreeBSD, GNU/Hurd, NetBSD, FreeBSD */
      28  # include <unistd.h>
      29  # if defined __ANDROID__
      30  #  include <fcntl.h>
      31  # endif
      32  #endif
      33  
      34  #if defined __minix || defined __sun                        /* Minix, Solaris */
      35  # include <fcntl.h>
      36  # include <unistd.h>
      37  #endif
      38  
      39  #if defined __OpenBSD__                                     /* OpenBSD */
      40  # include <sys/sysctl.h> /* sysctl, struct kinfo_proc */
      41  #endif
      42  
      43  #if defined __APPLE__ && defined __MACH__                   /* Mac OS X */
      44  /* Get MAC_OS_X_VERSION_MIN_REQUIRED, MAC_OS_X_VERSION_MAX_ALLOWED.
      45     The version at runtime satisfies
      46     MAC_OS_X_VERSION_MIN_REQUIRED <= version <= MAC_OS_X_VERSION_MAX_ALLOWED.  */
      47  # include <AvailabilityMacros.h>
      48  # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
      49  #  include <libproc.h>
      50  #  if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
      51  /* Mac OS X versions < 10.5 don't have this function.  Therefore declare it as
      52     weak, in order to avoid a runtime error when the binaries are run on these
      53     older versions.  */
      54  extern int proc_pidinfo (int, int, uint64_t, void *, int) WEAK_IMPORT_ATTRIBUTE;
      55  #  endif
      56  # endif
      57  #endif
      58  
      59  #if defined _AIX                                            /* AIX */
      60  # include <procinfo.h>
      61  #endif
      62  
      63  #if defined __hpux                                          /* HP-UX */
      64  # include <unistd.h>
      65  # include <sys/param.h>
      66  # include <sys/pstat.h>
      67  #endif
      68  
      69  #if defined __sgi                                           /* IRIX */
      70  # include <unistd.h>
      71  # include <fcntl.h>
      72  # include <sys/procfs.h>
      73  #endif
      74  
      75  #if defined __CYGWIN__                                      /* Cygwin */
      76  # define WIN32_LEAN_AND_MEAN
      77  # include <windows.h> /* needed to get 'struct external_pinfo' defined */
      78  # include <sys/cygwin.h>
      79  #endif
      80  
      81  #if defined __BEOS__ || defined __HAIKU__                   /* BeOS, Haiku */
      82  # include <OS.h>
      83  #endif
      84  
      85  char *
      86  get_progname_of (pid_t pid)
      87  {
      88  #if defined __linux__ || defined __ANDROID__ || (defined __FreeBSD_kernel__ && !defined __FreeBSD__) || defined __GNU__ || defined __NetBSD__ /* Linux, GNU/kFreeBSD, GNU/Hurd, NetBSD */
      89  /* GNU/kFreeBSD mounts /proc as linprocfs, which looks like a Linux /proc
      90     file system.  */
      91  
      92    /* Read the symlink /proc/<pid>/exe.  */
      93    {
      94      char filename[6 + 10 + 4 + 1];
      95      char linkbuf[1024 + 1];
      96      ssize_t linklen;
      97  
      98      sprintf (filename, "/proc/%u/exe", (unsigned int) pid);
      99      linklen = readlink (filename, linkbuf, sizeof (linkbuf) - 1);
     100      if (linklen > 0)
     101        {
     102          char *slash;
     103  
     104          /* NUL-terminate the link.  */
     105          linkbuf[linklen] = '\0';
     106          /* Find the portion after the last slash.  */
     107          slash = strrchr (linkbuf, '/');
     108          return strdup (slash != NULL ? slash + 1 : linkbuf);
     109        }
     110    }
     111  
     112  # if defined __ANDROID__
     113    /* But it may fail with "Permission denied".  As a fallback,
     114       read the contents of /proc/<pid>/cmdline into memory.  */
     115    {
     116      char filename[6 + 10 + 8 + 1];
     117      int fd;
     118  
     119      sprintf (filename, "/proc/%u/cmdline", (unsigned int) pid);
     120      fd = open (filename, O_RDONLY | O_CLOEXEC);
     121      if (fd >= 0)
     122        {
     123          char buf[4096 + 1];
     124          ssize_t nread = read (fd, buf, sizeof (buf) - 1);
     125          close (fd);
     126          if (nread >= 0)
     127            {
     128              char *slash;
     129  
     130              /* NUL-terminate the buffer (just in case it does not have the
     131                 expected format).  */
     132              buf[nread] = '\0';
     133              /* The program name and each argument is followed by a NUL byte.  */
     134              /* Find the portion after the last slash.  */
     135              slash = strrchr (buf, '/');
     136              return strdup (slash != NULL ? slash + 1 : buf);
     137            }
     138        }
     139    }
     140  # endif
     141  
     142  #endif
     143  
     144  #if defined __FreeBSD__                                     /* FreeBSD */
     145  
     146    /* Read the symlink /proc/<pid>/file.  */
     147    char filename[6 + 10 + 5 + 1];
     148    char linkbuf[1024 + 1];
     149    ssize_t linklen;
     150  
     151    sprintf (filename, "/proc/%u/file", (unsigned int) pid);
     152    linklen = readlink (filename, linkbuf, sizeof (linkbuf) - 1);
     153    if (linklen > 0)
     154      {
     155        char *slash;
     156  
     157        /* NUL-terminate the link.  */
     158        linkbuf[linklen] = '\0';
     159        /* Find the portion after the last slash.  */
     160        slash = strrchr (linkbuf, '/');
     161        return strdup (slash != NULL ? slash + 1 : linkbuf);
     162      }
     163  
     164  #endif
     165  
     166  #if defined __minix                                         /* Minix */
     167  
     168    /* Read the contents of /proc/<pid>/psinfo into memory.  */
     169    char filename[6 + 10 + 7 + 1];
     170    int fd;
     171  
     172    sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
     173    fd = open (filename, O_RDONLY | O_CLOEXEC);
     174    if (fd >= 0)
     175      {
     176        char buf[4096 + 1];
     177        ssize_t nread = read (fd, buf, sizeof (buf) - 1);
     178        close (fd);
     179        if (nread >= 0)
     180          {
     181            char *p;
     182            int count;
     183  
     184            /* NUL-terminate the buffer.  */
     185            buf[nread] = '\0';
     186  
     187            /* Search for the 4th space-separated field.  */
     188            p = strchr (buf, ' ');
     189            for (count = 1; p != NULL && count < 3; count++)
     190              p = strchr (p + 1, ' ');
     191            if (p != NULL)
     192              {
     193                char *start = p + 1;
     194                char *end = strchr (p + 1, ' ');
     195                if (end != NULL)
     196                  {
     197                    *end = '\0';
     198                    return strdup (start);
     199                  }
     200              }
     201          }
     202      }
     203  
     204  #endif
     205  
     206  #if defined __sun                                           /* Solaris */
     207  
     208    /* Read the symlink /proc/<pid>/path/a.out.
     209       When it succeeds, it doesn't truncate.  */
     210    {
     211      char filename[6 + 10 + 11 + 1];
     212      char linkbuf[1024 + 1];
     213      ssize_t linklen;
     214  
     215      sprintf (filename, "/proc/%u/path/a.out", (unsigned int) pid);
     216      linklen = readlink (filename, linkbuf, sizeof (linkbuf) - 1);
     217      if (linklen > 0)
     218        {
     219          char *slash;
     220  
     221          /* NUL-terminate the link.  */
     222          linkbuf[linklen] = '\0';
     223          /* Find the portion after the last slash.  */
     224          slash = strrchr (linkbuf, '/');
     225          return strdup (slash != NULL ? slash + 1 : linkbuf);
     226        }
     227    }
     228  
     229    /* But it may fail with "Permission denied".  As a fallback,
     230       read the contents of /proc/<pid>/psinfo into memory.
     231       Alternatively, we could read the contents of /proc/<pid>/status into
     232       memory.  But it contains a lot of information that we don't need.  */
     233    {
     234      char filename[6 + 10 + 7 + 1];
     235      int fd;
     236  
     237      sprintf (filename, "/proc/%u/psinfo", (unsigned int) pid);
     238      fd = open (filename, O_RDONLY | O_CLOEXEC);
     239      if (fd >= 0)
     240        {
     241          /* The contents is a 'struct psinfo'.  But since 'struct psinfo'
     242             has a different size in a 32-bit and a 64-bit environment, we
     243             avoid it.  Nevertheless, the size of this contents depends on
     244             whether the process that reads it is 32-bit or 64-bit!  */
     245          #if defined __LP64__
     246          # define PSINFO_SIZE 416
     247          # define PSINFO_FNAME_OFFSET 136
     248          #else
     249          # define PSINFO_SIZE 336
     250          # define PSINFO_FNAME_OFFSET 88
     251          #endif
     252          char buf[PSINFO_SIZE];
     253          ssize_t nread = read (fd, buf, sizeof (buf));
     254          close (fd);
     255          if (nread >= PSINFO_FNAME_OFFSET + 16)
     256            {
     257              /* Make sure it's NUL-terminated.  */
     258              buf[PSINFO_FNAME_OFFSET + 16] = '\0';
     259              return strdup (&buf[PSINFO_FNAME_OFFSET]);
     260            }
     261        }
     262    }
     263  
     264  #endif
     265  
     266  #if defined __OpenBSD__                                     /* OpenBSD */
     267  
     268    /* Documentation: https://man.openbsd.org/sysctl.2  */
     269    int info_path[] =
     270      { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, sizeof (struct kinfo_proc), 1 };
     271    struct kinfo_proc info;
     272    size_t len;
     273  
     274    len = sizeof (info);
     275    if (sysctl (info_path, 6, &info, &len, NULL, 0) >= 0 && len == sizeof (info))
     276      return strdup (info.p_comm);
     277  
     278  #endif
     279  
     280  #if defined __APPLE__ && defined __MACH__                   /* Mac OS X */
     281  # if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
     282  
     283    /* Mac OS X >= 10.7 has PROC_PIDT_SHORTBSDINFO.  */
     284  #  if defined PROC_PIDT_SHORTBSDINFO
     285  #   if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
     286    if (proc_pidinfo != NULL) /* at runtime Mac OS X >= 10.5 ? */
     287  #   endif
     288      {
     289        struct proc_bsdshortinfo info;
     290  
     291        if (proc_pidinfo (pid, PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof (info))
     292            == sizeof (info))
     293          return strdup (info.pbsi_comm);
     294      }
     295  #  endif
     296  
     297  #  if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
     298    /* For older versions, use PROC_PIDTBSDINFO instead.  */
     299    /* Note: The second part of 'struct proc_bsdinfo' differs in size between
     300       32-bit and 64-bit environments, and the kernel of Mac OS X 10.5 knows
     301       only about the 32-bit 'struct proc_bsdinfo'.  Fortunately all the info
     302       we need is in the first part, which is the same in 32-bit and 64-bit.  */
     303  #   if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
     304    if (proc_pidinfo != NULL) /* at runtime Mac OS X >= 10.5 ? */
     305  #   endif
     306      {
     307        struct proc_bsdinfo info;
     308  
     309        if (proc_pidinfo (pid, PROC_PIDTBSDINFO, 0, &info, 128) == 128)
     310          return strdup (info.pbi_comm);
     311      }
     312  #  endif
     313  
     314  # endif
     315  #endif
     316  
     317  #if defined _AIX                                            /* AIX */
     318  
     319    /* Reference: https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.basetrf1/getprocs.htm
     320    */
     321    struct procentry64 procs;
     322    if (getprocs64 (&procs, sizeof procs, NULL, 0, &pid, 1) > 0)
     323      return strdup (procs.pi_comm);
     324  
     325  #endif
     326  
     327  #if defined __hpux                                          /* HP-UX */
     328  
     329    char *p;
     330    struct pst_status status;
     331    if (pstat_getproc (&status, sizeof status, 0, pid) > 0)
     332      {
     333        char *ucomm = status.pst_ucomm;
     334        char *cmd = status.pst_cmd;
     335        if (strlen (ucomm) < PST_UCOMMLEN - 1)
     336          p = ucomm;
     337        else
     338          {
     339            /* ucomm is truncated to length PST_UCOMMLEN - 1.
     340               Look at cmd instead.  */
     341            char *space = strchr (cmd, ' ');
     342            if (space != NULL)
     343              *space = '\0';
     344            p = strrchr (cmd, '/');
     345            if (p != NULL)
     346              p++;
     347            else
     348              p = cmd;
     349            if (strlen (p) > PST_UCOMMLEN - 1
     350                && memcmp (p, ucomm, PST_UCOMMLEN - 1) == 0)
     351              /* p is less truncated than ucomm.  */
     352              ;
     353            else
     354              p = ucomm;
     355          }
     356        p = strdup (p);
     357      }
     358    else
     359      {
     360  # if !defined __LP64__
     361        /* Support for 32-bit programs running in 64-bit HP-UX.
     362           The documented way to do this is to use the same source code
     363           as above, but in a compilation unit where '#define _PSTAT64 1'
     364           is in effect.  I prefer a single compilation unit; the struct
     365           size and the offsets are not going to change.  */
     366        char status64[1216];
     367        if (__pstat_getproc64 (status64, sizeof status64, 0, pid) > 0)
     368          {
     369            char *ucomm = status64 + 288;
     370            char *cmd = status64 + 168;
     371            if (strlen (ucomm) < PST_UCOMMLEN - 1)
     372              p = ucomm;
     373            else
     374              {
     375                /* ucomm is truncated to length PST_UCOMMLEN - 1.
     376                   Look at cmd instead.  */
     377                char *space = strchr (cmd, ' ');
     378                if (space != NULL)
     379                  *space = '\0';
     380                p = strrchr (cmd, '/');
     381                if (p != NULL)
     382                  p++;
     383                else
     384                  p = cmd;
     385                if (strlen (p) > PST_UCOMMLEN - 1
     386                    && memcmp (p, ucomm, PST_UCOMMLEN - 1) == 0)
     387                  /* p is less truncated than ucomm.  */
     388                  ;
     389                else
     390                  p = ucomm;
     391              }
     392            p = strdup (p);
     393          }
     394        else
     395  # endif
     396          p = NULL;
     397      }
     398    if (p != NULL)
     399      return strdup (p);
     400  
     401  #endif
     402  
     403  #if defined __sgi                                           /* IRIX */
     404  
     405    char filename[12 + 10 + 1];
     406    int fd;
     407  
     408    sprintf (filename, "/proc/pinfo/%u", pid);
     409    fd = open (filename, O_RDONLY | O_CLOEXEC);
     410    if (0 <= fd)
     411      {
     412        prpsinfo_t buf;
     413        int ioctl_ok = 0 <= ioctl (fd, PIOCPSINFO, &buf);
     414        close (fd);
     415        if (ioctl_ok)
     416          {
     417            char *name = buf.pr_fname;
     418            size_t namesize = sizeof buf.pr_fname;
     419            /* It may not be NUL-terminated.  */
     420            char *namenul = memchr (name, '\0', namesize);
     421            size_t namelen = namenul ? namenul - name : namesize;
     422            char *namecopy = malloc (namelen + 1);
     423            if (namecopy)
     424              {
     425                namecopy[namelen] = '\0';
     426                return memcpy (namecopy, name, namelen);
     427              }
     428          }
     429      }
     430  
     431  #endif
     432  
     433  #if defined __CYGWIN__                                      /* Cygwin */
     434  
     435    struct external_pinfo *info =
     436      (struct external_pinfo *) cygwin_internal (CW_GETPINFO, pid);
     437    if (info != NULL)
     438      {
     439        const char *name = info->progname;
     440        size_t namesize = sizeof (info->progname);
     441        /* It may not be NUL-terminated.  */
     442        const char *namenul = memchr (name, '\0', namesize);
     443        size_t namelen = namenul ? namenul - name : namesize;
     444  
     445        /* Find the portion after the last backslash.
     446           Cygwin does not have memrchr().  */
     447        {
     448          const char *backslash = memchr (name, '\\', namelen);
     449          if (backslash != NULL)
     450            {
     451              const char *name_end = name + namelen;
     452              for (;;)
     453                {
     454                  const char *next_backslash =
     455                    memchr (backslash + 1, '\\', name_end - (backslash + 1));
     456                  if (next_backslash == NULL)
     457                    break;
     458                  backslash = next_backslash;
     459                }
     460              name = backslash + 1;
     461              namelen = name_end - name;
     462            }
     463        }
     464  
     465        {
     466          char *namecopy = malloc (namelen + 1);
     467          if (namecopy)
     468            {
     469              namecopy[namelen] = '\0';
     470              return memcpy (namecopy, name, namelen);
     471            }
     472        }
     473      }
     474  
     475  #endif
     476  
     477  #if defined __BEOS__ || defined __HAIKU__                   /* BeOS, Haiku */
     478  
     479    team_info info;
     480    if (_get_team_info (pid, &info, sizeof (info)) == B_OK)
     481      {
     482        const char *name = info.args;
     483        size_t namesize = sizeof (info.args);
     484        /* It may not be NUL-terminated.  */
     485        const char *namenul = memchr (name, '\0', namesize);
     486        size_t namelen = namenul ? namenul - name : namesize;
     487  
     488        /* Take the portion up to the first space.  */
     489        {
     490          const char *space = memchr (name, ' ', namelen);
     491          if (space != NULL)
     492            namelen = space - name;
     493        }
     494  
     495        /* Find the portion after the last slash.  */
     496        {
     497          const char *slash = memchr (name, '/', namelen);
     498          if (slash != NULL)
     499            {
     500              const char *name_end = name + namelen;
     501              for (;;)
     502                {
     503                  const char *next_slash =
     504                    memchr (slash + 1, '/', name_end - (slash + 1));
     505                  if (next_slash == NULL)
     506                    break;
     507                  slash = next_slash;
     508                }
     509              name = slash + 1;
     510              namelen = name_end - name;
     511            }
     512        }
     513  
     514        {
     515          char *namecopy = malloc (namelen + 1);
     516          if (namecopy)
     517            {
     518              namecopy[namelen] = '\0';
     519              return memcpy (namecopy, name, namelen);
     520            }
     521        }
     522      }
     523  
     524  #endif
     525  
     526    return NULL;
     527  }
     528  
     529  #ifdef TEST
     530  
     531  #include <stdlib.h>
     532  #include <unistd.h>
     533  
     534  /* Usage: ./a.out
     535     or:    ./a.out PID
     536   */
     537  int
     538  main (int argc, char *argv[])
     539  {
     540    char *arg = argv[1];
     541    pid_t pid = (arg != NULL ? atoi (arg) : getpid ());
     542    char *progname = get_progname_of (pid);
     543    printf ("PID=%lu COMMAND=%s\n",
     544            (unsigned long) pid, progname != NULL ? progname : "(null)");
     545    free (progname);
     546    return 0;
     547  }
     548  
     549  /*
     550   * Local Variables:
     551   * compile-command: "gcc -ggdb -DTEST -Wall -I.. get_progname_of.c"
     552   * End:
     553   */
     554  
     555  #endif