Skip to content

Commit

Permalink
use an import hook and less lazy argument list generation
Browse files Browse the repository at this point in the history
the import hook fixes argparse and similar modules
    seeing and complaining about --_with-elevate-
    options, if the argument parser is called before
    the re-entrant call to elevate()

the explicit argument list generation means that no
    extra empty "" argument is generated when
    restore_cwd=False.
this empty argument is treated by argparse as
    a separate parameter it doesn't know, but
    generating it was a bug in the first place.

also adding documentation for the hacks in elevate_util
  • Loading branch information
catb0t committed Feb 20, 2019
1 parent 4bf87f1 commit de7fa09
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 9 deletions.
6 changes: 6 additions & 0 deletions elevate/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import sys

import elevate.elevate_util as elevate_util

# this is run at import time so as to prevent argument parsers before
# the call to `elevate()` from breaking on our secret options
elevate_util._ELEVATE_GOT_ARGS = elevate_util._process_elevate_opts()


def elevate(show_console=True, graphical=True, restore_cwd=True):
"""
Expand Down
54 changes: 52 additions & 2 deletions elevate/elevate_util.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
import sys


# two parts of the prefix of a special elevate command-line option
_OPT_PREFIX = ("--_", "with-elevate-")
# preserve the command-line arguments to elevate()
_ELEVATE_GOT_ARGS = dict()


# tiny option parser to handle our special --_with-elevate-* opts
def _process_elevate_opts():
'''
Arguments: none
Returns: dict() mapping elevate command-line options to their values
for clarity and disambiguation the `with-elevate-` prefixes
are retained
Throws: No
Effects: Changes sys.argv, removing options specific to elevate
Simple option filter to remove and remember --_with-elevate-* options,
which would break argument parsers that appear before the
invocation of `elevate.elevate()`
These options allow elevate to give data to the elevated process it
will spawn.
This function reads and changes the entire argument list, and so
elevate's special options are not positional.
Calling this function again after the first time will have no effect.
Use _elevate_util._get_opt(opts, name) to get an option from this
dictionary.
'''
opttest = lambda x, m=True: m == all(
["=" in x, x.startswith( "".join(_OPT_PREFIX) )]
)
Expand All @@ -20,8 +44,34 @@ def _process_elevate_opts():


def _get_opt(opts, name):
'''
Arguments: opts (a dictionary of options and values, like returned
from _process_elevate_opts)
name (the base name of the option to get, without its
with-elevate prefix)
Returns: The value of the option, which might be boolean, or False
if the option wasn't specified
Throws: No
Effects: none
Safe/checked way to get an elevate option from a _process_elevate_opts
dictionary.
'''
return opts.get(_OPT_PREFIX[1] + name, False)


def _make_opt(name, param):
'''
Arguments: name (the base name of the option, without its
'with-elevate-' prefix)
param (the string value to specify as the parameter for the
option)
Returns: The string name of the command-line option, to be specified
in the elevated process.
Throws: No
Effects: none
Format the special elevate option with the name `name` and the
parameter `param`, so that a process can be launched with it.
'''
return "".join(_OPT_PREFIX + (name, "=", param))
11 changes: 9 additions & 2 deletions elevate/posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ def quote_applescript(string):

def elevate(show_console=True, graphical=True, restore_cwd=True):
# sys.argv has been changed here
elevate_opts = elevate_util._process_elevate_opts()
# check both values just in case _process_elevate_opts wasn't
# already called on import
elevate_opts = elevate_util._process_elevate_opts() \
or elevate_util._ELEVATE_GOT_ARGS

if os.getuid() == 0:
newdir = elevate_util._get_opt(elevate_opts, "cwd")
Expand All @@ -42,10 +45,14 @@ def elevate(show_console=True, graphical=True, restore_cwd=True):
args = [
sys.executable,
os.path.abspath(sys.argv[0]),
elevate_util._make_opt("cwd", os.getcwd()) if restore_cwd else "",
elevate_util._make_opt("invocation", "True")
] + sys.argv[1:]

# some argument parsers can't understand empty command-line options like ""
# so an explicit conditional append is needed
if restore_cwd:
args.append( elevate_util._make_opt("cwd", os.getcwd()) )

commands = []

if graphical:
Expand Down
5 changes: 4 additions & 1 deletion elevate/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ def __init__(self, **kw):

def elevate(show_console=True, graphical=True, restore_cwd=True):
# sys.argv is changed
elevate_opts = elevate_util._process_elevate_opts()
# check both values just in case _process_elevate_opts wasn't
# already called on import
elevate_opts = elevate_util._process_elevate_opts() \
or elevate_util._ELEVATE_GOT_ARGS

if (windll.shell32.IsUserAnAdmin()
# prevent infinite recursion in all cases
Expand Down
21 changes: 17 additions & 4 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import os, sys
#!/usr/bin/env python3
import os
import sys
import argparse
from elevate import elevate


Expand All @@ -10,14 +13,24 @@ def is_root():
return os.getuid() == 0


parser = argparse.ArgumentParser()
parser.add_argument('--sudo', action='store_true')
parser.add_argument('file')
# sudo'd process falls over here due to `--_` options?
print(sys.argv)
args = parser.parse_args()

print("before: ", os.getcwd())
print("before: ", is_root())

elevate()
if args.sudo:
elevate()
file = args.file # sys.argv[1]

print("after: ", os.getcwd())
print("after: ", is_root())
print(sys.argv)
print(open(sys.argv[1], "r").readline())
print(file)
print(os.getcwd())
print(open(file, "r").readline())

# should print: False True True and a line

0 comments on commit de7fa09

Please sign in to comment.