19

I have a server that it is normally switched off for security reasons. When I want to work on it I switch it on, execute my tasks, and shut it down again. My tasks usually take no more than 15 minutes. I would like to implement a mechanism to shut it down automatically after 60 minutes.

I've researched how to do this with cron, but I don't think it's the proper way because cron doesn't take into account when the server was last turned on. I can only set periodic patterns, but they don't take that data into account.

How could I do this implementation?

11
  • 4
    Have a look at at (one-time execution).
    – dirkt
    Commented Oct 2, 2018 at 10:14
  • 15
    I haven't done this, but your login script could start a sudo shutdown -h +60 which would start a countdown counter of 60 mins for the shutdown (-halt) process. If you wanted to cancel it, you could sudo shutdown -c (this doesn't use cron though)
    – guiverc
    Commented Oct 2, 2018 at 10:20
  • 7
    Depending on the nature of your tasks maybe it's worth running them inside a docker container, so you don't need to worry about switching off a server. The container is created, it runs your tasks, and it's destroyed after that. Commented Oct 2, 2018 at 11:50
  • 6
    Don't expect any real security benefit from this. Most vulnerabilities can be exploited in less than a second, so powering down after an hour isn't going to prevent exploitation. And an attacker who found a vulnerability can wait for you to power on the machine in order to perform the attack.
    – kasperd
    Commented Oct 4, 2018 at 10:30
  • 3
    Re: @kasperd 's comment, I'd rephrase it as saying that you need exactly the same amount of security on your server regardless of how much or little it is actually online. Note, it's a server so it connected to something and that means something will know when it's turned on - automated sweeps aren't what you worry about, it's connections of any kind and you can't avoid that so max security all the time is the only way. Commented Oct 4, 2018 at 16:44

10 Answers 10

70

There are a several options.

  • Provide time directly to shutdown -P:

    shutdown -P +60
    

    Note that shutdown man page also points out:

    If the time argument is used, 5 minutes before the system goes down the /run/nologin file is created to ensure that further logins shall not be allowed.

  • Use at command.

  • Create a systemd unit file or init script which runs shutdown -P 60 at startup.

  • Use cron's @reboot to run command after boot.

    Add to (root) crontab:

    @reboot shutdown -P +60
    

For the last two methods, you could also use sleep 3600 && shutdown -P now instead of using time argument to shutdown to delay the shutdown for 60 minutes. This way logins are possible to the last moment before shutdown is issued.

2
  • 2
    I think with the sleep command, you still need to specify a time to shutdown - we'd want to use now for that argument if we've already done the waiting (but note that you won't get very much warning to save your work before it disappears from under you!). Commented Oct 3, 2018 at 12:14
  • 1
    Although shutdown -P 60 works it is not the way how the command should be invoked. According to the man page you should use shutdown -P +60. --- Also shutdown without a time argument (in the example sleep 3600 && shutdown -P) will be delayed by one minute (like shutdown -P +1). As Toby Speight wrote you would probably want to use shutdown -P now. Commented Oct 4, 2018 at 9:14
33

This looks like an XY problem.

My tasks take usually no more than 15 minutes. I would like to implement a mechanism to shutdown it automatically after 60 minutes.

If you shut down after 60 minutes, you run the risk that you may be running a particularly complicated problem and just need to have more time. Many of the previous solutions would not make it easy to delay the shutdown.

If the task is not an interactive task, but instead are a scripted task that is automatically triggered from another machine, @sdkks gave a great solution for that; you really should just task the machine to run poweroff as soon as the script and all its tasks finishes.

However, if your task is an interactive task, I'd suggest to do idle detection instead.

If you do your task in a GUI (X11) you can detect the idle GUI sessions using the approach described here: Run a command when system is idle and when is active again

If you do the task through a terminal, you can detect logged in users using the who command. You can set up a cronjob that shuts the machine down if who returns an empty result. Note that this will be quite a conservative approach; it will not shut down the system if you left the console connected, but idle.

If you want to be a bit more aggressive and disconnect idle terminal sessions as well, you can combine the previous approach with automatically disconnecting idle SSH sessions ClientAliveInterval and ClientAliveCountMax. Another approach for this if you don't have SSH, but have a local terminal session, is to use the terminal idle time as returned by the w command.

2
  • 2
    This is a very important point. Whatever you implement - it's in your interest to make sure that you can also cancel the shut down.
    – Shadow
    Commented Oct 2, 2018 at 23:01
  • 1
    Something has gone wrong if it is still running at 60m, so maybe just start again. This can very expensive cloud-wise. Not ensuring a shutdown on one occasion set me back over $4,000.
    – mckenzm
    Commented Oct 4, 2018 at 22:06
22

If you execute your tasks as the same user every time, you can simply add the shutdown command, optionally with the option -P, to your profile. The number stands for the amount of minutes the shutdown command is delayed. Make sure your user has the ability to execute the shutdown command via sudo without a password.

echo "sudo shutdown -P +60" >> ~/.profile

Added:

After reading most of the other answers I love the option to add "@reboot /<path to script/command>" in (root) cron. Answer given by sebasth as his fourth option. You may either add the shutdown command or write a small script that executes the shutdown and execute that via cron. WoJ also has a great solution if you use systemd.

11
  • 11
    The time is in minutes, not seconds. Try shutdown -k -P 3600 vs shutdown -k -P 60 (-k prints wall message but doesn't have other effect)
    – sebasth
    Commented Oct 2, 2018 at 12:05
  • 12
    But try the command one time first, you don't want an instant shutdown to be bound to your profile! Commented Oct 2, 2018 at 12:38
  • 17
    I imagine every ssh login would cause a new call of shutdown. Would the counter reset then?
    – JoL
    Commented Oct 2, 2018 at 15:19
  • 4
    It might also be worthwhile to mention that logins will not be allowed in the last 5 minutes, as mentioned in the shutdown manpage.
    – JoL
    Commented Oct 2, 2018 at 15:22
  • 7
    Note that 60 minutes after login is not the same as 60 minutes after boot. What if you boot the machine and then forget to login? Commented Oct 3, 2018 at 12:20
8

Although previous answers here all satisfy the requirement to perfection, you can also make your machine power off as soon as the tasks are done.

bash scripts can be trapped, meaning certain signals can be intercepted and certain tasks can be executed when needed. EXIT is one of the signals that can be trapped.

You would be able to:

  1. Set a trap for EXIT of your automated shell scripts, meaning termination of your automated tasks
  2. Set a trap for your .bashrc EXIT, meaning whenever you log out of that machine, power it off.

Option #1 would be the ideal case, provided your tasks don't require adhoc inspection and manual judgement.

Option #2 would cover the cases where you'd forget to exit the terminal without powering off. There is a caveat though; if you have multiple terminals to the same machine open and you exit from one of them, it will still power off the machine all the same. (It can be scripted to avoid that, but I won't complicate the solution.)

cleanup(){
    # Do some tasks before terminating
    echo oh la la, cleaning is so nice
    echo "See you later, world"
    sudo poweroff & # finally shutdown
}
trap cleanup EXIT

This can be at the end of .bashrc for option #2, at somewhere at the top of your script for option #1.

Why not use poweroff at the end of the script?

I prefer to use set -eo pipefail at on the top of my scripts. If any error happens, it won't silently fail; it will stop executing more commands. trap of EXIT signal should cover the cases where the script terminates prematurely due to errors.

However for your tasks, this might also mean the machine will shut down before they are completed.

I have a simple bash template I use for making scripting easier to debug; maybe it might be of some use. Please see this gist.

3
  • Agreed, and to be sure maybe a time of day shutdown from cron. This is a dead man's brake.
    – mckenzm
    Commented Oct 4, 2018 at 22:05
  • @Abigail for script, it has to be above, for .bashrc I’d prefer the bottom in case something else overwrites it. Your concern doesn’t seem clear, can you share an example snippet on repl.it maybe?
    – user80158
    Commented Oct 5, 2018 at 0:27
  • @mckenzm I don’t understand. Can you share sample code or more explanation?
    – user80158
    Commented Oct 5, 2018 at 0:28
5

The following runs command with parameters, and if it completes successfully it will turn off the box immediately, assuming the user can run poweroff. It may need to use sudo poweroff.

command --parameters && poweroff

Whereas the following one simply runs poweroff immediately when the command terminates:

command --parameters ; poweroff

If you think the command needs rest time after completion, run

command --parameters ; sleep 3600 ; poweroff

If you think the command might run overtime you can constrain it to an hour:

timeout 1h command --parameters ; poweroff

timeout is part of the coreutils package so you probably already have it.

4

Elaborating on @dirkt comment, you can insert an at command on your .bashrc or .profile or whichever file your shell uses on login to schedule an automatic shutdown 60 minutes after your login.

Something like:

at now + 60 minutes -f /sbin/halt
3
  • This task is right up at’s alley. You can also see at tasks in /var/spool/
    – user80158
    Commented Oct 2, 2018 at 11:54
  • 5
    I would advice against using at in this context because it survives reboots. If OP were to log in, manually shutdown and then log in again in the span of an hour, he'd get kicked off before an hour passed after his last login when the first scheduled shutdown proc.
    – Aaron
    Commented Oct 2, 2018 at 12:27
  • @Aaron that’s true. Haven’t thought of that. Boot persistence is a caveat for this solution.
    – user80158
    Commented Oct 2, 2018 at 12:36
3

If you're really concerned about security, use an mechanical outlet timer on the power source. Just set it to whatever time you want it to shutdown. This way no one can login remotely and disable the shutdown. You would need physical access.

9
  • 2
    Filesystem corruption?
    – Xen2050
    Commented Oct 3, 2018 at 2:34
  • @Xen2050 not a problem on any modern (journaling) filesystem.
    – Tom
    Commented Oct 3, 2018 at 9:31
  • 1
    @Tom Do you have a source for forced umounts will never cause filesystem corruption if there's journaling? Sounds new to me, I'd like to read up on it. At the very least it must mark the filesystem as dirty, forcing a fsck right?
    – Xen2050
    Commented Oct 3, 2018 at 10:02
  • 1
    In most cases, it should just replay the journal and be done with it. A powerdown should not cause filesystem corruption on a journaled filesystem, though there are no guarantees unless you use a filesystem such as ZFS which is specifically designed for this case.
    – Tom
    Commented Oct 3, 2018 at 11:06
  • 1
    Journal replay can fail - I had a very difficult fsck session a couple of weeks ago following a ½-second power cut to a machine with no UPS. That was using ext4 with a journal; it's not completely invincible! Commented Oct 3, 2018 at 12:24
1

Try to put the script (sudo shutdown -P 3600) in the /etc/init.d directory to run it automatically at startup.

Or try using anacron, adding the command in the /etc/anacrontab file. I also suggest to use nohup in front of the command to be sure that the command is still working after logout.

3
  • 1
    Depending on distro, it might not get executed by just being in init.d
    – user80158
    Commented Oct 2, 2018 at 11:50
  • @sdkks, you're right. Anacron can be also considered. putting the script in /etc/anacrontab, also used with 'nohup' in the event of logout.
    – Saveriofr
    Commented Oct 2, 2018 at 11:56
  • 1
    @Saveriofr You can edit your answer to improve its quality (better than posting additional info as a comment). Commented Oct 2, 2018 at 12:01
1

If you are in a systemd machine, you can use a monotonic timer

The timer unit /etc/systemd/system/shutdown_after_an_hour.timer

[Unit]
Description=shutdown after an hour

[Timer]
OnBootSec=1h

[Install]
WantedBy=timers.target

The timer unit /etc/systemd/system/shutdown_after_an_hour.service:

[Unit]
Description=shutdown after an hour

[Service]
ExecStart=/sbin/poweroff --force --no-wall
Type=oneshot

It is enabled via

# systemctl enable shutdown_after_an_hour.timer

Its status (particularly how much time is left before the shutdown) is available via

# systemctl list-timers shutdown_after_an_hour.timer

It will work on the next reboot, it is not useful to systemctl start it during the session when it is created as it will either not work (because it was not triggered during the reboot) or shut down the machine right away if past one hour. I do not know which one will happen as a matter of fact, I have never tested that specific case.

0
1

Leverage Your Shell's Logout File

If you only need the server for interactive or non-interactive tasks started by your UID, consider putting your shutdown commands into your ~/.bash_logout file. The GNU Bash Manual says:

When an interactive login shell exits, or a non-interactive login shell executes the exit builtin command, Bash reads and executes commands from the file ~/.bash_logout, if it exists.

So, adding "sudo poweroff" or similar to your logout file would power off the server as soon as you log out of your current session, or when a non-interactive Bash session calls exit. You could also choose to use at or pass a time-delay to shutdown if you would prefer to defer the shutdown.

For a more interactive approach to this, you might put the following Bashisms into your logout file:

# Shutdown unless N or n is pressed within 30 seconds.
shopt -s nocasematch
read -t 30 -N 1 -p 'Shutdown now? (Y/n) '
[[ "$REPLY" =~ n ]] || sudo poweroff

Caveats

There are some caveats you should keep in mind about this solution:

  • This will probably work even under a terminal started by X Windows, but probably wouldn't work for an X11 session that wasn't launched by startx or similar.
  • If you have non-interactive scripts that call exit, you may experience shutdowns you aren't expecting.
  • Your sudoers file may prompt for a password if you haven't specified NOPASSWD for the shutdown commands, or if you haven't called sudo -v before running your shutdown command.
  • There are probably other edge cases I haven't thought of yet.

In short, you might want to consider whether this approach meets your actual need, or whether a completely different approach such as monitoring for an absence of logged-in users or some other alternative is a better bet. Your mileage will definitely vary.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .