Shutdown script fails to start via Cron job after change

TrueNAS-13.0-U6.2
I have a shutdown script used for over 10 years with no issues and have read that using midclt is better so I changed line 108 on the script. Script works, but now Cron will not start the script, even after reboot. Cron job is set to run the script every 11 minutes.

With midclt changes:
1-Cron will not start script.
2-Manual start of the script via Cron with ‘Run Now’ runs and TN shuts down.
3-Manual start of the script via console runs and TN shuts down.

If I replace the line 108 back to original, Cron will run the script and PC will shut down as normal.

If It runs manually why can’t Cron start it after the change to midclt?

I’ve included the full script. It’s line 100 since I removed my settings.
shutdown.py.txt (5.9 KB)

That must be murder on your uptime. :grinning:

Nothing obvious. You may want to change your log.info line above your changes?

I don’t understand? Every 11 minutes it checks the script for IPs. If no IP address are active TN shutsdown.

Line 107? How do I do that, I just change scripts I don’t actually know anything about writing them. So I wouldn’t know what to put there.

First, on CORE if you want to call the middleware shutdown, you can just use the service command to call the rc.d script which calls the middleware service ix_shutdown stop.

Second, I have absolutely no idea if this would call the system–besides the middleware–to actually shutdown as well (probably doesn’t) but, I see no point in calling anything other than shutdown -p now because the whole point for having the rc.d script is so the system knows how to shut down the middleware–or more accurately: “tell it to cleanup and close shop because the system is shutting down”.

I would have to dig through the code for this “middleware shutdown” thing if I actually wanted to issue a shutdown. I wouldn’t blindly follow a directive like that without researching it a bit more.

Sorry John but I have absolutely and unequivocally no idea what you are talking about, yet alone why Cron won’t start the script after I replace the shutdown -p now with midclt unless I manually start it.

Never assume I know what I’m doing.

Use the same line you’ve used for 10 years. The other line is for SCALE (the middleware call thing makes very little sense on CORE).

Do you have a Solution for this? I have the same problem. My script worked under core for years without problems, but now under scale it wont anymore.

It depends, are you running the same script as mine from https://gehrcke.de/2013/08/freenas-simple-auto-shutdown-revisited/

If you are, it has two things that need to be changed to work on SCALE one is to convert the Ping function from BSD to Linux, mainly the ‘-o’ It does not exist in Linux Ping. The second is the shutdown command itself. Scale uses ‘mdclt’

There are other possible issues so see notes at the bottom.

Below is my modified working script less IP addresses and log address:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#THIS SCRIPT HAS BEEN MODIFIED TO WORK WITH TRUENAS SCALE ATLEAST TNS 25.X.X
#
# Copyright (C) 2013 Jan-Philip Gehrcke, http://gehrcke.de
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import os
import sys
import time
import logging
from subprocess import Popen, PIPE
from logging.handlers import RotatingFileHandler

# List of hosts to check for being reachable. 
HOSTS_TO_CHECK = [

#	"Add your IP and remove the #",
#   "Add your IP and remove the #",
#   "Add your IP and remove the #",
#   "Add your IP and remove the #",
    ]

# Path to the log file of this script.
logfile_path = "Add you path for the log file"

# Condition for shutdown is that all test hosts have constantly not been
# reachable within a couple of minutes. `REQUIRED_OFFLINE_SECONDS`
REQUIRED_OFFLINE_SECONDS = 300

# `POLLING_INTERVAL_SECONDS` specifies how frequently the list of hosts should
# be checked during the time interval specified above.
POLLING_INTERVAL_SECONDS = 30

# Assert that the required offline time is greater than double the polling interval.
assert REQUIRED_OFFLINE_SECONDS > 2 * POLLING_INTERVAL_SECONDS

def main():
    exit_if_any_host_up()
    log.info("No host is reachable. Poll again, every %s s.", POLLING_INTERVAL_SECONDS)
    deadline = time.time() + REQUIRED_OFFLINE_SECONDS
    deadline_str = time.strftime("%H:%M:%S", time.localtime(deadline))
    while time.time() < deadline:
        log.info('Invoke shutdown if no host comes up until %s.', deadline_str)
        time.sleep(POLLING_INTERVAL_SECONDS)
        exit_if_any_host_up()
    
    # Modified shutdown command
    log.info("'midclt call system.shutdown now' returncode: %s" % run_subprocess(['/usr/bin/midclt', 'call', 'system.shutdown', 'now']))

def exit_if_any_host_up():
    log.info("Pinging hosts, exit program if one is up.")
    for host in HOSTS_TO_CHECK:
        if host_responding(host):
            log.info("Exit program.")
            sys.exit(0)

def host_responding(host):
    log.info("Pinging host '%s'...", host)
    rc = run_subprocess(['ping', '-c', '1', '-W', '5', host])
    if rc == 0:
        log.info("Ping returned with code 0, host is up.")
        return True
    log.info("Ping returned with code %s, host is down.", rc)
    return False

def run_subprocess(cmdlist):
    log.debug("Calling Popen(%s).", cmdlist)
    try:
        sp = Popen(cmdlist, stdout=PIPE, stderr=PIPE)
        out, err = sp.communicate()
    except OSError as e:
        log.error("OSError while executing subprocess. Error message:\n%s" % e)
        sys.exit(1)
    if out:
        log.debug("Subprocess stdout:\n%s", out.decode('utf-8'))
    if err:
        log.debug("Subprocess stderr:\n%s", err.decode('utf-8'))
    return sp.returncode

if __name__ == "__main__":
    log = logging.getLogger()
    log.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    fh = RotatingFileHandler(logfile_path, mode='a', maxBytes=500*1024, backupCount=30, encoding='utf-8')
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    ch.setFormatter(formatter)
    fh.setFormatter(formatter)
    log.addHandler(ch)
    log.addHandler(fh)

    # Set the file permissions to 770 after the log file is created
    os.chmod(logfile_path, 0o770)

    main()

The image below is how I have it setup in Cron, not including the time interval in the script:

Notes:

1- I used DuckDuck AI to convert the files. If you haven’t used before I suggest know how your script works, know what’s broken, then get AI to make simple changes at a time giving you the completed script. Then double check what it did. Treat like you are giving a child instructions, It will get it wrong but simple single changes gets you there quicker.

2- If you are using a script such as *.sh then look at my other post to issues I had with Cron and *.sh scripts: https://forums.truenas.com/t/cron-says-success-but-script-does-not-run/56109

3- Back when I tested Scale 22 I had another shutdown script that worked. Far more complex but with more checks and balances such as checking if a Scrub was in progress. Unfortunately It would not work when I actually migrated over to Scale 25 so I abandoned it for the above. easier to fix simple rather than complex, and this one is all I need anyway.

If your script is different and you do manage to get it two work post it for others as options are good.

Hi Paul,

i had copied your Script Content, but it didnt work. It give every Time the Same Error, when i want to run the Script in GUI:

This ist the Script Content:

The Error also cames in my Script after Migrating to Scale. Dont know what Error Code 14 means. Root can reach and execute the Script without Problems over SSH.

I would glad to be, if you could help me. That the NAS cant automaticly shutdown sucks. Currently i only can use, shutdown at Midnight daily.

I don’t know what to tell you. I just tested my script first; GUI then using Shell and it works as per normal, I then copied the one I posted above without adding IP’s and log address and again it worked fine, with the obvious of no IP’s.

I’m using Scale 25.04.2.6 so try this:

Ensure permissions are at least 770 for your script.

Reboot

Try the GUI again

Try the Shell by entering: sudo python /mnt/address_to_your_script.py

see what happens: