views:

394

answers:

2

Hello,

I now have a small java script server working correctly, called by:

<?php
  $handle = fsockopen("udp://78.129.148.16",12345);
  fwrite($handle,"vzctlrestart110");
  fclose($handle);
?>

On a remote server the following python server is running and executing the comand's

#!/usr/bin/python
import os
import socket
print "  Loading Bindings..."
settings = {}
line = 0
for each in open('/root/actions.txt', 'r'):
 line = line + 1
 each = each.rstrip()
 if each != "":
   if each[0] != '#':
     a = each.partition(':')
     if a[2]:
       settings[a[0]] = a[2]
     else:
       print "    Err @ line",line,":",each
print "  Starting Server...",
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", port))
print "OK."
print "  Listening on port:", port
while True:
    datagram = s.recv(1024)
    if not datagram:
        break
    print "Rx Cmd:", datagram
    if settings.has_key(datagram):
      print "Launch:", settings[datagram]
      os.system(settings[datagram]+" &")
s.close()

Is it possible to easily send the output of the command back, when the server is started and is running a command the output is shown in the ssh window, however I want this output to be sent back to the browser of the original client, maybe setting the browser to wait for 15 seconds and then check for any data received via the socket.

I know I am asking quite a lot, however I am creating a PHP script which I have a large knowledge about, however my python knowledge lacks greatly.

Thanks, Ashley

+1  A: 

Yes, you can read the output of the command. For this I would recommend the Python subprocess module. Then you can just s.write() it back.

Naturally this has some implications, you would probably have to let your PHP script run for a while longer since the process may be running slow.

# The pipe behaves like a file object in Python.
process = Popen(cmd, shell=True, stdout=PIPE)
process_output = ""
while process.poll():
     process_output += process.stdout.read(256)
s.write(process_output)

# Better yet.
process = Popen(cmd, shell=true, stdout=PIPE)
stdout, stderr = process.communicate() # will read and wait for process to end.
s.write(stdout)

Integrated into your code:

# ... snip ...
import subprocess
con, addr = s.accept()
while True:
    datagram = con.recv(1024)
    if not datagram:
        break
    print "Rx Cmd:", datagram
    if settings.has_key(datagram):
        print "Launch:", settings[datagram]
        process = subprocess.Popen(settings[datagram]+" &", shell=True, stdout=subprocess.PIPE)
        stdout, stderr = process.communicate()
        con.send(stdout)
    con.close()
s.close()
Skurmedel
all my commands take about 15 to 20 second's, as I said im very new to python, any change of a basic script that I can get the idea of how it work's and implement it?
AshleyUK
Well sadly that depends on the command you are running with your Python script I think.If you could do it asynchronously from your page it would at least not make the browser "spin", but that would obviously make matters much more complex.
Skurmedel
I am running the commands vzctl from openvz such as vzctl restart 110 e.t.c, I don't mind the brower spinning, as I want the user to be able to see that the command is running. Im not sure exactly how I could intergrate your above code into the script i'm already using, for example exactly where to place it.
AshleyUK
Okay. Well this would probably go after and in the same block as the print "Launch:" statement
Skurmedel
Im getting the following error, the server start's fine however once I ask it to run a command it crashes and throws this error.Traceback (most recent call last): File "vzctl.py", line 30, in <module> process = Popen(cmd, shell=true, stdout=PIPE)NameError: name 'Popen' is not definedI says the name Popen isnt defined, guessing I need to Include another class at the top of the script?
AshleyUK
Yes that is correct. Sorry for just assuming some stuff :)I'll update my examples.
Skurmedel
I used your code, had to change the "true" to "True" as it was saying "NameError: name 'true' is not defined" I then had to change "PIPE" to "subprocess.PIPE" as it was throwing "NameError: name 'PIPE' is not defined"However now I'm getting: File "vzctl.py", line 33, in <module> s.write(stdout)AttributeError: '_socketobject' object has no attribute 'write'
AshleyUK
Yes sorry Ashley, I'm a bit tired and working in C# right now and behaves like a total Python newbie. :) Python sockets of course use "send" and "recv" so their usage is close to Berkeley sockets. I have updated my last example yet again.
Skurmedel
Hello, It's okie =], I bet your loving me for finding all these errors, anyway I have now changed it to use send, however its not complaing it hasnt't got an adressess to send to. Which is correct, socket.error: (89, 'Destination address required')I know the IP that it will be coming from each and every time, how would I intergate that into the send command, and Im guessing then in PHP I would just need to tell it to read from the socket conection?Thanks
AshleyUK
Oh yes. You'll need to take a look at the "accept" method for Python sockets. That method blocks till it gets a connection. When it gets one it returns a socket which is connected to your PHP script. You are doing it mostly correct before that, with the bind. It's just the accept part that is missing. You then communicate with PHP using the socket you got from accept.
Skurmedel
So I have to create a new conection back to PHP, i can't just tell it to send back via the socket that php originally opened?
AshleyUK
Ashley no no, if you use the accept method, it'll listen for connections. When it gets one, it creates a new connection, which is connected to your PHP script. This assumes your PHP script initiates the communication, like in your example above.What you'll get back is a tuple (like an array, but immutable) with the first index containing a connected socket, and the second index containing the address of the connecting side (your PHP script.) You just read and write from/to that socket instead of "s".
Skurmedel
Ah I understand now, is there any chance you could give me an example of the code, as Ive just looked at some tutorials about the accept method and it just went flying over my head. But yes the PHP does create the socket as indicated in the above script. So just need the accept method code, as I can't understand exactly how I would do it. Thanks so muck skurmedel.
AshleyUK
Okie thanks, if you could just do it when you had the chance once you was home.Thanks,Ashley
AshleyUK
Skurmedel, have you manage to have a go at this yet?Thanks
AshleyUK
Ashley yeah, I've updated, untested though.
Skurmedel
Hello, Im getting the following error message:Traceback (most recent call last): File "vzctl2.py", line 24, in <module> con, addr = s.accept() File "/usr/local/lib/python2.5/socket.py", line 167, in accept sock, addr = self._sock.accept()socket.error: (95, 'Operation not supported')
AshleyUK
Yeah, after bind type in i "s.listen(1)".
Skurmedel
I now have: File "vzctl2.py", line 22, in <module> s.listen(1) File "<string>", line 1, in listensocket.error: (95, 'Operation not supported')
AshleyUK
Hehe, I see why, you have specified datagrams. What you want is TCP/IP. Switch out the socket.SOCK_DGRAM part with socket.SOCK_STREAM. It is two different protocols, and datagrams (UDP) is stateless, TCP/IP is not. Hence datagrams don't listen or connect in the same way. You can do this with datagrams but I think TCP/IP is easier for now.
Skurmedel
It's now not throwing any error's however where before It showed the output of the command in the ssh window, its not doing this. In the PHP script I have changed udp to tcp, is this correct? Is it just not showing the output becuase I have told it to send the output via socket?Thanks
AshleyUK
Ahh looks like I need to change the PHP coding to something like: http://www.justskins.com/forums/help-with-tcp-socket-i-cant-get-it-to-work-96092.htmlDoes that look correct?Thanks,Ashley
AshleyUK
Yes that looks like something you would want. I'm quite ignorant when it comes to the PHP standard library, but as you can see, the functions used in PHP is quite similar to those in PHP. What you are doing is basically building a client in PHP and a server in Python. The client initiates connection and the server listens for connections, when the server accepts the connection they can start communicating.
Skurmedel
The PHP code does not seem to be working, even with the server not running it says it connects fine, it doesn't seem to be connecting correctly to the python server or sending the command.
AshleyUK
Fixed it, used a different script.Thanks,Ashley
AshleyUK
Well as I said its now working, it sends the full respone, however then crashed with the following error: File "/usr/local/lib/python2.5/socket.py", line 141, in _dummy raise error(EBADF, 'Bad file descriptor')socket.error: (9, 'Bad file descriptor')
AshleyUK
Ive read up and it seem's to be because one the php file closes the socket the python server shut's down, however I want it to be constantly waiting for request's. Is this possible?
AshleyUK
Yeah then you'll have to loop, accept:ing new connections and process them each separately. The 1 you give the listen method is how big the backlog should be, that is how many connections are allowed to sit and wait for you to accept them. You could raise that number then do 1) accept a socket 2) process it and close connection 3) go back to 1.
Skurmedel
So change s.listen(1) to a large number? What's the easiest way of running a loop in the python script? Is it a few simple lines of code or more advance? Python noob =]Thnxs for all your help!
AshleyUK
No it's not especially advanced, you'll want to loop forever I guess. It's not the prettiest way, but it's the easiest. while 1: loops forever. I think you can change the number you give to listen to something like 5. The maximum is platform dependent, and the bigger the number the more resources you use up.
Skurmedel
A: 

Here's an example of how to get the output of a command:

>>> import commands
>>> s =  commands.getoutput("ls *")
>>> s
'client.py'
Macarse