Как узнать, какой процесс запущен в конкретном окне GNU Screen?

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

Проблема

Мне нужно найти, какой процесс работает в конкретном окне экрана (за разумное время).

Сценарий

Мне нужно использовать имя сеанса и заголовок окна, чтобы найти процесс, работающий там. Это должно выполнить не очень медленно.

Также, возможно, стоит отметить: я использую byobu как оболочку для screen.

Что я пробовал

  • Поиск в интернете
  • Чтение man-страницы screen (не для слабонервных). (Окей, я не прочитал все, но ознакомился с большинством важных разделов и тщательно искал все, что может быть полезным.)
    • Что я узнал:
      • Единственный способ получить информацию, которая может мне понадобиться от screen (вызвав screen), — это использование его флагов командной строки.
        • -Q позволит вам запрашивать определенные команды, но ни одна из них не предоставляет все, что мне нужно. Та, которую я использую, возвращает номер окна.
          • number – то, что я использую для получения номера окна
          • windowlist – позволяет получить кастомную строку информации, но PID сеанса не является одной из доступных запрашиваемых вещей
          • echo, info, lastmsg, select, time, title — это другие, и они не выглядели полезными
        • -ls перечисляет активные сеансы. Он добавляет PID к имени сеанса, так что это то, как я сейчас получаю PID сеанса.
      • Как только у меня есть PID оболочки, работающей в определенном окне, я могу проверить её переменные окружения на наличие WINDOW. Эта переменная устанавливается в номер окна. Это то, что я использую для сопоставления процесса с окном.
      • Нет одной команды, которая позволит мне вернуть PID сеанса и карту заголовков окон к номерам окон. Также я не смог найти никак, чтобы детерминированно определить ID сеанса и карту заголовков окон к номерам окон вне вызова screen.
  • Попытки и ошибки / рытье через переменные окружения
  • Написание скрипта

Мой скрипт

Я написал скрипт, который, кажется, успешно решает проблему, но выполнение занимает чуть больше 0,75 секунды. Это слишком долго для того, что мне нужно, но, что более важно, слишком долго, когда сервер ожидает его завершения, чтобы отправить ответ на HTML-запрос.

Вот скрипт:

#!/bin/bash
# Принимаем имя сеанса GNU/screen и окно и возвращаем процесс, работающий в его оболочке
SessionName=$1
TabName=$2

# ====== В среднем 0.370 секунд ======
# Это находит номер окна, заданный именем сеанса и заголовком окна
# Команда screen запрашивает номер окна указанного
# заголовка окна в указанном сеансе.
# Пример вывода команды screen: 1 (Main)
# Пример вывода после команды cut: 1
TargetTabNum=$(screen -S $SessionName -p $TabName -Q number | cut -d ' ' -f1)

# ====== В среднем 0.370 секунд ======
# Это находит PID сеанса, заданный именем сеанса.
# Команда screen выводит список ID сеансов
# Пример вывода команды screen:
#     На экране:
#             29676.byobu     (12/09/2019 10:23:19 AM)        (Подключено)
#     1 сокет в /run/screen/S-{здесь_имя_пользователя}.
# Пример вывода после команды sed: 29676
SessionPID=$(screen -ls | sed -n "s/\s*\([0-9]*\)\.$SessionName\t.*/\1/p")

# ====== Остаток в среднем 0.025 секунды ======
# Это получает все процессы, имеющие сеанс как родительский,
# перебирает их, проверяя переменную WINDOW в окружении
# каждого из них, пока не найдет тот, который соответствует заголовку окна,
# а затем находит процесс с этим процессом как родительским и выводит его
# команду и аргументы (или null, если нет совпадающих процессов)
ProcessArray=( $(ps -o pid --ppid $SessionPID --no-headers) )
for i in "${ProcessArray[@]}"
do
    ProcTabNum=$(tr '\0' '\n' < /proc/$i/environ | grep ^WINDOW= | cut -d '=' -f2)
    if [ ! -z "$ProcTabNum" ] && [ "$TargetTabNum" -eq "$ProcTabNum" ]; then
        ProcInTab=$(ps -o cmd --ppid $i --no-headers)
        if [[ $? -eq 1 ]]; then
            ProcInTab=NULL
        fi
        echo $ProcInTab
        exit 0
    fi
done
echo "Не удалось найти указанную вкладку: $TabName" >&2
exit 1

Как вы можете видеть, проблемные команды — это две команды screen. Я могу избавиться от одной из них, найдя процесс screen, запущенный с именем сеанса, но это кажется немного ненадежным, и я не уверен, что это будет детерминированно:

SessionPID=$(ps -eo pid,command --no-headers | grep -i "[0-9]* screen.*\-s $SessionName " | grep -v grep | cut -d ' ' -f1)

Цель

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

(Я все еще довольно нов на StackExchange, так что любые отзывы по моему вопросу приветствуются!)

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

Нет, вы смотрите на screen неправильно. screen — это виртуальный псевдотерминал (поэтому он прикрепляется к /dev/pts/*) и с ним можно взаимодействовать сотней разных способов. В момент, когда вы запускаете screen, вы получаете сокет, видимый в netstat, с которым вы можете взаимодействовать, и то, как вы управляете этой сокетной связью, будет определять, сколько времени займет данный обмен. Лучший способ сделать эту связь как можно более быстрой — воспользоваться вещами, которые уже возвращаются, вместо отправки запроса и ожидания ответа.

В вашем случае вопрос можно решить, запустив w следующим образом:

shark@tank:~$ sudo w
 20:39:11 up 2 days,  1:24,  2 users,  load average: 0.64, 0.41, 0.44
USER     TTY      FROM             LOGIN@   IDLE       WHAT
shark    tty7     :0               Sun19    2days      /usr/lib/xorg/Xorg :0 *
fish     pts/1    :pts/6:S.0       20:31    7:42       htop

как вы можете видеть, второй пользователь fish работает на screen сокете, подключенном к tty на /dev/pts/1, который принадлежит пользователю shark, как можно увидеть, запустив ls -la /dev/pts/, и этот сокет слушает на /dev/pts/6, и там работает htop.

Как мы можем проверить, что это действительно подключение к сокету? Запустив:

sudo file /run/screen/*/*
/run/screen/S-fish/98633.pts-6.tank: socket

и номер, который мы видим, соответствует

ps aux | grep 98633 -B 1 -A 1 
fish      98632  0.0  0.0   6732  2964 pts/6   S+   20:31   0:00 screen
fish      98633  0.0  0.0   7032  2576 ?        Ss   20:31   0:00 SCREEN
fish      98634  0.0  0.0   8660  5400 pts/1   Ss+  20:31   0:00 /bin/bash

Команда получает эту информацию из /proc и /var/run/utmp, и задержка минимальна, как можно увидеть, запустив:

$ sudo usr/bin/time -f "\t%E real\n\t%U user\n\t%S sys\n" w > /dev/null
    0:00.01 real
    0.00 user
    0.01 sys

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

Поиск выполнения конкретного процесса в окне GNU screen может быть сложной задачей, особенно если требуется выполнить это быстро и надежно. В контексте использования byobu как обёртки для screen, задача становится ещё более специфичной. В данной статье мы рассмотрим теоретические аспекты, приведём примеры и предложим практическое решение проблемы.

Теория

GNU screen — это утилита, которая позволяет пользователям работать в нескольких терминальных сессиях одновременно, независимо от их состояния. Она обеспечивает удобный интерфейс для управления этими сессиями, что особенно полезно для удаленной работы. Основная сложность при получении информации о процессе заключается в том, что screen не предоставляет прямого метода для ассоциации окон с конкретными процессами. Однако, screen создает псевдотерминалы (/dev/pts/*), с которыми можно взаимодействовать, используя различные системные утилиты.

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

Пример

Представим, что у нас есть screen-сессия, и мы хотим узнать, какой процесс выполняется в определенном окне. Один из вариантов — использование команды w, которая отображает информацию о пользователях и их текущих процессах:

$ w
 20:39:11 up 2 days,  1:24,  2 users,  load average: 0.64, 0.41, 0.44
USER     TTY      FROM             LOGIN@   IDLE       WHAT
user1    tty1     :0               Sun19    2days      /usr/lib/xorg/Xorg :0
user2    pts/1    :pts/6:S.0       20:31    7:42       htop

Здесь видно, что пользователь user2 использует screen-связь через /dev/pts/1, которая подключена к псевдотерминалу, связанному с командой htop. Эта команда позволяет быстро определить активный процесс в окне screen, минимизируя системные ресурсы.

Применение

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

#!/bin/bash

SessionName=$1
WindowTitle=$2

# Получение сведений об активной сессии
ActiveSessions=$(screen -ls | awk -v session="$SessionName" '$0 ~ session {print $1}')

# Проверка на наличие активной сессии
if [ -z "$ActiveSessions" ]; then
    echo "Сессия $SessionName не найдена"
    exit 1
fi

# Получение информации о процессе, связанного с указанным заголовком окна
for Session in $ActiveSessions; do
    PtsName=$(screen -S "$Session" -Q windows | grep "$WindowTitle" | awk '{print $1}')

    if [ -z "$PtsName" ]; then
        continue
    fi

    TTY=$(pgrep -fla screen | grep "$Session" | grep "$PtsName" | awk '{print $NF}')

    if [ -n "$TTY" ]; then
        ProcessInfo=$(ps aux | grep $TTY | awk 'NR==1')
        echo "Процесс в окне $WindowTitle: $ProcessInfo"
        exit 0
    fi
done

echo "Процесс для окна $WindowTitle не найден"
exit 1

Заключение

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

В заключение, для решения задачи идентификации процесса, выполняющегося в окне GNU screen, можно использовать методы, которые выгодно используют существующие системные инструменты и псевдотерминалы, тем самым значительно снижая нагрузку на систему и сокращая время выполнения поисковых операций.

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

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