2

I have written a python program that continuously parses my current IP and emails it to me if it changes that I want to begin running as soon as the computer boots up: /home/pi/sendIP.py

#note that unnecessary comments and sensitive information have been stripped
#assume this code works as intended
import smtplib
import os
import re
import time
def mail(message): try: server = smtplib.SMTP('smtp.gmail.com', 587) server.ehlo() server.starttls() server.login('from_email', 'password') server.sendmail('from_email', 'to_email', '\nIP: ' + str(message)) server.close() return True except: return False
def getStringIP(): try: IP = re.search(r'((?:\d{1,3}.){3}\d{1,3})', os.popen('hostname -I').read()).group(1) return IP except: return "null"
ip = ""
while(True): try: newIP = getStringIP() while(newIP == "null"): time.sleep(1) newIP = getStringIP() if(ip == newIP): time.sleep(1) else: ip = newIP while(not mail(ip)): time.sleep(0.5) time.sleep(1) except: time.sleep(2)
This file is run by /home/pi/sendIP.sh

#!/bin/bash
echo "calling sendIP.py"
/usr/bin/python3 /home/pi/sendIP.py&
echo "called in separate process"

sendIP.sh is in turn called by sendIP.service when the service is started manually or on bootup. If I call sendIP.sh directly by doing sudo bash sendIP.sh in a terminal window, it works perfectly and I receive an email with my current local IP. If, however, I do sudo systemctl start sendIP.service, it does not run the python file. Here is the .service file: /etc/systemd/system/sendIP.service

[Unit]
Description=Sends IP to my cellphone
After=all

[Service] ExecStart=/bin/bash /home/pi/sendIP.sh StandardOutput=inherit StandardInput=inherit User=pi
[Install] WantedBy=multi-user.target

And here is the result of sudo systemctl status sendIP.service after attempting to start the service:

● sendIP.service - Sends IP to my cellphone
   Loaded: loaded (/etc/systemd/system/sendIP.service; enabled)
   Active: inactive (dead) since Sun 2018-05-06 20:10:45 UTC; 1min 40s ago
  Process: 9830 ExecStart=/bin/bash /home/pi/sendIP.sh (code=exited, status=0/SUCCESS)
 Main PID: 9830 (code=exited, status=0/SUCCESS)

May 06 20:10:44 raspberrypi systemd[1]: Started Sends IP to my cellphone. May 06 20:10:45 raspberrypi bash[9830]: calling sendIP.py May 06 20:10:45 raspberrypi bash[9830]: called in separate process

It looks like it runs, as it echoes the text from the .sh file, but I do not receive any email as I should... I tried using print() statements in my python file to see if it even begins execution, but it does not print anything to my terminal window—including the echo statements, which I can only see when I check the status of the service (I am using ssh to access it, I don't know if it opens another window and prints there but it shouldn't). Running sendIP.sh directly does print both echo statements in the .sh file and and any print statements in the python program to the console, even with the & character causing sendIP.py to execute in another process. I should admit that I do not really know what I am doing with the .system file configuration, that part is mostly just what I have pieced together from similar questions and responses. If anyone could help me out that would be great! Thank y'all so much!

1
  • Also this is my first post on this site so if there are any formatting guidelines I am not following or I haven't provided any general information typically included in a question such as this let me know so I can update this post. Commented May 6, 2018 at 20:52

2 Answers 2

3

To show how it works I have made a stripped down test script that simply sends an email every minute. Here is the script:

rpi ~$ cat >/home/pi/mySendMail.py <<EOF
#! /usr/bin/python3

import time
import smtplib

def mail(message):
    try:
        server = smtplib.SMTP('smtp.gmail.com', 587)
        server.ehlo()
        server.starttls()
        server.login('from_email', 'password')
        server.sendmail('from_email', 'to_email', str(message))
        server.close()
        return True
    except:
        return False

while(True):
    fh = open("/tmp/mySendMail.log","a")
    if(mail("hello world")):
        fh.write("sended email at "+time.asctime(time.gmtime())+"\n")
    else:
        fh.write("error sending email at "+time.asctime(time.gmtime())+"\n")
    fh.close()
    time.sleep(60)
EOF

Test it on the command line. You should get emails.

rpi ~$ chmod u+x mySendMail.py
rpi ~$ ./mySendMail.py
rpi ~$ cat /tmp/mySendMail.log
sended email at Thu May 10 00:43:14 2018
sended email at Thu May 10 00:44:14 2018
sended email at Thu May 10 00:45:14 2018

Terminate with <ctrl>C.

Now we make a service for this script. The only condition is that it can only start After network is up and running so we can send emails. For this we have the network-online.target [3]. Create the new service with:

rpi ~$ sudo systemctl edit --force --full mySendMail.service

Insert this text in the editor save it and quit the editor:

[Unit]
Description=My Send Mail Service
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi
ExecStart=/home/pi/mySendMail.py

[Install]
WantedBy=multi-user.target

Check the service, enable and start it:

rpi ~$ systemctl status mySendMail.service
● mySendMail.service - My Send Mail Service
   Loaded: loaded (/etc/systemd/system/mySendMail.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

rpi ~$ systemctl cat mySendMail.service
rpi ~$ sudo systemctl enable mySendMail.service
rpi ~$ sudo systemctl start mySendMail.service

Now you should receive every minute an email. Stop it with:

rpi ~$ sudo systemctl stop mySendMail.service

You can edit the service with:

rpi ~$ sudo systemctl edit --full mySendMail.service

Reboot your raspi and it will start sending emails after boot up.


references:
[1] man systemd.unit
[2] How To Use Systemctl to Manage Systemd Services and Units
[3] Running Services After the Network is up
[4] systemd bootup
[5] systemd System and Service

3
  • Thanks so much! I’ll give it a shot and post the results tomorrow. The network-online.target part just says don’t start until it’s connected to the internet, yeah? If it boots up offline and then a few minutes later is connected to the internet will the service just start once it’s connected? Sorry, these are kind of easy questions I just have yet to find a good tutorial on services that like really explains what’s going on. Commented May 10, 2018 at 2:28
  • @ArikRundquist I have updated my answer with references. Quote ref[3]: "network-online.target is a target that actively waits until the nework is "up", Systemd is working parallel to honor modern cpus with many cores. All units (services) will start at the same time if there are no dependencies defined. Its order is undefined. One unit could start earlier but run longer than another. Look at man systemd.unit section [Unit] Before/After=. Having this in mind is very important to understand what's going on. Look at ref[4]. To get in this, you can look at ref[5].
    – Ingo
    Commented May 10, 2018 at 8:45
  • Just a note to future readers of this question: the “systemctl edit” command did not work for me, however using nano to create the .service file in the proper directory does the same thing. Also, I discovered that using & to run in a separate process was what was breaking the script, I’m not sure why but removing it made the whole thing work as I had wanted. Thanks everybody for y’all’s help! Commented May 11, 2018 at 2:34
0

This question is not Pi specific, and is a general Linux question, but probably wouldn't get a helpful response as asked on U&L

As I understand it you are trying to create a systemd service, which runs a bash script, which runs a python script, which in turn gets the output of a system command.

The objective seems to monitor if your IP address changes (why do you think it might?) and notify you of the IP address. Why you would want to do this, or even why you want to know the IP address is unclear, but it is possible.

The bash script contains a spurious '\' in the shebang and contains echo (which will do nothing - as if successful will be run by root). If you want output write to a log file.

The above step is unnecessary, python is a scripting language, so you could just run a python script with shebang #! /usr/bin/env python3, or better call python3 from your service.

The systemd service is unusual, to say the least. I am unsure what After=all means, but I am not a systemd expert, and writing systemd services is complex.

The python script is an endless loop which runs every few seconds. This is wasteful - if you want to do this you would be better to use crontab to run a script every few seconds, rather than the complex steps you are attempting.

3
  • The backslash in the bash script is not actually in the file, the # character threw off the formatting so I added the backslash—this seemed to fix the issue but it looks like when I went back through to change some of the format styles it decided to render the character instead of using it to “escape” the #. I’ll edit my question. As for monitoring my IP, eventually I want to use my script to monitor my router’s dynamic IP so that I can use my pi as a server without paying for a domain name...I’m trying to hack the system but hey that’s what the pi is for! Thanks for the advice to use crontab! Commented May 7, 2018 at 1:57
  • "to monitor my router’s dynamic IP" - then you are in for a new learning experience. The router will allocate a private IP, e.g. 192.168.1.123 then NAT to external IP. PS Most routers DO NOT change IP; they allocate the previous value.
    – Milliways
    Commented May 7, 2018 at 2:45
  • I was under the impression that ISPs assign temporary IPs to each router and each router then directs to the proper local IP. I was planning on trying to figure out how to use the external public IP of my router plus the internal local IP of my pi to access it as a server from anywhere, but that might not be the way it works. If not I’ll just use the router’s external IP and an unused port to forward incoming traffic on that port to my pi. Commented May 7, 2018 at 3:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.