Jump to content

How can I control the running of my script?

LewisSpring
Go to solution Solved by LewisSpring,

Hello everybody, I've found a tutorial for SYSTEMD online, and that was the best way of doing it after all.

Thank you so much everybody. I've learnt quite a lot!

Hi there,

 

I need a method to control the running of my Python Script.

I already have a method to trigger the script, but have had no luck making my own control script, especially one that terminates it gracefully (I've not been able to reliably get PID, etc)

I have a script to keep the script alive also (forever.py)

 

I am happy to use bash, python or something else. I am also happy if I need to add a visudo 


Idea:

$> control.ext restart
[Script gracefully kills forever.py]
[Script starts forever.py]

$> control.ext stop
[Script gracefully kills forever.py]

 

I've tried the internet, but had no such luck.

I don't expect anybody to write a full program! If you can point me in the right direction (eg: mainly How to terminate the script) that would help greatly, and i'll work the rest out

Thank you!

UTC/GMT (Except during BST)

Link to comment
Share on other sites

Link to post
Share on other sites

29 minutes ago, TheLewisS1 said:

I already have a method to trigger the script, but have had no luck making my own control script, especially one that terminates it gracefully (I've not been able to reliably get PID, etc)

My thought (and I don't know if this is the "right" way) would be to have the running program periodically check a flag in an external file to see if it should keep running. Based on the starting arguments ("start", "restart", "stop"), set the value of the flag in the file. The only trouble I see is handling multiple threads accessing the same file potentially at the same time.

Main System (Byarlant): Ryzen 7 5800X | Asus B550-Creator ProArt | EK 240mm Basic AIO | 16GB G.Skill DDR4 3200MT/s CAS-14 | XFX Speedster SWFT 210 RX 6600 | Samsung 990 PRO 2TB / Samsung 960 PRO 512GB / 4× Crucial MX500 2TB (RAID-0) | Corsair RM750X | Mellanox ConnectX-3 10G NIC | Inateck USB 3.0 Card | Hyte Y60 Case | Dell U3415W Monitor | Keychron K4 Brown (white backlight)

 

Laptop (Narrative): Lenovo Flex 5 81X20005US | Ryzen 5 4500U | 16GB RAM (soldered) | Vega 6 Graphics | SKHynix P31 1TB NVMe SSD | Intel AX200 Wifi (all-around awesome machine)

 

Proxmox Server (Veda): Ryzen 7 3800XT | AsRock Rack X470D4U | Corsair H80i v2 | 64GB Micron DDR4 ECC 3200MT/s | 4x 10TB WD Whites / 4x 14TB Seagate Exos / 2× Samsung PM963a 960GB SSD | Seasonic Prime Fanless 500W | Intel X540-T2 10G NIC | LSI 9207-8i HBA | Fractal Design Node 804 Case (side panels swapped to show off drives) | VMs: TrueNAS Scale; Ubuntu Server (PiHole/PiVPN/NGINX?); Windows 10 Pro; Ubuntu Server (Apache/MySQL)


Media Center/Video Capture (Jesta Cannon): Ryzen 5 1600X | ASRock B450M Pro4 R2.0 | Noctua NH-L12S | 16GB Crucial DDR4 3200MT/s CAS-22 | EVGA GTX750Ti SC | UMIS NVMe SSD 256GB / Seagate 1.5TB HDD | Corsair CX450M | Viewcast Osprey 260e Video Capture | Mellanox ConnectX-2 10G NIC | LG UH12NS30 BD-ROM | Silverstone Sugo SG-11 Case | Sony XR65A80K

 

Camera: Sony ɑ7II w/ Meike Grip | Sony SEL24240 | Samyang 35mm ƒ/2.8 | Sony SEL50F18F | Sony SEL2870 (kit lens) | PNY Elite Perfomance 512GB SDXC card

 

Network:

Spoiler
                           ┌─────────────── Office/Rack ────────────────────────────────────────────────────────────────────────────┐
Google Fiber Webpass ────── UniFi Security Gateway ─── UniFi Switch 8-60W ─┬─ UniFi Switch Flex XG ═╦═ Veda (Proxmox Virtual Switch)
(500Mbps↑/500Mbps↓)                             UniFi CloudKey Gen2 (PoE) ─┴─ Veda (IPMI)           ╠═ Veda-NAS (HW Passthrough NIC)
╔═══════════════════════════════════════════════════════════════════════════════════════════════════╩═ Narrative (Asus USB 2.5G NIC)
║ ┌────── Closet ──────┐   ┌─────────────── Bedroom ──────────────────────────────────────────────────────┐
╚═ UniFi Switch Flex XG ═╤═ UniFi Switch Flex XG ═╦═ Byarlant
   (PoE)                 │                        ╠═ Narrative (Cable Matters USB-PD 2.5G Ethernet Dongle)
                         │                        ╚═ Jesta Cannon*
                         │ ┌─────────────── Media Center ──────────────────────────────────┐
Notes:                   └─ UniFi Switch 8 ─────────┬─ UniFi Access Point nanoHD (PoE)
═══ is Multi-Gigabit                                ├─ Sony Playstation 4 
─── is Gigabit                                      ├─ Pioneer VSX-S520
* = cable passed to Bedroom from Media Center       ├─ Sony XR65A80K (Google TV)
** = cable passed from Media Center to Bedroom      └─ Work Laptop** (Startech USB-PD Dock)

 

Retired/Other:

Spoiler

Laptop (Rozen-Zulu): Sony VAIO VPCF13WFX | Core i7-740QM | 8GB Patriot DDR3 | GT 425M | Samsung 850EVO 250GB SSD | Blu-ray Drive | Intel 7260 Wifi (lived a good life, retired with honor)

Testbed/Old Desktop (Kshatriya): Xeon X5470 @ 4.0GHz | ZALMAN CNPS9500 | Gigabyte EP45-UD3L | 8GB Nanya DDR2 400MHz | XFX HD6870 DD | OCZ Vertex 3 Max-IOPS 120GB | Corsair CX430M | HooToo USB 3.0 PCIe Card | Osprey 230 Video Capture | NZXT H230 Case

TrueNAS Server (La Vie en Rose): Xeon E3-1241v3 | Supermicro X10SLL-F | Corsair H60 | 32GB Micron DDR3L ECC 1600MHz | 1x Kingston 16GB SSD / Crucial MX500 500GB

Link to comment
Share on other sites

Link to post
Share on other sites

1 minute ago, AbydosOne said:

My thought (and I don't know if this is the "right" way) would be to have the running program periodically check a flag in an external file to see if it should keep running. Based on the starting arguments ("start", "restart", "stop"), set the value of the flag in the file. The only trouble I see is handling multiple threads accessing the same file potentially at the same time.

I suppose that could work. I'd have to have a think where to build this into, since:

  • Forever.py needs to keep an eye on the file and work out if it should relaunch monitor.py if it dies
  • Monitor.py needs to keep an eye on the file and work out if it should quit.

I'm definitely not ruling this out.

UTC/GMT (Except during BST)

Link to comment
Share on other sites

Link to post
Share on other sites

Yeah I think you need to be a little more detailed about what you're trying to do and what the requirements of the "control program" are. There are tons of things that can do control-esque stuff such as:
- cron jobs

- docker containers (esp when run with the --restart [option] flag)

- systemd

Main Rig: R9 5950X @ PBO, RTX 3090, 64 GB DDR4 3666, InWin 101, Full Hardline Watercooling

Server: R7 1700X @ 4.0 GHz, GTX 1080 Ti, 32GB DDR4 3000, Cooler Master NR200P, Full Soft Watercooling

LAN Rig: R5 3600X @ PBO, RTX 2070, 32 GB DDR4 3200, Dan Case A4-SFV V4, 120mm AIO for the CPU

HTPC: i7-7700K @ 4.6 GHz, GTX 1050 Ti, 16 GB DDR4 3200, AliExpress K39, IS-47K Cooler

Router: R3 2200G @ stock, 4GB DDR4 2400, what are cases, stock cooler
 

I don't have a problem...

Link to comment
Share on other sites

Link to post
Share on other sites

21 minutes ago, tarfeef101 said:

Yeah I think you need to be a little more detailed about what you're trying to do and what the requirements of the "control program" are. There are tons of things that can do control-esque stuff such as:
- cron jobs

- docker containers (esp when run with the --restart [option] flag)

- systemd

 

There is a main python script (monitor.py) that is ran by another (forever.py). Forever.py executes monitor.py and relaunches it if it crashes. Forever.py is started by a cron task

I want the control script to be able to

  • Gracefully send an exit command to forever.py (which in turn gracefully exits monitor.py)
  • Start forever.py (which in turn starts monitor.py
     

The script will have two arguments

  • restart - (Both actions above)
  • Stop (Graceful exit command)

The control script will be triggered by TriggerCMD (command trigger software)

 

I have started to look at writing a bash script to achieve these options, but wanted to know if there were any better options.

I also cannot find a easy way to tell forever.py to gracefully quit, which if I go toward using a bash script is a problem.

 

Hope I helped explain my problem

UTC/GMT (Except during BST)

Link to comment
Share on other sites

Link to post
Share on other sites

5 minutes ago, TheLewisS1 said:

 

There is a main python script (monitor.py) that is ran by another (forever.py). Forever.py executes monitor.py and relaunches it if it crashes. Forever.py is started by a cron task

I want the control script to be able to

  • Gracefully send an exit command to forever.py (which in turn gracefully exits monitor.py)
  • Start forever.py (which in turn starts monitor.py
     

The script will have two arguments

  • restart - (Both actions above)
  • Stop (Graceful exit command)

The control script will be triggered by TriggerCMD (command trigger software)

 

I have started to look at writing a bash script to achieve these options, but wanted to know if there were any better options.

I also cannot find a easy way to tell forever.py to gracefully quit.

 

Hope I helped explain my problem

I think you need to start at the beginning: what is the problem statement you're trying to solve with these scripts? This sounds very over-engineered to me. I am fairly confident this can be made simpler and more reliable in doing so.

Main Rig: R9 5950X @ PBO, RTX 3090, 64 GB DDR4 3666, InWin 101, Full Hardline Watercooling

Server: R7 1700X @ 4.0 GHz, GTX 1080 Ti, 32GB DDR4 3000, Cooler Master NR200P, Full Soft Watercooling

LAN Rig: R5 3600X @ PBO, RTX 2070, 32 GB DDR4 3200, Dan Case A4-SFV V4, 120mm AIO for the CPU

HTPC: i7-7700K @ 4.6 GHz, GTX 1050 Ti, 16 GB DDR4 3200, AliExpress K39, IS-47K Cooler

Router: R3 2200G @ stock, 4GB DDR4 2400, what are cases, stock cooler
 

I don't have a problem...

Link to comment
Share on other sites

Link to post
Share on other sites

Just now, tarfeef101 said:

I think you need to start at the beginning: what is the problem statement you're trying to solve with these scripts? This sounds very over-engineered to me. I am fairly confident this can be made simpler and more reliable in doing so.

The monitor script is monitors a serial adapter and acts based on it's output.

The forever script restarts the script if it crashes (though it shouldn't since there is error handling.

 

I want to be able to control the script's running with my phone, so want to use TriggerCMD to restart/stop the script, hence the need for a control script.

UTC/GMT (Except during BST)

Link to comment
Share on other sites

Link to post
Share on other sites

1 minute ago, TheLewisS1 said:

The monitor script is monitors a serial adapter and acts based on it's output.

The forever script restarts the script if it crashes (though it shouldn't since there is error handling.

 

I want to be able to control the script's running with my phone, so want to use TriggerCMD to restart/stop the script, hence the need for a control script.

so both systemd and docker can restart things that fail. and can accept stop commands. i'd suggest using one of those. 

there's a million utilities that exist for remote system monitoring/management that can interact with docker or systemd to do what you want with triggercmd without having to write your own stuff. if you really want to cause that's fun (fair enough, i get it), I'd recommend writing an API with something like flask rather than trying to use some random app. but i do have a personal distaste for downloading and installing things, so i'm sure i'm biased saying that

Main Rig: R9 5950X @ PBO, RTX 3090, 64 GB DDR4 3666, InWin 101, Full Hardline Watercooling

Server: R7 1700X @ 4.0 GHz, GTX 1080 Ti, 32GB DDR4 3000, Cooler Master NR200P, Full Soft Watercooling

LAN Rig: R5 3600X @ PBO, RTX 2070, 32 GB DDR4 3200, Dan Case A4-SFV V4, 120mm AIO for the CPU

HTPC: i7-7700K @ 4.6 GHz, GTX 1050 Ti, 16 GB DDR4 3200, AliExpress K39, IS-47K Cooler

Router: R3 2200G @ stock, 4GB DDR4 2400, what are cases, stock cooler
 

I don't have a problem...

Link to comment
Share on other sites

Link to post
Share on other sites

7 hours ago, TheLewisS1 said:

The monitor script is monitors a serial adapter and acts based on it's output.

The forever script restarts the script if it crashes (though it shouldn't since there is error handling.

 

I want to be able to control the script's running with my phone, so want to use TriggerCMD to restart/stop the script, hence the need for a control script.

That's a rather convoluted way of doing things. Anyways, you could e.g. have your monitor-script and forever-script write their PID to a file (under Linux, this would typically go into /run/user/youruidhere/yourappsname.pid -- you can get the UID with os.getuid() and the current process's PID with os.getpid() ), then your control-script could check if the PID-file exists, then proceed to read the PID from there, check that it points to the correct process and then send it a SIGHUP or SIGTERM to tell it to exit. Remember to delete the PID-file in the control-script, if the process doesn't exist or points to a wrong process, and in forever-script and monitor-script at exit.

 

Here is a script that demonstrates more-or-less everything you need under Linux. Adapt for Windows, if you use that. Save as fancyScript.py :

import os, psutil, signal

def signalHandler(receivedSignal, frame):
        print("Received SIGTERM, exiting.")
        try:
                os.remove(f"/run/user/{os.getuid()}/mySuperFancyExcitingScript.pid")
        except:
                pass
        exit(0)

def main():
        myScriptPid = os.getpid()
        myScriptUid = os.getuid()

        # Write our PID to a file. Also, let me introduce you to Python3's f-strings while at it!
        with open(f"/run/user/{myScriptUid}/mySuperFancyExcitingScript.pid", "w") as pidFile:
                pidFile.write(f"{myScriptPid}")

        try:
                processInfo = psutil.Process(myScriptPid)
                print("PID: ", myScriptPid)
                print("Process-name: ", processInfo.name())
                print("Process cmdline: ", processInfo.cmdline())

                # Check that the PID points to the correct process
                # You'd read the PID from the file here, but we skip that in this demo. You should get the point anyway.
                if(not "fancyScript.py" in processInfo.cmdline()):
                        print("This PID does not point to our script!")
                        exit(1)
        except NoSuchProcess:
                print("The process pointed to by PID does not exist!")
                try:
                        os.remove(f"/run/user/{myScriptUid}/mySuperFancyExcitingScript.pid")
                except:
                        pass
        except AccessDenied:
                print("We don't have access to process-details, cannot continue!")
                try:
                        os.remove(f"/run/user/{myScriptUid}/mySuperFancyExcitingScript.pid")
                except:
                        pass

        # Attach signal-handler for SIGTERM
        signal.signal(signal.SIGTERM, signalHandler)

        # Send ourselves SIGTERM
        os.kill(myScriptPid, signal.SIGTERM)

        # Start a neverending loop to demonstrate that we receive and act on the signal we just sent
        while True:
                pass

# It's a good habit to learn this, even if this isn't strictly necessary at this point.
# It'll come in handy once you start making more complex stuff, including custom libraries.
if(__name__ == "__main__"):
        main()

 

Remember to like, share and subscr...oh, wait.

 

Links:

https://docs.python.org/3/library/os.html

https://psutil.readthedocs.io/en/latest/#processes

https://docs.python.org/3/library/signal.html

Hand, n. A singular instrument worn at the end of the human arm and commonly thrust into somebody’s pocket.

Link to comment
Share on other sites

Link to post
Share on other sites

3 hours ago, WereCatf said:

That's a rather convoluted way of doing things. Anyways, you could e.g. have your monitor-script and forever-script write their PID to a file (under Linux, this would typically go into /run/user/youruidhere/yourappsname.pid -- you can get the UID with os.getuid() and the current process's PID with os.getpid() ), then your control-script could check if the PID-file exists, then proceed to read the PID from there, check that it points to the correct process and then send it a SIGHUP or SIGTERM to tell it to exit. Remember to delete the PID-file in the control-script, if the process doesn't exist or points to a wrong process, and in forever-script and monitor-script at exit.

 

Here is a script that demonstrates more-or-less everything you need under Linux. Adapt for Windows, if you use that. Save as fancyScript.py :



import os, psutil, signal

def signalHandler(receivedSignal, frame):
        print("Received SIGTERM, exiting.")
        try:
                os.remove(f"/run/user/{os.getuid()}/mySuperFancyExcitingScript.pid")
        except:
                pass
        exit(0)

def main():
        myScriptPid = os.getpid()
        myScriptUid = os.getuid()

        # Write our PID to a file. Also, let me introduce you to Python3's f-strings while at it!
        with open(f"/run/user/{myScriptUid}/mySuperFancyExcitingScript.pid", "w") as pidFile:
                pidFile.write(f"{myScriptPid}")

        try:
                processInfo = psutil.Process(myScriptPid)
                print("PID: ", myScriptPid)
                print("Process-name: ", processInfo.name())
                print("Process cmdline: ", processInfo.cmdline())

                # Check that the PID points to the correct process
                # You'd read the PID from the file here, but we skip that in this demo. You should get the point anyway.
                if(not "fancyScript.py" in processInfo.cmdline()):
                        print("This PID does not point to our script!")
                        exit(1)
        except NoSuchProcess:
                print("The process pointed to by PID does not exist!")
                try:
                        os.remove(f"/run/user/{myScriptUid}/mySuperFancyExcitingScript.pid")
                except:
                        pass
        except AccessDenied:
                print("We don't have access to process-details, cannot continue!")
                try:
                        os.remove(f"/run/user/{myScriptUid}/mySuperFancyExcitingScript.pid")
                except:
                        pass

        # Attach signal-handler for SIGTERM
        signal.signal(signal.SIGTERM, signalHandler)

        # Send ourselves SIGTERM
        os.kill(myScriptPid, signal.SIGTERM)

        # Start a neverending loop to demonstrate that we receive and act on the signal we just sent
        while True:
                pass

# It's a good habit to learn this, even if this isn't strictly necessary at this point.
# It'll come in handy once you start making more complex stuff, including custom libraries.
if(__name__ == "__main__"):
        main()

 

Remember to like, share and subscr...oh, wait.

 

Links:

https://docs.python.org/3/library/os.html

https://psutil.readthedocs.io/en/latest/#processes

https://docs.python.org/3/library/signal.html

Hi there! Wow, Thank you so much!

I understand what the script does, but

Could you explain where this script goes? Do I build it into my monitor.py?

Could you also explain how I'd trigger an exit with an command/script?

UTC/GMT (Except during BST)

Link to comment
Share on other sites

Link to post
Share on other sites

1 minute ago, TheLewisS1 said:

Could you explain where this script goes? Do I build it into my monitor.py?

Could you also explain how I'd trigger an exit with an command/script?

You don't use it directly, I just simply demonstrated how to do all the things I mentioned, like e.g. how to send a signal (SIGTERM) to a process and then how to handle a received signal. I explained all this in the same post as where I posted the script.

Hand, n. A singular instrument worn at the end of the human arm and commonly thrust into somebody’s pocket.

Link to comment
Share on other sites

Link to post
Share on other sites

4 minutes ago, WereCatf said:

You don't use it directly, I just simply demonstrated how to do all the things I mentioned, like e.g. how to send a signal (SIGTERM) to a process and then how to handle a received signal. I explained all this in the same post as where I posted the script.

Ah, right. I misunderstood. I'll start piecing it together. Thanks.

UTC/GMT (Except during BST)

Link to comment
Share on other sites

Link to post
Share on other sites

Hello everybody, I've found a tutorial for SYSTEMD online, and that was the best way of doing it after all.

Thank you so much everybody. I've learnt quite a lot!

UTC/GMT (Except during BST)

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×