You are not logged in.

#1 2021-05-25 09:51:44

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 8,156
Website

generic "restart" script

Moved to "Scripts, Tutorials & Tips" with the hope that it's now OK.

You give it any process name eg "conky", and it closes down all the running conky instances and starts them up again with the same command line as before, forked off as independent processes.

Why 80 (well, 24) lines to do just that?
1) All duplicates are omitted.
2) No extra "sleep" time - script checks if killed processes are really dead before restarting them.
3) Multiple processes are killed, and waited for, in parallel.
4) If any killed processes are still running after eg 10s, script exits instead of going to restart.
5) Only looks at user-owned processes, no system stuff.

It's based on our current bl-tint2restart, incorporating this.

I've been testing it today and it seems OK but if anyone can see anything weird about it, or ran it and got strange results, or has ideas for improvement, any feedback would be appreciated.

#!/bin/bash
#
#    bl-restart: a script to restart all instances of a process name
#    Copyright (C) 2015 damo    <damo@bunsenlabs.org>
#    Copyright (C) 2015-2016  John Crawley <john@bunsenlabs.org>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

HELP='bl-restart is a script to restart all running instances of
any process name passed

Usage: bl-restart <name>
   or: bl-restart -h|--help

Optional arguments:
    -h | --help  Show this message (no other options are supported at this time)

Each running process is stopped with SIGTERM.

All previously running commands are then re-run, forked and disowned,
with any duplicates removed.
'
wait_timeout=10s

# look for a help option somewhere
for i in "$@"
do
    case "$i" in
    -h|--help)
        echo "$HELP"
        exit 0
        ;;
    esac
done

name=$1

pidof "$name" >/dev/null || {
    echo "$0: no $name processes running"
    exit 0
}

declare -A commands # associative array
tailpids=()
index=1
while read -r pid cmd; do
    commands[$cmd]=C${index} # duplicate commands will be launched only once
    mapfile -d '' -t "C${index}" < "/proc/${pid}/cmdline" # use NULL delimiter to split cmdline into array C$index
    (( index++ ))
    kill -TERM "$pid" || { echo "$0: ERROR: failed to kill $pid" >&2; exit 1;}
    tail -f /dev/null --pid="$pid" & tailpids+=($!) # wait in background for process to die
done <<< "$(pgrep -ax "$name" -u "$USER")"
# an extra "tail" process for each killed process is the price of being able to "wait" for them to close
# "wait" can only wait for children of this script

trap 'exit 1' SIGUSR1
{ sleep $wait_timeout; echo "$0: Some processes still running after $wait_timeout, killing $0" >&2; kill -s SIGUSR1 $$; } & sleep_pid=$!

wait "${tailpids[@]}" # wait till all forked tail commands have terminated

kill "$sleep_pid"
wait "$sleep_pid" >/dev/null # suppress "terminated" message after killing

set -m # enable job control so forked processes are immune to signals from script

declare -n CMD # don't have to specify the reference target yet
for CMD in "${commands[@]}" # CMD will refer to each array name in turn
do
    "${CMD[@]}" >/dev/null 2>&1 &
    disown
    sleep 0.2
done

set +m

EDIT: Updated with changes described here: https://forums.bunsenlabs.org/viewtopic … 02#p115102

Last edited by johnraff (2021-06-12 06:17:46)


...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), idle Twitterings and GitStuff )

Introduction to the Bunsenlabs Lithium Desktop

Offline

#2 2021-05-26 00:59:55

hhh
Meep!
Registered: 2015-09-17
Posts: 12,021
Website

Re: generic "restart" script

^ I assume the idea is this will work on apps like Nautilus that restart themselves? I'm in!

Offline

#3 2021-05-26 01:40:16

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 8,156
Website

Re: generic "restart" script

^Still have to do some (a lot of) testing on more complicated cases like that. Trying to restart things like daemons that restart themselves anyway sounds like getting tangled up...

Original use case I had in mind was simpler things like conky and tint2.
Atm tint2 has a dedicated bl-tint2restart script which seems a bit wasteful, and also used random sleeps of 1s or so to make sure everything had been shut down. Dropping those, and actually waiting for the process to die, saves significant time eg when restarting a bunch of conkies.

Any suggestions of other things that users might frequently want to restart? Let's test them.

EDIT: 'bl-restart thunar' restarted 'thunar --daemon' OK

Last edited by johnraff (2021-05-26 01:58:18)


...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), idle Twitterings and GitStuff )

Introduction to the Bunsenlabs Lithium Desktop

Offline

#4 2021-05-26 02:28:01

hhh
Meep!
Registered: 2015-09-17
Posts: 12,021
Website

Re: generic "restart" script

johnraff wrote:

EDIT: 'bl-restart thunar' restarted 'thunar --daemon' OK

Boo yah! That's the stuff! That's the only daemon BL runs, isn't it? Well, xfce4-notifyd and xfce4-power-manager and I guess nm-applet, none of which auto-restart?

Offline

#5 2021-05-26 02:58:45

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 8,156
Website

Re: generic "restart" script

xfce4-notifyd does auto-restart in fact, but not right away. Kill it, and the next time it's needed it will start up. (That's why there's no need to start it at login.)


...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), idle Twitterings and GitStuff )

Introduction to the Bunsenlabs Lithium Desktop

Offline

#6 2021-06-12 06:10:13

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 8,156
Website

Re: generic "restart" script

Finally got this sorted out - as far as I can tell - this morning, so thought I'd drop it here for future reference. A couple of interesting (if you like this sort of thing) points came up:

1) Get the command lines of running processes, and re-use them. Say you want to see all running conkys, the obvious tool is 'pgrep -a conky':

john@lithium:~$ pgrep -ax -u "$USER" conky
27773 conky -c /home/john/.config/conky/two words/BL-Button-conky.conf
27776 conky -c /home/john/.config/conky/netusers.conkyrc
27784 conky -c /home/john/.config/conky/sysmon.conkyrc
27789 conky -c /home/john/.config/conky/netmon_conkyrc

Chop off the first word with the PID, and the rest should be a re-usable command line, right? But, check out the first one. The conky filepath has a space in it, so 'conky -c /home/john/.config/conky/two words/BL-Button-conky.conf' will not work. neutral
OK, put quotes round the filepath - but how do you define exactly where the quotes should go? Just after '-c ' and up to... the next -something option? Maybe use a cunning regex? It gets messy.

If you've got the pid, though, then /proc/${pid}/cmdline holds the command line, with all the arguments separated by NULL bytes. That's much better than the single string from pgrep because shell strings or filepaths can never contain the NULL byte. We can put the arguments into the elements of an array with mapfile:

john@lithium:~$ mapfile -t -d '' CMD < /proc/27773/cmdline
john@lithium:~$ for arg in "${CMD[@]}"; do echo "> $arg"; done
> conky
> -c
> /home/john/.config/conky/two words/BL-Button-conky.conf

(read also has this -d '' option, which, although '' is an empty string not a nullbyte, sets the delimiter to the nullbyte)

We can do whatever we want with the command line arguments, or run the whole command again just by:

"${CMD[@]}"

No problems with spaces or unusual characters anywhere because all the args are quoted. cool

2) Getting access to a list of arrays. This is trickier than it looks. Say you run through all the conky instances running, and pack all their commands into arrays with mapfile - how do you find those arrays again later in the script? You might want to add an index number like:

i=1
mapfile -t -d '' "CMD$i" < /proc/27773/cmdline

This makes an array called CMD1, fine, but how do you get back to it?
This errors out:

john@lithium:~$ echo "${CMD$i[@]}"
bash: ${CMD$i[@]}: bad substitution

Much head-scratching and web-searching later, bash namerefs:

john@lithium:~$ declare -n cmdref=CMD$i
john@lithium:~$ echo "${cmdref[@]}"
conky -c /home/john/.config/conky/two words/BL-Button-conky.conf

So $cmdref is a reference to the indexed variable name, and that kind of indirection works. smile
https://stackoverflow.com/questions/458 … 3#45091743
http://mywiki.wooledge.org/BashFAQ/006

I'll update the script in the OP incorporating these changes.


...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), idle Twitterings and GitStuff )

Introduction to the Bunsenlabs Lithium Desktop

Offline

Board footer

Powered by FluxBB