iwconfig unavailable to processes started with crontab #reboot? - python

this is my first post to stackoverflow, so please bear with me :)
I'm trying to read the output of iwconfig from a python script to determine whether there is a wifi connection. When I run script (via a bash script that first sets the directory) using crontab #reboot (user, not root), subprocess.check_output(['iwconfig']) always throws an [Errno 2]. This is even true when I catch the error using try/except and loop the code, so it is still running when the Wifi is certainly connected (as I can check with running iwconfig manually). When I run the python script from the command line via the same bash script, it works fine. What am I overlooking?
#!/usr/bin/python3
import subprocess
import time
import logging
logging.basicConfig(filename='wifi_check.log', filemode='w', format='%(name)s - %(levelname)s
- %(message)s', level=logging.DEBUG)
logging.info("Checking for Wifi")
for i in range(20):
try:
iwconfig_output = subprocess.check_output(['iwconfig']).decode('utf-8')
except Exception as err:
logging.error(str(i) + str(err))
else:
logging.debug(str(i) + iwconfig_output)
if "ESSID" in iwconfig_output:
logging.info(str(i) + "Wifi active")
time.sleep(10)

Errno 2 can indicate that the file is not found.
Perhaps iwconfig is not in PATH for the user who executes the script. Try using /sbin/iwconfig (the full path) of the executable to rule this out.

Related

Simple use of Python’s Subprocess.call to open multiple Mac apps

I’d like to open a few apps using a very simple python script:
from subprocess import call
call("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome")
call("/Applications/MongoDB Compass.app/Contents/MacOS/MongoDB Compass")
The problem is that opening them this way seems to open a terminal window along with the app itself - for chrome, it outputs this in the terminal for example:
Last login: Sun Oct 23 00:20:38 on ttys000
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome ; exit;
nick#Nicks-MBP ~ % /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome ; exit;
objc[3817]: Class WebSwapCGLLayer is implemented in both /System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/Frameworks/libANGLE-shared.dylib (0x7ffb45565ec8) and /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/106.0.5249.119/Libraries/libGLESv2.dylib (0x116ba9668). One of the two will be used. Which one is undefined.
So it hijacks the terminal and does not proceed to this next line:
call("/Applications/MongoDB Compass.app/Contents/MacOS/MongoDB Compass")
If I try to call these:
call(("/Applications/Google Chrome.app"))
call(("/Applications/MongoDB Compass.app"))
I get this error, with other posts stating that it may be a dir and not an app:
OSError: [Errno 13] Permission denied
How can this be fixed? Note that I do not want to do this despite it working:
os.system("open /Applications/" + app + ".app")
Because I need to be able to wait for the apps to finish opening before running another command, hence the use of Subprocess.call. Thank you.
UPDATE:
I now have this:
print("start")
call(
[("/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome")],
shell=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT,
)
print("end")
But the print("end") line only executes when I exit out of chrome. How can I get it to wait for Chrome to load and then print 'end' after? Also it requires Shell=True for some reason, otherwise it complains with:
FileNotFoundError: [Errno 2] No such file or directory: '/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome
Updated Answer
This also appears to work and doesn't involve the shell:
import subprocess as sp
print("start")
sp.run(["open", "-a", "Google Chrome"])
print("end")
Original Answer
This appears to do what you want, though I have no explanation as to why:
import subprocess as sp
print("start")
sp.Popen(
["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"],
stdin =sp.DEVNULL,
stdout=sp.DEVNULL,
stderr=sp.DEVNULL)
print("end")

Python script runs fine from terminal but crashes when run from crontab

I have a simple python script that takes screenshots of a computer that is running Ubuntu. I want it to run automatically on startup, so I put #reboot python3 /bin/program.py in the non-sudo version of crontab.
The program works fine when run from terminal, but gives the error pyscreenshot.err.FailedBackendError. I put it in a try loop, and had it write all exceptions to a file, and that's how I found the error message, "All backends failed."
It has something to do with the program 'pyscreenshot' not working correctly.
import pyscreenshot as screen
import os
from numpy import random
from time import sleep
from os.path import expanduser
TMP_SCREEN_PATH = expanduser('~') + '/.UE/tmp.png'
LOG_FILE_PATH = expanduser('~') + '/.UE/log.txt'
GRAB_DELAY_RANGE = (1, 10)
def screenshot(save_path=TMP_SCREEN_PATH):
img = screen.grab()
img.save(save_path)
def delay(delay_range):
sleep_time = random.randint(delay_range[0], delay_range[1])
print(f"Sleeping for {sleep_time} seconds")
sleep(sleep_time)
def main():
try:
while True:
screenshot()
delay(GRAB_DELAY_RANGE)
except KeyboardInterrupt:
print("Nope")
main()
except Exception as e:
print(e)
with open(LOG_FILE_PATH, 'a') as f:
f.write(str(type(e))+str(e)+'\n')
sleep(5)
main()
f = open(LOG_FILE_PATH, 'w+')
f.write('Startup')
f.close()
main()
I need one of the following solutions:
Simply fix the problem
Another way to run a program at startup
A different module to take screenshots with
Any help is appreciated, thanks
If the user that the cron job runs as is also logged in on the console (you mention a reboot, so I'm guessing that you have enabled autologin), then your cron job might work if you also add:
os.environ["DISPLAY"] = ":0"
This worked for me on Ubuntu in a test using cron and a simplified version of your script:
import os
import pyscreenshot as screen
os.environ["DISPLAY"] = ":0"
img = screen.grab()
img.save("/tmp/test.png")
If it doesn't work for you, then you might also have to try setting the value of the XAUTHORITY environment variable to the value found in the environment of the user's interactive processes, which could be extracted using the psutil package, but let's hope this isn't needed.

Bad Display Name when running Python script on boot with Touch Screen

Attempting to run a Python script on boot on Raspberry Pi 3B+ 1GB RAM, Raspbian, with a SunFounder 10" Touch Screen, - .log file returns "Bad display name'
Python script is 100% functional when run via Terminal / executable script / Thonny etc. Attempted to run at boot first via rc.local - created a service, enabled service, daemon-reload... etc. Did not work.
Tried to run as crontab, same result - .log output from crontab shows "Bad display name". Thought it was lack of Display Environment imported and declared within the Python script, so I added that - but on boot returns the same result.
This is the Python Script I'm using
#!/usr/bin/env python3
import RPi.GPIO as GPIO
import os
import sys
import webbrowser
import time
import subprocess
from pynput import keyboard
from Xlib.display import Display
#GPIO Readout
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#GPIO Header Setup
header = 2
GPIO.setup(header, GPIO.IN)
#Omxplayer Commands
Loop = 'omxplayer -b --loop --no-osd -o hdmi /home/pi/Videos/PlanetEarth.mp4 > /dev/null'
Donation = 'omxplayer -b --no-osd -o hdmi /home/pi/Videos/Cartoon.mp4 > /dev/null'
KillPlayer = 'pkill omxplayer.bin'
KillForm = 'pkill chromium'
#Set Display Environment
new_env = dict(os.environ)
new_env['DISPLAY'] = ':0.0'
#Form Handling Required Below
#If Donation is successful, Stop Looping Video, Open Form in Chromium, Wait 60 seconds, Close Chromium, Restart Loop
def PullDownSuccess():
subprocess.Popen(KillPlayer, env=new_env, shell=True)
time.sleep(2)
webbrowser.open('<url>')
time.sleep(60)
subprocess.Popen(KillForm, env=new_env, shell=True)
time.sleep(2)
subprocess.Popen(Loop, env=new_env, shell=True)
#Inception
subprocess.Popen(Loop, env=new_env, shell=True)
#Terminate Loop with Escape Key or Manually Initiate Donation Success
def on_press(key):
if key == keyboard.Key.ctrl:
PullDownSuccess()
if key == keyboard.Key.esc:
subprocess.Popen(KillPlayer, shell=True)
#Keyboard Listener Module
with keyboard.Listener(
on_press=on_press) as listener:
listener.join()
#Donation Successful Do:
while True:
header_state = GPIO.input(header)
if header_state == GPIO.HIGH:
PullDownSuccess()
I am currently attempting to run this script on Boot via crontab with this line:
#reboot (/bin/sleep 10; /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py > /home/pi/Custom_Scripts/cronjoblog 2>&1)
The error log file for crontab returns the following:
raise error.DisplayNameError(display)
Xlib.error.DisplayNameError: Bad display name ""
This error only exists when attempting to run script on boot up. Is the Display overriding the boot display permissions on Boot Up? What is keeping the script from running on the Display on boot up, but not when remotely executed? Thank you for your consideration.
Update: Still no solution. Display environment returns ":0.0' ... so far I have tried to remove
> /dev/null from #Omxplayer Commands
Replacing crontab startup line to:
DISPLAY=":0" /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
and
DISPLAY=":0.0" /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
And any possible combination of these.
Confirmed the script is not waiting for any background processes as I have added delay (time.sleep) up to 30 seconds, as well as returning IP addresses, etc.
Returns either Bad Display Name still OR "Can't connect to display ":0": b'Invalid MIT-MAGIC-COOKIE-1 key"
Still looking for a solution if anyone has one.
EDIT:
Fixed using /LXDE-pi/autostart. Answer below.
Try to add DISPLAY environment variable before calling your script:
DISPLAY=":0" /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
DISPLAY="/dev/null" /usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
Have you tried a longer sleep?
If you try to get your ip adress during your startup script with (for example) :
import socket
def get_ip_address():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]
you will get an errror like:
File "/usr/lib/python2.7/socket.py", line 228, in meth
return getattr(self._sock,name)(*args)
error: [Errno 101] Network is unreachable
This is because at reboot network is not necessarily available. In order to make it available
see https://raspberrypi.stackexchange.com/questions/45769/how-to-wait-for-networking-on-login-after-reboot :
sudo raspi-config
then select option 3 Boot Options
then select option 4 Wait for network at boot
Once you have done that networking should not be an issue at reboot.
You get also check for other ways to enforce network setup before running your script take a look at :
https://askubuntu.com/questions/3299/how-to-run-cron-job-when-network-is-up
If there are unforeseen ramifications for using this method, I will update this thread accordingly. I fixed this issue just by adding two lines to the autostart file which resides in /etc/sdg/lxsession/LXDE-pi/autostart.
sudo nano /etc/xgd/lxsession/LXDE-pi/autostart
Add these two lines, do not modify the existing code.
sleep 5
#/usr/bin/python3 /home/pi/Custom_Scripts/<script>.py
My autostart file looks like this:
#lxpanel --profile LXDE-pi
#pcmanfm --desktop --profile LXDE-pi
#xscreensaver -no-splash
point-rpi
sleep 5
#usr/bin/python3 /home/pi/Custom_Scripts/<script>/py
Ctrl + O to write file
sudo reboot

How to exit function with signal on Windows?

I have the following code written in Python 2.7 on Windows. I want to check for updates for the current python script and update it, if there is an update, with a new version through ftp server preserving the filename and then executing the new python script after terminating the current through the os.kill with SIGNTERM.
I went with the exit function approach but I read that in Windows this only works with the atexit library and default python exit methods. So I used a combination of the atexit.register() and the signal handler.
***necessary libraries***
filematch = 'test.py'
version = '0.0'
checkdir = os.path.abspath(".")
dircontent = os.listdir(checkdir)
r = StringIO()
def exithandler():
try:
try:
if filematch in dircontent:
os.remove(checkdir + '\\' + filematch)
except Exception as e:
print e
ftp = FTP(ip address)
ftp.login(username, password)
ftp.cwd('/Test')
for filename in ftp.nlst(filematch):
fhandle = open(filename, 'wb')
ftp.retrbinary('RETR ' + filename, fhandle.write)
fhandle.close()
subprocess.Popen([sys.executable, "test.py"])
print 'Test file successfully updated.'
except Exception as e:
print e
ftp = FTP(ip address)
ftp.login(username, password)
ftp.cwd('/Test')
ftp.retrbinary('RETR version.txt', r.write)
if(r.getvalue() != version):
atexit.register(exithandler)
somepid = os.getpid()
signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))
os.kill(somepid, signal.SIGTERM)
print 'Successfully replaced and started the file'
Using the:
signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))
I get:
Traceback (most recent call last):
File "C:\Users\STiX\Desktop\Python Keylogger\test.py", line 50, in <module>
signal.signal(SIGTERM, lambda signum, stack_frame: exit(1))
NameError: name 'SIGTERM' is not defined
But I get the job done without a problem except if I use the current code in a more complex script where the script give me the same error but terminates right away for some reason.
On the other hand though, if I use it the correct way, signal.SIGTERM, the process goes straight to termination and the exit function never executed. Why is that?
How can I make this work on Windows and get the outcome that I described above successfully?
What you are trying to do seems a bit complicated (and dangerous from an infosec-perspective ;-). I would suggest to handle the reload-file-when-updated part of the functionality be adding a controller class that imports the python script you have now as a module and, starts it and the reloads it when it is updated (based on a function return or other technique) - look this way for inspiration - https://stackoverflow.com/a/1517072/1010991
Edit - what about exe?
Another hacky technique for manipulating the file of the currently running program would be the shell ping trick. It can be used from all programming languages. The trick is to send a shell command that is not executed before after the calling process has terminated. Use ping to cause the delay and chain the other commands with &. For your use case it could be something like this:
import subprocess
subprocess.Popen("ping -n 2 -w 2000 1.1.1.1 > Nul & del hack.py & rename hack_temp.py hack.py & hack.py ", shell=True)
Edit 2 - Alternative solution to original question
Since python does not block write access to the currently running script an alternative concept to solve the original question would be:
import subprocess
print "hello"
a = open(__file__,"r")
running_script_as_string = a.read()
b = open(__file__,"w")
b.write(running_script_as_string)
b.write("\nprint 'updated version of hack'")
b.close()
subprocess.Popen("python hack.py")

Python - how to run the application and leave until it end?

How can i run the application and leave instead of waiting when it will be ended? for example : /var/tmp/runme.sh &
Following code is working but it waits forever when Google Chrome will exit. how can i run Google Chrome but let my script exit?
import subprocess
import sys
import time
line = sys.argv[1]
print line
def kill_chrome():
subprocess.call(['Taskkill', '/IM', 'chrome.exe', '/F'])
def run_chrome():
subprocess.call(['C:/Program Files (x86)/Google/Chrome/Application/chrome.exe', '--kiosk'])
def run_java():
subprocess.call(['java', '-cp', 'C:/Python27/pdfbox-app-2.0.0-RC3.jar;C:/Python27/jprint.jar', 'JPrint'])
try:
if line.startswith("myjava:website"):
print "Google Chrome - IDLE"
run_chrome()
elif line.startswith("myjava:a4"):
print "Printing - JAVA"
run_java()
elif line.startswith("myjava:kill"):
print "Killer"
kill_chrome()
except Exception, err:
print (err)
pass
#time.sleep(2)
What about subprocess.Popen? Looks like it does exactly what you want - runs app end does not waiting. You also pass arguments to the runned application.
from subprocess import Popen
Popen( [ "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe", "myarg"])
Without "myarg" it works too.

Categories

Resources