Как заставить systemd правильно управлять сценарием с фоновыми процессами?

Постоянство подключения

Для постоянного подключения требуется поддержка на стороне сервера. Это недоступно на текущем сервере openssh (и не похоже, что будет).

Клиентская часть была реализована много лет назад, известная функция UseRoaming , которая была удалена из openssh несколько недель назад. Эта функция никогда не работала и была одной большой уязвимостью . Возможно, он работал с SSH-сервером appgate.com , но я слышал об этом впервые с этой уязвимостью.

Интервалы активности клиент / сервер работают только в рамках поддержки активности TCP и, конечно, если вы не меняли IP-адрес между режимами приостановки и пробуждения.

Сценарии после подключения к сети

Я бы рассмотрел решение для вашего случая - использовать скрипт ifup (скрипт, который запускается после запуска сетевого интерфейса). Это может выглядеть примерно так в / sbin / ifup-local , где вы можете запустить SOCKS5:

#!/bin/sh
if [[ "$1" == "eth0" ]]
then
  ssh -DPORT alias
else
  #DO_NOTHING
fi

Не забудьте сделать исполняемый файл скрипта sudo chmod + x / sbin / ifup-local . Вы также можете добавить в этот сценарий дополнительные проверки и прочее, различать сетевые интерфейсы или что-то еще. Источник

1
08.02.2019, 21:23
4 ответа

В последних версиях bashкоманда waitимеет параметр -n, который ожидает завершения любого фонового процесса, а затем завершает работу.

Кроме того, по пока неизвестной причине, catвремя от времени прерывался между запуском и ожиданием, но не было объявлено об окончании до wait.Поэтому я добавил команду jobsнепосредственно перед ожиданием, которая, похоже, проверяет, завершился ли catвыход или нет. Если да, то ожидание обращает внимание только на два оставшихся процесса. Если он не завершился, то ожидание заканчивается, когда завершается любой из трех процессов.

Итак, последняя строка waitв моем скрипте заменена на

jobs
wait -n

Если какое-либо из заданий завершается после вызова ожидания, ожидание завершается, systemd убивает все оставшиеся дочерние процессы и перезапускает сценарий.

1
27.01.2020, 23:14

I've found, however, that if one process dies, it doesn't kill everything in that script and restart it. For this application they must all be restarted if any one of them dies.

systemd отслеживает ваш сценарий оболочки, а не его дочерние элементы. Вы бы не хотели, чтобы systemd реагировала на выход дочерних элементов, потому что это приводило бы к перезапуску каждый раз, когда вы запускали команду . Учтите, что если у меня есть сценарий оболочки, который работает...

date

Я только что создал дочерний процесс, он запустился, а затем завершился. Я не хочу, чтобы это вызывало какие-либо действия со стороны моего руководителя процесса.

Если вы хотите, чтобы systemd отслеживал дочерние процессы, создайте отдельный юнит-файл для каждого процесса:

  1. Один блок для настройки и чтения из последовательного порта
  2. Один для/home/user/transport
  3. Один для/home/user/backgroundprogram

Вы можете использовать зависимости systemd, чтобы обеспечить правильный порядок запуска служб (и гарантировать, что если вы остановите одну из них, они все остановятся ), и вы можете использовать директивы EnvironmentFileдля загрузки конфигурации (, например$tty_port)из файла.

Возможно, вы поместите некоторые из ваших команд настройки («Отправить команды на tty-устройство...» )в строку ExecStartPre, или, возможно, они получат свою собственную службу Type=oneshot.

2
27.01.2020, 23:14
#!/usr/bin/env/python3
# POSIX shell and bash < 4.3 doesn't want to do this.
# https://unix.stackexchange.com/questions/285156/exiting-a-shell-script-if-certain-child-processes-exit
#
# If you haven't written python3 before, be aware the string type
# is Unicode (UTF-8).  Python 3.0 aborts on invalid UTF-8.
# Python 3.1 aims to round-trip invalid UTF-8 using "surrogateescape".
# Python 3.2 may accept non-UTF-8 encoding according to your locale.
#...
#
# * Functions should be better tested.
#
# * Doesn't bother killing (and waiting for) child processes.
#   Assumes systemd does it for us.
#   Convenient, but I'm not 100% happy about it.
#
# * Otherwise direct translation of nasty script, e.g. use of "sleep".

import sys
import os
import time

tty_port = "/dev/ttyS0"  # or: tty_port = sys.environ["tty_port"]

def die(msg):
    sys.exit(msg)

# Run program in background
def bg(*argv):
    pid = os.fork()
    if pid == 0:
        # Child process: exec or die
        # Either way, we never return from this function.
        try:
            os.execvp(argv[0], argv)
        except Exception as e:
            # By convention, child always uses _exit()
            sys._exit(e)
        assert False
    return pid

def __fg(*argv):
    pid = bg(*argv)
    (_, status) = os.waitpid(pid, 0)
    return status

# Run program, wait for exit, die if the program fails
def fg(*argv):
    status = __fg(*argv)
    if os.WIFEXITED(status):
        code = os.WEXITSTATUS(status)
        if code != 0:
            die("exit status {} from running {}".format(code, argv))
    elif os.WIFSIGNALED(status):
        die("signal {} when running {}"
           .format(os.WTERMSIG(status), argv))
    else:
        assert False, "Unexpected result from waitpid()"

# Use with care.
# "Any  user input that is employed as part of command should be carefully sanitized, to ensure that unexpected shell commands or command options are  not executed."
#
def bg_shell(cmd):
    return bg("/bin/sh", "-c", cmd)

def fg_shell(cmd):
    return fg("/bin/sh", "-c", cmd)


fg("stty", "-F", tty_port, "115200")

tty_pid = bg("cat", tty_port)
print("\"cat {}\" started as pid {}".format(tty_port, tty_pid))

time.sleep(15)

tty_out = open(tty_port, "w")
def tty_print(msg):
    tty_out.write(msg)
    tty_out.flush()

tty_print("command1")
time.sleep(1)

tty_print("command2")
time.sleep(1)

transport_pid = bg_shell("exec /home/user/transport >/dev/null 2>&1")
print("transport started as pid {}".format(transport_pid))
time.sleep(1)

tty_print("command3")
time.sleep(1)

background_pid = bg("/home/user/backgroundprogram")
print("backgroundprogam started as pid {}".format(background_pid))

(pid, status) = os.wait()

# This could be modified to accept exit code 0 as a success,
# and/or accept exit due to SIGTERM as a success.

if os.WIFEXITED(status):
    die("exit status {} from pid {}".format(os.WEXITSTATUS(status)), pid)
elif os.WIFSIGNALED(status):
    die("signal {} when running {}".format(os.WTERMSIG(status), pid))
else:
    assert False, "Unexpected result from wait()"
1
27.01.2020, 23:14

Если вы можете разбить ваш основной скрипт на отдельные сервисы, вы можете легко решить это так:

В следующем примере у меня есть три службы возрождения, s1, s2 и s3, и я управляю ими всеми как группой через целевой s.target.

Примечание:
Если вы настроите три службы как Requiresв s.target, то, если одна из них выйдет из строя и возродится, все участвующие процессы в этой группе будут перезапущены.
Или, если вы настроите их как Wantsв вашем s.target, тогда, если один из них выйдет из строя и возродится, перезапустится только этот отдельный процесс.




Для каждой службы создайте файл службы s1, s2, s3:

/etc/systemd/system/s1.service:

[Unit]
Description=my worker s1
After=network.target
Before=foobar.service
PartOf=s.target

[Service]
Type=simple
ExecStart=/usr/local/bin/s1.sh
Restart=always

(обратите внимание, :если ваши службы идентичны, вы можете создать один файл s1@.service вместо нескольких файлов. Посмотрите в руководстве экземпляры службы, используя @ и %i.)


Теперь создайте файл основной целевой группы (), для которого требуется s1,службы s2 и s3:

/etc/systemd/system/s.target:

[Unit]
Description=main s service
Requires=s1.service s2.service s3.service
# or
# Wants=s1.service s2.service s3.service

[Install]
WantedBy=multi-user.target

Готово.
Как обычно, вы должны запустить systemctl daemon-reload.

Теперь вы можете запускать свои службы с помощьюsystemctl start s.target
s1, s2 и s3 запускаются.

Вы можете остановить все три службы с помощьюsystemctl stop s.target
s1, s2 и s3 остановлены.

Естественно, вы можете запускать/останавливать/перезапускать/состоять отдельные службы как обычно:
systemctl status s1

Если вы убьете процесс s1, s2 или s3, он автоматически перезапустится (Restart=always ).
Если вы использовали Requires, то все процессы в группе будут перезапущены.

PS :Запустите systemctl enable s.target, если хотите запускать службы при загрузке.

PS :К сожалению, при использовании systemctl вы не можете использовать сокращенное слово "s" для "s.target", как вы можете сделать с "s1" вместо полного ввода "s1.service". Вы должны ввести «s.target», когда хотите управлять группой.

2
27.01.2020, 23:14

Теги

Похожие вопросы