A series of applications I’m writing require that the user be able to read from a filesystem with KLOG authentication. Some functions require the user to have KLOG tokens (i.e., be authenticated) and others don’t. I wrote a small Python decorator so that I can refactor the “you must be KLOGed” functionality within my modules:
# this decorator is defined in ``mymodule.utils.decorators``
def require_klog(method):
def require_klog_wrapper(*args, **kwargs):
# run the ``tokens`` program to see if we have KLOG tokens
out = subprocess.Popen('tokens', stdout=subprocess.PIPE)
# the tokens (if any) are located in lines 4:n-1
tokens_list = out.stdout.readlines()[3:-1]
if tokens_list == []:
# this is where I launch KLOG (if the user is not authenticated)
subprocess.Popen('klog')
out = method(*args, **kwargs)
return out
return require_klog_wrapper
# once the decorator is defined, any function can use it as follows:
from mymodule.utils.decorators import require_klog
@require_klog
def my_function():
# do something (if not KLOGed, it SHUOLD ask for the password... but it does not!)
It is all very simple. Except when I tried to apply the following logic: “if the user is not KLOGed, run KLOG and ask for the password”.
I do this using subprocess.Popen('klog') and the password: prompt does come up to the terminal. However, when I write the password it actually is echoed back to the terminal and, worse, nothing happens upon hitting return.
Edit:
After Alex’s fast and correct response I solved the problem as follows:
- I erased all the
*.pycfiles from my module’s directory (yes – this made a difference) - I used
getpass.getpass()to store the password in a local variable - I called the KLOG command with the
-pipeoption - I passed the locally-stored password to the pipe via the pipe’s
writemethod
Below is the corrected decorator:
def require_klog(method):
def require_klog_wrapper(*args, **kwargs):
# run the ``tokens`` program to see if we have KLOG tokens
out = subprocess.Popen('tokens', stdout=subprocess.PIPE)
# the tokens (if any) are located in lines 4:n-1
tokens_list = out.stdout.readlines()[3:-1]
if tokens_list == []:
args = ['klog', '-pipe']
# this is the custom pwd prompt
pwd = getpass.getpass('Type in your AFS password: ')
pipe = subprocess.Popen(args, stdin=subprocess.PIPE)
# here is where the password is sent to the program
pipe.stdin.write(pwd)
return method(*args, **kwargs)
return require_klog_wrapper
Apparently, your script (or something else it’s spawning later) and the subprocess running
klogare “competing” for the/dev/tty— and the subprocess is losing (after all, you’re not calling thewaitmethod of the object returned fromsubprocess.Popen, to ensure you wait until it terminates before continuing, so a race condition of some kind would be hardly surprising).If a
waitdoes not suffice, I would work around this by puttingin the Python code (with an
import getpassat the top of course), then runningklogwith the-pipeargument andstdin=subprocess.PIPE, and writingpwdto that pipe (with a call to the communicate method of the object returned fromsubprocess.Popen).