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

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