Bash-скрипт, содержащий sudo – ненадежное возобновление в фоновом режиме (bg)

Вопрос или проблема

У меня есть следующий простой скрипт bash (называется test.sh), который показывает использование диска в корневом каталоге. Для списка всех директорий требуется sudo (и меня не запрашивают пароль sudo).

#!/bin/bash
sudo du -h --max-depth=1 / 2> /dev/null

Каталог находится в моем пути, и затем я запускаю скрипт так (чтобы получить вывод в текстовый файл):

$ ./test.sh >> ./test.txt

Теперь, если я приостановлю задачу с помощью Ctrl+Z, я получаю следующее:

^Z
[1]+  Stopped                 ./test.sh >> ./test.txt

Если затем я возобновляю выполнение в фоне с помощью bg, я все равно получаю:

$ bg
[1]+ ./test.sh >> ./test.txt &

[1]+  Stopped                 ./test.sh >> ./test.txt

$ jobs
[1]+  Stopped                 ./test.sh >> ./test.txt

(Дополнительные попытки с bg могут привести к тому, что скрипт действительно возобновит выполнение в фоне после 2-3 попыток, но это кажется случайным…)

Однако, если я возобновляю выполнение с fg, тогда скрипт выполняется на переднем плане:

$ fg
./test.sh >> ./test.txt

И результат записывается в test.txt:

3.8M    /root
4.0K    /authentic-theme
4.0K    /srv
72K     /tmp
3.2G    /snap
4.0K    /media
8.4M    /etc
0       /proc
0       /sys
4.0K    /cdrom
16K     /opt
16K     /lost+found
24K     /dev
4.3M    /run
263G    /mnt
14M     /home
19G     /var
245M    /boot
3.8G    /usr
297G    /

Если я изменяю скрипт так, чтобы не использовать sudo (и вместо этого запускаю скрипт с sudo), то я могу возобновить выполнение в фоне нормально с bg, и скрипт выполняется:

$ sudo ./test.sh >> ./test.txt 
^Z
[1]+  Stopped                 sudo ./test.sh >> ./test.txt

$ bg
[1]+ sudo ./test.sh >> ./test.txt &

$ jobs
[1]+  Running                 sudo ./test.sh >> ./test.txt &

То же самое происходит, если я запускаю всю команду с sudo, но не как скрипт:

$ sudo du -h --max-depth=1 / 2> /dev/null >> ./test.txt
^Z
[1]+  Stopped                 sudo du -h --max-depth=1 / 2> /dev/null >> ./test.txt

$ bg
[1]+ sudo du -h --max-depth=1 / 2> /dev/null >> ./test.txt &

$ jobs
[1]+  Running                 sudo du -h --max-depth=1 / 2> /dev/null >> ./test.txt &

Может ли кто-нибудь объяснить, что здесь происходит? Почему вы можете возобновить команду, использующую sudo, а также скрипт в фоне, но когда скрипт содержит точно такую же команду с sudo, то возобновление выполнения в фоне с bg, по-видимому, не работает корректно?

Я использую Ubuntu 22.04.1 с версией Bash по умолчанию 5.1.16.

Редактирование #1: Я могу сообщить, что у меня настроен alias sudo='sudo ', чтобы команды с sudo могли использовать другие алиасы. Однако я тестировал и с этим алиасом, и без него, и в любом случае получал ту же нерегулярную реакцию bg на возобновление.

Редактирование #2: jobs -l дает следующую нормальную информацию:

jobs -l
[1]+ 1074808 Stopped                 ./test.sh >> ./test.txt

Редактирование #3: Обычно я работаю в tmux, но я также тестировал без tmux, и проблема все еще сохраняется.

Редактирование #4: Помимо моего сервера SuperMicro, у меня также есть Raspberry Pi и виртуальная машина Ubuntu для тестирования на моем ноутбуке (Aorus X5). Вот где становится действительно странно:

  • На моей виртуальной машине Ubuntu (на VMWare под Windows 10) этой проблемы не происходит вообще. Все корректно возобновляется в фоне с bg с первого раза во всех случаях.
  • На моем Raspberry Pi проблема тоже присутствует – обычно требуется 2-3 попытки с bg, чтобы она корректно возобновила выполнение.

Я начинаю думать, что мне нужно тестировать с учетом программного обеспечения, которое работает как на моем основном сервере, так и на моем Raspberry Pi, но не на моей виртуальной машине.

Редактирование #5: Установка stty -tostop перед запуском скрипта, к сожалению, не особо помогает. В большинстве случаев все еще требуется 2-3 попытки, чтобы корректно возобновить выполнение. Несколько раз это удавалось с первой попытки, но я думаю, что это скорее случайность, чем что-то другое.

Редактирование #6: Вот службы, запущенные на моем Raspberry Pi:

$ systemctl --type=service --state=running
  UNIT                             LOAD   ACTIVE SUB     DESCRIPTION                                            
  atd.service                      loaded active running Планировщик отложенного выполнения
  containerd.service               loaded active running контейнерная среда выполнения containerd
  cron.service                     loaded active running Демон регулярной обработки фоновых программ
  dbus.service                     loaded active running Системная шина сообщений D-Bus
  docker.service                   loaded active running Контейнерная среда выполнения Docker
  [email protected]               loaded active running Сеанс на tty1
  irqbalance.service               loaded active running демон irqbalance
  ModemManager.service             loaded active running Менеджер модемов
  networkd-dispatcher.service      loaded active running Демон диспетчера для systemd-networkd
  packagekit.service               loaded active running Демон PackageKit
  polkit.service                   loaded active running Менеджер полномочий
  prometheus-node-exporter.service loaded active running Экспортер Prometheus для метрик машины
  rsyslog.service                  loaded active running Служба системного логирования
  [email protected]       loaded active running Последовательный сеанс на ttyS0
  smartmontools.service            loaded active running Демон самомониторинга и отчетов (SMART)
  snapd.service                    loaded active running Демон Snap
  ssh.service                      loaded active running Сервер защищенных оболочек OpenBSD
  systemd-journald.service         loaded active running Журнальная служба
  systemd-logind.service           loaded active running Управление пользовательским входом
  systemd-networkd.service         loaded active running Конфигурация сети
  systemd-timesyncd.service        loaded active running Синхронизация сетевого времени
  systemd-udevd.service            loaded active running Менеджер событий устройств на основе правил
  udisks2.service                  loaded active running Менеджер дисков
  unattended-upgrades.service      loaded active running Автоматические обновления
  unbound.service                  loaded active running Сервер DNS Unbound
  [email protected]                loaded active running Менеджер пользователей для UID 1000

LOAD   = Отражает, корректно ли загружен файл определения устройства.
ACTIVE = Высокоуровневое состояние активации устройства, то есть обобщение SUB.
SUB    = Низкоуровневое состояние активации устройства, значения зависят от типа устройства.
26 загруженных устройств, перечисленных.

Я считаю, что это те, которые я установил и активировал (и работают как на SuperMicro, так и на Raspberry Pi):

  containerd.service               loaded active running контейнерная среда выполнения containerd
  docker.service                   loaded active running Контейнерная среда выполнения Docker
  prometheus-node-exporter.service loaded active running Экспортер Prometheus для метрик машины
  smartmontools.service            loaded active running Демон самомониторинга и отчетов (SMART)
  unbound.service                  loaded active running Сервер DNS Unbound

Вещи, которые нужно протестировать:

  • Проверьте, изменится ли что-то в конфигурации sudo без NOPASSWD.
  • Отключите установленные службы, которые общие между моим сервером SuperMicro и Raspberry Pi.

Обычно, когда программа отказывается работать в фоновом режиме, это связано с тем, что ей нужен ввод.
Хотя df на самом деле не читает из stdin, он все же открыт. Попробуйте:

#!/bin/bash
sudo du -h --max-depth=1 / 2> /dev/null 0< /dev/null

Обратите внимание, что это может оставить вас без возможности ввести пароль.

Также возможно, что могут возникнуть проблемы с попыткой отправки соответствующих сигналов в процессе, потому что это не . Я сомневаюсь в этом, но если это так, я бы предложил просто сразу запустить его в фоновом режиме. (Эта проблема у меня отсутствует.)

sudo ненадежен ;(

Попробуйте

sudo -l

Вы можете обнаружить, что вам нужно остановить sudo от ожидания tty или пароля, например, создать /etc/sudoers.d/000-defaults содержащий

Defaults !requiretty, !verifypw, listpw=any

Это поведение по умолчанию. Вы можете избежать приостановки тремя способами

  1. Убедитесь, что ни один из stdout, stderr и stdin не подключен к терминалу. Например,

    sudo du -h --max-depth=1 / </dev/null 2>/dev/null >>./test.txt
    
  2. Разрешите фоновым процессам работать безпрепятственно. Установите stty -tostop перед выполнением кода

    stty -tostop
    sudo du -h --max-depth=1 / >>./test.txt
    
  3. Включите логирование в конфигурации sudo.
    Информация задокументирована в файле NEWS из исходного кода (в Debian):

    Для sudo версии 1.8.22

    • Когда включено логирование ввода-вывода, sudo теперь будет писать в терминал
      даже когда оно является фоновым процессом.

    Смотрите раздел I/O LOGGING в man sudoers для получения информации о том, как настроить логирование.

Ответ или решение

Чтобы ответить на вопрос о "ненадежной проверке фона с использованием bg в Bash-скрипте с sudo", необходимо рассмотреть основные аспекты работы с фоновыми процессами и командой sudo в операционных системах на базе Linux.

Проблема

Вы создали bash-скрипт (test.sh), который выводит использование дискового пространства в каталогах корневой директории. Скрипт использует команду sudo для выполнения команды du с повышенными привилегиями. При этом вы не запрашиваете пароль для sudo.

Скрипт запускается командой:

./test.sh >> ./test.txt

При его приостановке через комбинацию Ctrl+Z и попытке возобновить выполнение в фоновом режиме с помощью bg, вы сталкиваетесь с проблемой: процесс не продолжается в фоновом режиме, несмотря на несколько попыток.

Поведение sudo в фоновом режиме

Команда sudo иногда может вести себя непредсказуемо, когда её процесс переводится в фоновый режим. Это связано с несколькими факторами:

  1. Взаимодействие с псевдотерминалом: sudo может требовать соединения с терминалом для выполнения интерактивных запросов (например, связанных с вводом пароля), даже если ввод пароля отключен.

  2. Обработка сигналов: Когда скрипт приостанавливается и переводится в фоновый режим, sudo продолжает ожидать терминал и может не получать сигналы управления фоновой работой должным образом.

Решения проблемы

  1. Перенаправление стандартных потоков: Как было предложено в описании проблемы, можно попробовать перенаправить стандартные потоки stdin, stdout и stderr, чтобы избежать зависимостей от терминала:

    sudo du -h --max-depth=1 / < /dev/null 2>/dev/null >> ./test.txt
  2. Использование stty -tostop: Эта команда отключает стандартное действие остановки фоновым процессом, когда он пытается записывать в терминал:

    stty -tostop
    ./test.sh >> ./test.txt

    Однако, в описании указано, что этот метод не всегда помогает.

  3. Изменение конфигурации sudo: В некоторых случаях надо модифицировать конфигурацию sudo, чтобы отключить запрос пароля и требование терминала. Это можно сделать, добавив в файл /etc/sudoers.d/000-defaults:

    Defaults !requiretty, !verifypw, listpw=any
  4. Запуск без sudo в скрипте: Самый эффективный способ обойти проблему — это запускать весь скрипт с sudo, а не включать его непосредственно в скрипт. Это важный аспект, так как в этом случае bg и fg работают как ожидается.

    sudo ./test.sh >> ./test.txt

Заключение

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

При правильной конфигурации и понимании работы sudo, управление фоновыми процессами становится более предсказуемым и надежным. Советую провести эксперименты с вышеописанными способами и, при необходимости, изучить конфигурацию вашей среды для поиска дополнительных решений.

Оцените материал
Добавить комментарий

Капча загружается...