Re: [AD] Using procfs on UNIX for get_executable_name

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


On Friday 06 August 2004 16:08, Evert Glebbeek wrote:
> The attached patch changes get_executable_name on UNIX platforms to use 
> procfs if this is available.

I'm going to apply the attached (more or less equivalent) patch. The only 
difference is that it will use __crt0_argv[0] as a last resort if all else 
fails.

I'll be making a patch to remove END_OF_MAIN() and with it __crt0_argv on 
UNIX systems later on, if we still want that.

Evert
Index: aclocal.m4
===================================================================
RCS file: /cvsroot/alleg/allegro/aclocal.m4,v
retrieving revision 1.65
diff -u -r1.65 aclocal.m4
--- aclocal.m4	6 Aug 2004 15:07:24 -0000	1.65
+++ aclocal.m4	17 Aug 2004 19:25:19 -0000
@@ -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:
Index: configure.in
===================================================================
RCS file: /cvsroot/alleg/allegro/configure.in,v
retrieving revision 1.81
diff -u -r1.81 configure.in
--- configure.in	13 Aug 2004 08:18:40 -0000	1.81
+++ configure.in	17 Aug 2004 19:25:20 -0000
@@ -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
Index: src/unix/usystem.c
===================================================================
RCS file: /cvsroot/alleg/allegro/src/unix/usystem.c,v
retrieving revision 1.30
diff -u -r1.30 usystem.c
--- src/unix/usystem.c	6 Aug 2004 15:15:30 -0000	1.30
+++ src/unix/usystem.c	17 Aug 2004 19:25:21 -0000
@@ -41,6 +41,9 @@
    #include <sys/utsname.h>
 #endif
 
+#ifdef ALLEGRO_HAVE_SV_PROCFS
+   #include <sys/procfs.h>
+#endif
 
 
 /* list the available drivers */
@@ -240,38 +243,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;
 
@@ -279,19 +284,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. */
 
@@ -300,9 +305,117 @@
       /* Path search failed */
       free (buffer);
    }
+   
+   return 0;
+}
+
+/* _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;
+   }
+
+   /* Try the captured argv[0] */   
+   if (_find_executable_file(__crt0_argv[0], output, size))
+      return;
 
-   /* 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);
+   /* 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/