0

I am trying to replicate this command using python and Popen:

echo "Acct-Session-Id = 'E4FD590583649358F3B712'" | /usr/local/freeradius/bin/radclient -r 1 1.1.1.1:3799 disconnect secret 

When running this from the command line as it is above, I get the expected:

Sent Disconnect-Request Id 17 from 0.0.0.0:59887 to 1.1.1.1:3799 length 44 

I want to achieve the same from a python script, so I coded it like this:

rp1 = subprocess.Popen(["echo", "Acct-Session-Id = 'E4FD590583649358F3B712'"], stdout=subprocess.PIPE) rp2 = subprocess.Popen(["/usr/local/freeradius/bin/radclient", "-r 1", "1.1.1.1:3799", "disconnect", "secret"], stdin = rp1.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE) rp1.stdout.close() result = rp2.communicate() print "RESULT: " + str(result) 

But, I must be doing this incorrectly as the "result" variable contains the radclient usage info, as if it is called incorrectly:

RESULT: ('', "Usage: radclient [options] server[:port] <command> [<secret>]\n <command>.... 

Anybody any idea where my mistake lies?

Thanks!

9
  • 3
    Why are you using a subprocess for the first command? it's just an echo Commented Sep 27, 2017 at 9:53
  • 2
    "-r 1" should be separate arguments: "-r", "1" Commented Sep 27, 2017 at 9:57
  • @Rawing, that is it! Thanks a million for that! Commented Sep 27, 2017 at 10:00
  • 2
    But you should really decommission that echo as a subprocess. Just write the string into the input of the other subprocess with Python means. This will make everything way simpler. Commented Sep 27, 2017 at 10:01
  • 1
    @Alfe: Actually, as of Python 3.3, communicate supports a timeout argument so you can bound the time spent waiting for a response. Yes, it only does a single input/output, so live back-and-forth communication isn't possible, but the only way to do that safely is to use threads or select/selectors module primitives (the same way communicate does) to manage sending and pulling data in a deadlock-free fashion. Much more complicated, but the only way to do it safely without communicate. Commented Sep 27, 2017 at 16:38

1 Answer 1

1

Besides @Rawing catch of the args typo, you can make it much simpler with a single Popen process. Try this:

rp = subprocess.Popen(["/usr/local/freeradius/bin/radclient", "-r", "1", "1.1.1.1:3799", "disconnect", "secret"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) result = rp.communicate("Acct-Session-Id = 'E4FD590583649358F3B712'") 

Using communicate to handle all the I/O prevents possible deadlocks that are possible when explicitly writing to stdin when you also need to read from stdout/stderr.

Sign up to request clarification or add additional context in comments.

4 Comments

Much better solution: Don't pass bufsize=0, and instead of calling write or close, pass the string you're passing to write to communicate (which, as the name implies, "communicates", passing the argument to the process stdin, but safely, preventing deadlocks which your attempt at unbuffering the process can't prevent).
You're totally right. I forgot you can pass communicate args; thanks for pointing that
Yar. One additional note: If this is Py3, the input string should be bytes, not str (so prefix with b).
I have done that now and it works a charm - much simpler and neater solution. Thanks for that!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.