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?

terdon
  • 242,166
  • 4
    Have a look at at (one-time execution). – dirkt Oct 02 '18 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 Oct 02 '18 at 10:20
  • I might have a solution that will shut it down as soon as your task is done. May I know how you run these tasks, by a bash script? –  Oct 02 '18 at 11:49
  • 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. – Vicente Olivert Riera Oct 02 '18 at 11:50
  • 1
    Have you looked into uptime? You could have a periodic cron job that checks uptime and if the value is > 1hr then initiate init 0. You could also check ps -ef to see if your process is still running. – Patrick Taylor Oct 03 '18 at 05:58
  • Why not shut it down when your tasks are done, rather than after some fixed amount of time? – hmakholm left over Monica Oct 03 '18 at 15:21
  • 1
    Did you actually implement the currently accepted answer or did you just accept the first answer that was posted? It's good form to wait at least 24 hours to give everyone a chance to post, and that will also lead to better answers. – pipe Oct 04 '18 at 08:15
  • 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 Oct 04 '18 at 10:30
  • @kasperd A good point but I feel that you mostly think about targeted attacks. A server that's only turned on one hour every week will be largely invisible to automated "sweeps". – pipe Oct 04 '18 at 11:58
  • @pipe I was thinking both targeted and non-targeted attacks. An attack which scans all of the v4 address space can be repeated over and over again. And since attacks performing such attacks tend to use previously compromised machines to find new victims, it is entirely feasible for them to scan for new targets multiple times per day. – kasperd Oct 04 '18 at 12:02
  • 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. – StephenG - Help Ukraine Oct 04 '18 at 16:44

10 Answers10

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.

sebasth
  • 14,872
  • 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!). – Toby Speight Oct 03 '18 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. – pabouk - Ukraine stay strong Oct 04 '18 at 09: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.

Lie Ryan
  • 1,228
  • 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 Oct 02 '18 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 Oct 04 '18 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.

Dirk Krijgsman
  • 549
  • 2
  • 6
  • 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 Oct 02 '18 at 12:05
  • 12
    But try the command one time first, you don't want an instant shutdown to be bound to your profile! – Fabian Röling Oct 02 '18 at 12:38
  • 17
    I imagine every ssh login would cause a new call of shutdown. Would the counter reset then? – JoL Oct 02 '18 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 Oct 02 '18 at 15:22
  • 1
    I believe this solution will ask you for root password and start a new instance of shutdown every time a new login shell is launched which may or may not be a shortcoming. – undercat Oct 03 '18 at 06:38
  • 2
    @JoL - Yes. Based on some experimenting with shutdown and checking the times in /run/systemd/shutdown/scheduled, it appears that the last shutdown commands overrides all previous shutdowns, regardless of whether the new time is sooner or later than the previous time. So this solution would shut the system down 3600 minutes after the last login, not 3600 minutes after the first login. – Dave Sherohman Oct 03 '18 at 11:32
  • 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? – Hagen von Eitzen Oct 03 '18 at 12:20
  • You can also set the execute bit but not the readable bit on a script that logs in under a special user to run the shutdown command if you don't have sudo without a password. – noɥʇʎԀʎzɐɹƆ Oct 03 '18 at 21:00
  • 1
    @HagenvonEitzen You can do the same by editing the rc.local script. It will get run a few times but it shouldn't matter in case of the shutdown command. Also, see the answer that didn't get picked but has many more upvotes, that will provide a clear how-to. – John Hamilton Oct 04 '18 at 10:56
  • 1
    @sebasth The shutdown manpage says that time can be now, an exact time of day (hh:mm) or number of minutes from now (+m). It doesn't indicate how raw numbers are interpreted, although some implementations (eg. "upstart") treat it the same without the +. – JigglyNaga Oct 04 '18 at 13:29
  • maybe fire this off from @reboot in crontab. – mckenzm Oct 04 '18 at 21:59
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.

  • Agreed, and to be sure maybe a time of day shutdown from cron. This is a dead man's brake. – mckenzm Oct 04 '18 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? –  Oct 05 '18 at 00:27
  • @mckenzm I don’t understand. Can you share sample code or more explanation? –  Oct 05 '18 at 00: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.

Criggie
  • 1,781
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
Daniele Santi
  • 4,137
  • 2
  • 30
  • 30
  • This task is right up at’s alley. You can also see at tasks in /var/spool/ –  Oct 02 '18 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 Oct 02 '18 at 12:27
  • @Aaron that’s true. Haven’t thought of that. Boot persistence is a caveat for this solution. –  Oct 02 '18 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.

gogators
  • 472
  • 2
    Filesystem corruption? – Xen2050 Oct 03 '18 at 02:34
  • @Xen2050 not a problem on any modern (journaling) filesystem. – Tom Oct 03 '18 at 09:31
  • However, a better solution would indeed be to have poweroff system that can send proper SNMP signals for a clean shutdown. – Tom Oct 03 '18 at 09:32
  • 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 Oct 03 '18 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 Oct 03 '18 at 11:06
  • @Xen2050 - The basic mode of operation for journaling filesystems is that they first write a journal entry saying "I'm going to write this data to block X", then write to block X, then delete the journal entry. If power is lost in step 1, it's a bad journal entry and discarded on restart; the write is lost, but the fs is intact. If it's lost in step 2, the journal entry has enough information to complete the write as if nothing had happened. If lost in step 3, the write is repeated, but it's just re-writing the same data to the same block, so no problems. No fsck is needed in any case. – Dave Sherohman Oct 03 '18 at 11:41
  • 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! – Toby Speight Oct 03 '18 at 12:24
  • on a system where security is paramount, you'll probably using a raid device with a battery backup anyways. – gogators Oct 04 '18 at 16:33
  • @gogators Depends on what kind of security you're after. Battery-backed RAID (except RAID 0) can help against some kinds of threats, but be utterly useless or even counterproductive against others. "Security" is too broad a term to be useful in such a context. – user Oct 05 '18 at 14:17
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.

  • 1
    Depending on distro, it might not get executed by just being in init.d –  Oct 02 '18 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 Oct 02 '18 at 11:56
  • 1
    @Saveriofr You can [edit] your answer to improve its quality (better than posting additional info as a comment). – Anthony Geoghegan Oct 02 '18 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.

WoJ
  • 1,545
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.

CodeGnome
  • 7,820