[AD] Using procfs on UNIX for get_executable_name

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


The attached patch changes get_executable_name on UNIX platforms to use 
procfs if this is available.

It first tries the Linux-like procfs to see if there us a symlink /proc/
pid/exe, which the current CVS version also tries to read.
Failing that, it tries to get the information using an ioctl call. This 
should be compatible with UNIX System V systems if I interpret the 
documentation properly. On systems where the procfs ioctl call directly 
returns argc and argv as well (only Solaris, as far as I can tell) it will 
use argv[0] as a basis for the search, otherwise it will try to guess 
argv[0] from the complete argv string, or failing that try to use the 
process name.
If procfs cannot be accessed for whatever reason, it tries to obtain this 
information through a pipe from the ps command.

Because the code to find the executable file in the path is identical in 
all cases, I have pulled that out and made a seperate (static) helper 
function. This also fixes a bug with Allegro's current implementation of 
the same algorithm where the active directory is not searched (although it 
should be, in the case of a realtive path in argv[0]).

The patch adds configure checks for a usable sys/procfs.h and for the 
ability to get argv[0] from procfs.

The old __crt0_argv and __crt0_argc are not used with this patch, meaning 
that Allegro's magic main no longer has any meaning on UNIX systems with 
this patch in place and can be removed.

Moderately tested on Linux and Solaris, although the code `should' work at 
least on IRIX too.

Evert
--- allegro/configure.in	2004-07-28 02:15:33.000000000 +0200
+++ alleg_no_magic_main/configure.in	2004-08-06 13:20:59.000000000 +0200
@@ -414,6 +414,19 @@
   _disabled_modules="ossmidi $_disabled_modules"
 fi
 
+dnl Test for System V procfs
+ALLEGRO_SV_PROCFS
+if test "$allegro_sv_procfs" = yes; then
+   AC_DEFINE(ALLEGRO_HAVE_SV_PROCFS,1,
+      [Define to 1 if you have a System V sys/procfs.h])
+      
+   ALLEGRO_PROCFS_ARGCV
+   if test "$allegro_procfs_argcv" = yes; then
+      AC_DEFINE(ALLEGRO_HAVE_PROCFS_ARGCV,1,
+         [Define to 1 if procfs reveals argc and argv])
+   fi
+fi
+
 dnl Test for X-Windows support.
 ALLEGRO_ACTEST_SUPPORT_XWINDOWS
 if test "$allegro_support_xwindows" = yes; then
--- allegro/aclocal.m4	2004-07-28 02:15:33.000000000 +0200
+++ alleg_no_magic_main/aclocal.m4	2004-08-06 13:21:26.000000000 +0200
@@ -178,6 +178,31 @@
 ])
 
 dnl
+dnl Test for System V sys/procfs.h.
+dnl
+dnl Variables:
+dnl  allegro_sv_procfs=(yes|no)
+dnl
+AC_MSG_CHECKING(for System V sys/procfs)
+AC_DEFUN(ALLEGRO_SV_PROCFS,
+[AC_CHECK_HEADER(sys/procfs.h,
+AC_TRY_COMPILE([#include  <sys/procfs.h>], [struct prpsinfo psinfo;],
+allegro_sv_procfs=yes, allegro_sv_procfs=no), allegro_sv_procfs=no)])
+AC_MSG_RESULT($allegro_sv_procfs)
+
+dnl
+dnl Test if sys/procfs.h tells us argc/argv.
+dnl
+dnl Variables:
+dnl  allegro_procfs_argcv=(yes|no)
+dnl
+AC_MSG_CHECKING(if sys/procfs.h tells us argc/argv)
+AC_DEFUN(ALLEGRO_PROCFS_ARGCV,
+[AC_TRY_COMPILE([#include  <sys/procfs.h>], [struct prpsinfo psinfo; psinfo.pr_argc = 0;],
+allegro_procfs_argcv=yes, allegro_procfs_argcv=no)])
+AC_MSG_RESULT($allegro_procfs_argcv)
+
+dnl
 dnl Test for X-Windows support.
 dnl
 dnl Variables:
--- allegro/src/unix/usystem.c	2004-08-02 13:27:29.000000000 +0200
+++ alleg_no_magic_main/src/unix/usystem.c	2004-08-06 14:04:21.000000000 +0200
@@ -40,6 +40,9 @@
    #include <sys/utsname.h>
 #endif
 
+#ifdef ALLEGRO_HAVE_SV_PROCFS
+   #include <sys/procfs.h>
+#endif
 
 
 /* list the available drivers */
@@ -239,38 +242,40 @@
 
 
 #ifndef ALLEGRO_MACOSX
-
-/* _unix_get_executable_name:
- *  Return full path to the current executable.
+/* _find_executable_file:
+ *  Helper function: searches path and current directory for executable.
+ *  Returns 1 on succes, 0 on failure.
  */
-void _unix_get_executable_name(char *output, int size)
+static int _find_executable_file(const char *filename, char *output, int size)
 {
-   FILE *pipe;
-   char linkname[1024];
-   char filename[1024];
-   struct stat buf;
    char *path;
-   pid_t pid;
-   int len;
 
-   /* get symolic link to executable from proc fs */
-   pid = getpid();
-   sprintf (linkname, "/proc/%d/exe", pid);
-   
-   if (stat (linkname, &buf) == 0) {
-      len = readlink (linkname, filename, sizeof(filename)-1);
-      if (len>-1) {
-	 filename[len] = '\0';
-         
-	 do_uconvert (filename, U_ASCII, output, U_CURRENT, size);
-	 return;
+   /* If filename has an explicit path, search current directory */
+   if (strchr (filename, '/')) {
+      if (filename[0] == '/') {
+         /* Full path; done */
+         do_uconvert (filename, U_ASCII, output, U_CURRENT, size);
+         return 1;
+      }
+      else {
+         struct stat finfo;
+         char pathname[1024];
+         int len;
+            
+	 /* Prepend current directory */
+	 getcwd(pathname, sizeof(pathname));
+	 len = strlen(pathname);
+	 pathname[len] = '/';
+	 _al_sane_strncpy (pathname+len+1, filename, strlen(filename));
+            
+	 if ((stat(pathname, &finfo)==0) && (!S_ISDIR (finfo.st_mode))) {
+	    do_uconvert (pathname, U_ASCII, output, U_CURRENT, size);
+	    return 1;
+	 }
       }
    }
-   
-   /* Cannot stat /proc/pid/exe, or cannot resolve symlink. */
-   /* Fall back on the old argv[0] method */
-   /* If argv[0] has no explicit path, but we do have $PATH, search there */
-   if (!strchr (__crt0_argv[0], '/') && (path = getenv("PATH"))) {
+   /* If filename has no explicit path, but we do have $PATH, search there */
+   else if ((path = getenv("PATH"))) {
       char *start = path, *end = path, *buffer = NULL, *temp;
       struct stat finfo;
 
@@ -278,19 +283,19 @@
 	 end = strchr (start, ':');
 	 if (!end) end = strchr (start, '\0');
 
-	 /* Resize `buffer' for path component, slash, argv[0] and a '\0' */
-	 temp = realloc (buffer, end - start + 1 + strlen (__crt0_argv[0]) + 1);
+	 /* Resize `buffer' for path component, slash, filename and a '\0' */
+	 temp = realloc (buffer, end - start + 1 + strlen (filename) + 1);
 	 if (temp) {
 	    buffer = temp;
 
 	    _al_sane_strncpy (buffer, start, end - start);
 	    *(buffer + (end - start)) = '/';
-	    _al_sane_strncpy (buffer + (end - start) + 1, __crt0_argv[0], end - start + 1 + strlen (__crt0_argv[0]) + 1);
+	    _al_sane_strncpy (buffer + (end - start) + 1, filename, end - start + 1 + strlen (filename) + 1);
 
 	    if ((stat(buffer, &finfo)==0) && (!S_ISDIR (finfo.st_mode))) {
 	       do_uconvert (buffer, U_ASCII, output, U_CURRENT, size);
 	       free (buffer);
-	       return;
+	       return 1;
 	    }
 	 } /* else... ignore the failure; `buffer' is still valid anyway. */
 
@@ -299,9 +304,113 @@
       /* Path search failed */
       free (buffer);
    }
+   
+   return 0;
+}
 
-   /* If argv[0] had a slash, or the path search failed, just return argv[0] */
-   do_uconvert (__crt0_argv[0], U_ASCII, output, U_CURRENT, size);
+/* _unix_get_executable_name:
+ *  Return full path to the current executable, use proc fs if available.
+ */
+void _unix_get_executable_name(char *output, int size)
+{
+   #ifdef ALLEGRO_HAVE_SV_PROCFS
+      struct prpsinfo psinfo;
+      int fd;
+      char *s;
+   #endif
+   char linkname[1024];
+   char filename[1024];
+   struct stat finfo;
+   FILE *pipe;
+   pid_t pid;
+   int len;
+
+   /* We need the PID in order to query procfs */
+   pid = getpid();
+
+   /* First try a Linux-like procfs */   
+   /* get symolic link to executable from proc fs */
+   sprintf (linkname, "/proc/%d/exe", pid);
+   if (stat (linkname, &finfo) == 0) {
+      len = readlink (linkname, filename, sizeof(filename)-1);
+      if (len>-1) {
+	 filename[len] = '\0';
+         
+	 do_uconvert (filename, U_ASCII, output, U_CURRENT, size);
+	 return;
+      }
+   }
+   
+   /* Use System V procfs calls if available */
+   #ifdef ALLEGRO_HAVE_SV_PROCFS
+      sprintf (linkname, "/proc/%d/exe", pid);
+      fd = open(linkname, O_RDONLY);
+      ioctl(fd, PIOCPSINFO, &psinfo);
+      close(fd);
+   
+      /* Use argv[0] directly if we can */
+      #ifdef ALLEGRO_HAVE_PROCFS_ARGCV
+         if (_find_executable_file(psinfo.pr_argv[0], output, size))
+            return;
+      #else
+         /* Emulate it */
+         /* We use the pr_psargs field to find argv[0]
+         * This is better than using the pr_fname field because we need the
+         *  additional path information that may be present in argv[0]
+         */
+         
+         /* Skip other args */
+         s = strchr(psinfo.pr_psargs, ' ');
+         if (s) s[0] = '\0';
+         if (_find_executable_file(psinfo.pr_psargs, output, size))
+            return;
+      #endif
+
+      /* Try the pr_fname just for completeness' sake if argv[0] fails */
+      if (_find_executable_file(psinfo.pr_fname, output, size))
+         return;     
+   #endif
+   
+   /* Last resort: try using the output of the ps command to at least find */
+   /* the name of the file if not the full path */
+   uszprintf (linkname, sizeof(linkname), "ps -f -p %d", pid);
+   do_uconvert (linkname, U_CURRENT, filename, U_ASCII, size);
+   pipe = popen(filename, "r");
+   if (pipe) {
+      /* The first line of output is a header */
+      fgets(linkname, sizeof(linkname), pipe);
+      
+      /* The information we want is in the last column; find it */
+      len = strlen(linkname);
+      while (linkname[len] != ' ' && linkname[len] != '\t')
+         len--;
+      
+      /* The second line contains the info we want */
+      fgets(linkname, sizeof(linkname), pipe);
+      pclose(pipe);
+      
+      /* Treat special cases: filename between [] and - for login shell */
+      if (linkname[len] == '-')
+         len++;
+
+      if (linkname[len] == '[' && linkname[strlen(linkname)] == ']') {
+         len++;
+         linkname[strlen(linkname)] == '\0';
+      }         
+      
+      /* Now, the filename should be in the last column */
+      _al_sane_strncpy (filename, linkname+len+1, strlen(linkname)-len+1);
+            
+      if (_find_executable_file(filename, output, size))
+         return;
+
+      /* Just return the output from ps... */         
+      do_uconvert (filename, U_ASCII, output, U_CURRENT, size);
+      return;
+   }
+   
+   /* Give up; return empty string */
+   do_uconvert ("", U_ASCII, output, U_CURRENT, size);
 }
 
 #endif


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/