Start/Stop Apache via Python script - python

On a RHEL 6.7 server running Python 2.6.6 I am trying to start and stop the Apache webserver as an unprivileged user in a Python script.
The command I am using is "sudo service httpd " where parameter is "start", "stop" or "status".
p = subprocess.Popen("sudo service httpd start", stdout=subprocess.PIPE, stderr=subprocess.PIPE)
This line alone does not work. Adding a
p.communicate()
lets the commands work as desired. Can somebody tell me why?
UPDATE:
The sudoers file contains a line that allows my user to run these commands passwordless.

Neither code works (with and without .communicate()).
You should use instead (assuming passwordless sudo for these commands):
import subprocess
subprocess.check_call("sudo service httpd start".split())
The reasons:
subprocess functions do not run the shell by default and therefore the string is interpreted as a name of the command: you should use a list to pass multiple command-line arguments on POSIX
Popen() starts the command and returns immediately without waiting for it to finish i.e., it may happen before httpd is started. Assuming Popen() call is fixed, .communicate() waits for the child process to terminate and therefore it returns after httpd is started (whether it was successful or not).

There are different reasons for that to happen:
1. the user is in sudoers and configured to not insert password.
sudo remembers your password for some time (depending on your system configuration), see: https://unix.stackexchange.com/questions/37299/how-does-sudo-remember-you-already-entered-roots-password
does it work in a brand new terminal session?

Related

How to run two applications (deamon and client) at the same time in single SSH using Python Paramiko

I want to run client and daemon application which responds to client in the same time.
Connection established to SSH using Paramiko. But I could not run both daemon and client in the same time.
How to do this with Paramiko?
Here the expectation is, client provide input as 1,2,3 and daemon responds to each input.
Both run in the same SSH.
Could any one help me with this?
I assume you can use simple shell syntax to achieve what you need. You do not need any fancy code in Python/Paramiko.
Assuming *nix server, see How do I run multiple background commands in bash in a single line?
To run (any) command in Paramiko, see Execute command and wait for it to finish with Python Paramiko
So probably like this:
stdin, stdout, stderr = ssh_client.exec_command("deamon & client")
stdout.channel.set_combine_stderr(True)
output = stdout.readlines()
If you need to somehow run the two commands (deamon and clinet) independenty for a better control, you can start here:
Run multiple commands in different SSH servers in parallel using Python Paramiko
Except that you do not need to open multiple connections (SSHClient). You will just call SSHClient.exec_command twice on the same SSHClient instance.

run bash script in background with process name

Got a script for activating a python venv and running a server in the background, but right now I am trying to keep the pid when I start the process and then kill the process with pid after I am done. However, it is not all the time is gets killed.
My question is, can I run the process with a name, then killing it by using pkill name after? and how will that look
#!/bin/sh
ROOT_DIR=$(pwd)
activate(){
source $ROOT_DIR/.venv/bin/activate
python3 src/server.py -l & pid=$! # <== This is the process
python3 src/client.py localhost 8080
}
activate
sleep 10
kill "$pid"
printf "\n\nServer is done, terminating processes..."
You can run programs with a specific command name by using the bash buildin exec. Note that exec replaces the shell with the command so you have to run it in a subshell environment like:
( exec -a my_new_name my_old_command ) &
However, it probably won't help you much because this sets the command line name, which is apparently different from the command name. So executing the above snippet will show your process as "my_new_name" for example in top or htop, but pkill and killall are filtering by the command name and will thus not find a process called "my_new_name".
While it is interesting, how one can start a command with a different name than the executable, it is most likely not the cause of your problem. PIDs never change, so I assume that the problem lays somewhere different.
My best guess is that the server binds a socket to listen on a specific port. If the program is not shutdown gracefully but killed the port number remains occupied and is only freed by the kernel after some time during some kind of kernel garbage collect. If the program is restarted after a short period of time it finds the port already been occupied and prints a misleading message, that says it is already running. If that is indeed the cause of your problem I would strongly consider implementing a way to graceful shutdown the server. (may be already closing the socket in a destructor or something similar could help)
I think you should have to use systemd for this case:
https://github.com/torfsen/python-systemd-tutorial

using paramiko to start python script remotely, won't import user modules [duplicate]

I am trying to run sesu command in Unix server from Python with the help of Paramiko exec_command. However when I am running this command exec_command('sesu test'), I am getting
sh: sesu: not found
When I am running simple ls command it giving me desired output. Only with sesu command it is not working fine.
This is how my code looks like:
import paramiko
host = host
username = username
password = password
port = port
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip,port,username,password)
stdin,stdout,stderr=ssh.exec_command('sesu test')
stdin.write('Password')
stdin.flush()
outlines=stdout.readlines()
resp=''.join(outlines)
print(resp)
The SSHClient.exec_command by default does not run shell in "login" mode and does not allocate a pseudo terminal for the session. As a consequence a different set of startup scripts is (might be) sourced, than in your regular interactive SSH session (particularly for non-interactive sessions, .bash_profile is not sourced). And/or different branches in the scripts are taken, based on an absence/presence of TERM environment variable.
Possible solutions (in preference order):
Fix the command not to rely on a specific environment. Use a full path to sesu in the command. E.g.:
/bin/sesu test
If you do not know the full path, on common *nix systems, you can use which sesu command in your interactive SSH session.
Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.
Try running the script explicitly via login shell (use --login switch with common *nix shells):
bash --login -c "sesu test"
If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:
PATH="$PATH;/path/to/sesu" && sesu test
Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel using the get_pty parameter:
stdin,stdout,stderr = ssh.exec_command('sesu test', get_pty=True)
Using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?
You may have a similar problem with LD_LIBRARY_PATH and locating shared objects.
See also:
Environment variable differences when using Paramiko
Certain Unix commands fail with "... not found", when executed through Java using JSch

ssh on python script returning none and no value

I am copying a python file (abnormalattr.py) from one linux server to another, and after that executing it on the second server with this command:
ssh root#machinename python tmp/abnormalattr.py
this returns NONE.
But if i execute from 2nd linux server like this:
ssh root#machinename
python tmp/abnormalattr.py
...it returns the correct value.
This script was referring (rather unnecessarily obliquely) to os.environ["HOSTNAME"].
Instances of bash invoked as interactive login shells -- as created by ssh hostname to an account configured to use bash with no further arguments -- source /etc/profile (if present), followed by the first to exist of ~/.bash_profile, ~/.bash_login, or ~/.profile. (Interactive non-login shells run ~/.bashrc).
When you run ssh hostname 'some-command', the command is run in a noninteractive shell. It thus does not have the benefit of /etc/profile or any of these other scripts.
To get the hostname in a manner which does not rely on shell initialization, use the socket library in Python:
import socket
print(socket.getfqdn())

Python on Pi 3 to open and control a console application

I have a very simple problem but cant seem to get a simple solution anywhere.
I have an application running on my Pi which I start by typing into terminal and passing some arguments. For example:
sudo $HOME/Projects/myExampleApp MD_DWNC2378
This results in the console application starting and as expected, can take keyboard inputs.
Now, what I want to do is repeat the process described so far from a python application. My python application should be able to open the myExampleApp in terminal, get a reference to the console window and then direct any commands from my Python application as a keyboard press to myExampleApp.
On a windows platform, pywinauto library does the job.
What is the best option for doing what I described on linux running on my Pi 3?
Any suggestions would be very helpful.
Have a look at https://docs.python.org/2/library/cmd.html for receiving commands. Your application can be run with subprocess as suggested by another user:
import cmd
import sys
import subprocess
class Controller(cmd.Cmd):
cmd = 'while true; do cat /dev/stdin;sleep 1;done'
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
def default(self, cmd):
self.p.stdin.write(cmd+"\n")
a=self.p.stdout.readline()
sys.stdout.write("subprocess returned {}".format(a))
sys.stdout.flush()
if __name__ == '__main__':
try:
Controller().cmdloop()
except:
print('Exit')
Running this script will present you with a CLI interface. Any command sent in will be forwarded to the stdin of your application (simulated by the shell script shown).
If you don't need to process the returning stdout you can skip the self.p.stdout.readline() call. If you need the returning data, it may be beneficial to change the readline() call to read(), this will read until the application sends an EOF.
The sudo requirement of your application can probably be overcome by running the python script with sudo. I'm no security expert, but be aware of the security risks of running the script as sudo, as well as the shell=True parameter of the Popen call.

Categories

Resources