summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjquast <contact@jeffquast.com>2014-06-07 20:35:56 -0700
committerjquast <contact@jeffquast.com>2014-06-07 20:35:56 -0700
commit11af2e908c5c3cd0484ef5f25da2b75054092653 (patch)
treecef5ee403f47d464bf87f5467518094caa005e93
parentcb7cbd21186e67815fb487277b6f0ac479f74d44 (diff)
downloadpexpect-11af2e908c5c3cd0484ef5f25da2b75054092653.tar.gz
Provide new 'is_exe' function used by 'which'
As noted by bug report http://bugs.python.org/issue14706, "This is not a Python bug. os.access() is just a wrapper around the POSIX access() function" http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html """ If any access permissions are checked, each shall be checked individually, as described in XBD File Access Permissions, except that where that description refers to execute permission for a process with appropriate privileges, an implementation may indicate success for X_OK even if execute permission is not granted to any user. """
-rw-r--r--pexpect/__init__.py43
1 files changed, 38 insertions, 5 deletions
diff --git a/pexpect/__init__.py b/pexpect/__init__.py
index f27e3fe..7220437 100644
--- a/pexpect/__init__.py
+++ b/pexpect/__init__.py
@@ -80,6 +80,7 @@ try:
import traceback
import signal
import codecs
+ import stat
except ImportError: # pragma: no cover
err = sys.exc_info()[1]
raise ImportError(str(err) + '''
@@ -1957,15 +1958,48 @@ class searcher_re(object):
return best_index
+def is_exe(fname):
+ # follow symlinks,
+ fpath = os.path.realpath(fname)
+
+ # return False for non-files (directories, fifo, etc.)
+ if not os.path.isfile(fpath):
+ return False
+
+ # On Solaris, etc., "If the process has appropriate privileges, an
+ # implementation may indicate success for X_OK even if none of the
+ # execute file permission bits are set."
+ #
+ # For this reason, it is necessary to explicitly check st_mode
+
+ # get file mode using os.stat, and check if `other',
+ # that is anybody, may read and execute.
+ mode = os.stat(fpath).st_mode
+ if mode & stat.S_IROTH and mode & stat.S_IXOTH:
+ return True
+
+ # get current user's group ids, and check if `group',
+ # when matching ours, may read and execute.
+ user_gids = os.getgroups() + [os.getgid()]
+ if (os.stat(fpath).st_gid in user_gids and
+ mode & stat.S_IRGRP and mode & stat.S_IXGRP):
+ return True
+
+ # finally, if file owner matches our effective userid,
+ # check if `user', may read and execute.
+ user_gids = os.getgroups() + [os.getgid()]
+ if (os.stat(fpath).st_uid == os.geteuid() and
+ mode & stat.S_IRUSR and mode & stat.S_IXUSR):
+ return True
+
+
def which(filename):
'''This takes a given filename; tries to find it in the environment path;
then checks if it is executable. This returns the full path to the filename
if found and executable. Otherwise this returns None.'''
# Special case where filename contains an explicit path.
- if (os.path.dirname(filename) != '' and
- os.access(filename, os.X_OK) and
- os.path.isfile(os.path.realpath(filename))):
+ if os.path.dirname(filename) != '' and is_exe(filename):
return filename
if 'PATH' not in os.environ or os.environ['PATH'] == '':
p = os.defpath
@@ -1974,8 +2008,7 @@ def which(filename):
pathlist = p.split(os.pathsep)
for path in pathlist:
ff = os.path.join(path, filename)
- if (os.access(ff, os.X_OK) and
- os.path.isfile(os.path.realpath(ff))):
+ if is_exe(ff):
return ff
return None