diff options
author | jquast <contact@jeffquast.com> | 2014-06-07 20:35:56 -0700 |
---|---|---|
committer | jquast <contact@jeffquast.com> | 2014-06-07 20:35:56 -0700 |
commit | 11af2e908c5c3cd0484ef5f25da2b75054092653 (patch) | |
tree | cef5ee403f47d464bf87f5467518094caa005e93 | |
parent | cb7cbd21186e67815fb487277b6f0ac479f74d44 (diff) | |
download | pexpect-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__.py | 43 |
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 |