root/maint/gnulib/lib/get_progname_of.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_progname_of
  2. main

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

/* [previous][next][first][last][top][bottom][index][help] */