изменить среду запущенного процесса

18

Как может быть возможно изменить некоторую переменную в envуже запущенном процессе, например, через /proc/PID/environ?«файл» read-only.

Нужно изменить или сбросить переменную DISPLAY в длительном пакетном задании, не убивая его.

Marcos
источник
3
Сейчас слишком поздно, но для дальнейшего использования xpraможет быть интересно.
sr_
xpraзвучит полезно. Обычно я перенаправляю на не-пользовательские дисплеи, размещенные Xvfbили Xephyr, но сегодня я забыл и запустил из cli, а не cron / at для устранения проблем с выводом, поэтому меня это раздражало:0
Маркос

Ответы:

19

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

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

По существу:

(gdb) attach process_id
(gdb) call putenv ("DISPLAY=your.new:value")
(gdb) detach

Другими возможными функциями, которые вы можете попытаться вызвать, являются setenvили unsetenv.

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

Мат
источник
3
Да, я понимаю, что это что-то вроде хака, рискованного и не гарантированного по причинам, которые вы упомянули. (Часть причины, по которой я посещаю эту группу, связана с такими нетрадиционными потребностями, которые я обычно не могу найти.) В этом случае настройка DISPLAY на нежелательную или пустую просто устраняет раздражение и задержку (ненужные частые снимки экрана по сети, хорошо, если они терпят неудачу). Так как child копирует parent, мне нужно только мод родительского env. Многие новые процессы дочерних элементов и дочерних элементов порождаются и быстро завершаются в моей пакетной работе; это важно. Я подумал, что отладчик может сделать это, спасибо - я мог бы обернуть это в функцию оболочки.
Маркос
0

Нет необходимости делать это, если пакетное задание может считывать данные из файловой системы для получения изменений. Просто запустите задание с путем к временному уникальному каталогу и передайте тот же путь к дочернему сценарию оболочки. Скрипт заблокирует файл в этом каталоге и запишет файл с новыми значениями рядом с файлом блокировки. Время от времени сценарий задания блокирует один и тот же файл, анализирует и считывает изменения из файла значений. Чтобы узнать, как сделать блокировку в оболочке Unix, просто найдите unix shell lock fileили bash lock file, уже существует множество решений для этого.

Преимущества этого решения:

  • переносимость между практически любой ОС, такой как Windows или Unix
  • нет необходимости писать и дублировать сложные синтаксические анализаторы для каждого интерпретатора (unix / windows / etc) для чтения значений из файла, пока файл значений остается простым

Вопросы реализации ниже:

  • Реализация опирается на блокировку файла на этапе перенаправления оболочки ( flockв Linux для достижения эффекта исключения в Windows есть встроенное исключение)
  • Каждое значение для переменной является однострочным (не многострочным)

Реализация хранится здесь: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools

bashРеализация:

set_vars_from_locked_file_pair.sh

#!/bin/bash

# Another variant of a configuration file variables read and set script.
# The script must stay as simple as possible, so for this task it uses these parameters:
# 1. path where to lock a lock file
# 2. path where to read a file with variable names (each per line)
# 3. path where to read a file with variable values (each per line, must be the same quantity of lines with the variable names file)

# Script can be ONLY included by "source" command.
if [[ -n "$BASH" && (-z "$BASH_LINENO" || ${BASH_LINENO[0]} -gt 0) ]]; then 

function set_vars_from_locked_file_pair()
{
  # the lock file directory must already exist
  if [[ ! -d "${1%[/\\]*}" ]]; then
    echo "$0: error: lock file directory does not exist: \`${1%[/\\]*}\`" >&2
    return 1
  fi

  if [[ ! -f "${2//\\//}" ]]; then
    echo "$0: error: variable names file does not exist: \`$2\`" >&2
    return 2
  fi

  if [[ ! -f "${3//\\//}" ]]; then
    echo "$0: error: variable values file does not exist: \`$3\`" >&2
    return 3
  fi

  function LocalMain()
  {
    # open file for direct reading by the `read` in the same shell process
    exec 7< "$2"
    exec 8< "$3"

    # cleanup on return
    trap "rm -f \"$1\" 2> /dev/null; exec 8>&-; exec 7>&-; trap - RETURN" RETURN

    local __VarName
    local __VarValue

    # shared acquire of the lock file
    while :; do
      # lock via redirection to file
      {
        flock -s 9

        # simultaneous iteration over 2 lists in the same time
        while read -r -u 7 __VarName; do
          read -r -u 8 __VarValue
          # drop line returns
          __VarName="${__VarName//[$'\r\n']}"
          __VarValue="${__VarValue//[$'\r\n']}"
          # instead of `declare -gx` because `-g` is introduced only in `bash-4.2-alpha`
          export $__VarName="$__VarValue"
          (( ${4:-0} )) && echo "$__VarName=\`$__VarValue\`"
        done

        break

        # return with previous code
      } 9> "$1" 2> /dev/null # has exclusive lock been acquired?

      # busy wait
      sleep 0.02
    done
  }

  LocalMain "${1//\\//}" "${2//\\//}" "${3//\\//}" "${4:-0}"
}

fi

testlock.sh

#!/bin/bash

{
  flock -x 9 2> /dev/null
  read -n1 -r -p "Press any key to continue..."
  echo >&2
} 9> "lock"

То же самое в Windows (как пример переносимости):

set_vars_from_locked_file_pair.bat

@echo off

rem Another variant of a configuration file variables read and set script.
rem The script must stay as simple as possible, so for this task it uses these parameters:
rem 1. path where to lock a lock file
rem 2. path where to read a file with variable names (each per line)
rem 3. path where to read a file with variable values (each per line, must be the same quantity of lines with the variable names file)

rem disable alternative variables expansion to avoid `!` character consumption
setlocal DISABLEDELAYEDEXPANSION

set "FILE_LOCK_PATH=%~1"
set "FILE_VAR_NAMES_PATH=%~2"
set "FILE_VAR_VALUES_PATH=%~3"
set "PRINT_VARS_SET=%~4"

set "FILE_LOCK_DIR=%~d1"

rem the lock file directory must already exist
if not exist "%FILE_LOCK_DIR%" (
  echo.%~nx0: error: FILE_LOCK_DIR does not exist: "%FILE_LOCK_DIR%"
  exit /b 1
) >&2

if not exist "%FILE_VAR_NAMES_PATH%" (
  echo.%~nx0: error: FILE_VAR_NAMES_PATH does not exist: "%FILE_VAR_NAMES_PATH%"
  exit /b 2
) >&2

if not exist "%FILE_VAR_VALUES_PATH%" (
  echo.%~nx0: error: FILE_VAR_VALUES_PATH does not exist: "%FILE_VAR_VALUES_PATH%"
  exit /b 3
) >&2

rem The endlocal works only in the same call context
endlocal

rem exclusive acquire of the lock file
:REPEAT_LOCK_LOOP

(
  (
    rem if lock is acquired, then we are in...
    call :MAIN "%%~2" "%%~3" "%%~4"
    call set "LASTERROR=%%ERRORLEVEL%%"

    rem exit with return code from the MAIN
  ) 9> "%~1" && (del /F /Q /A:-D "%~1" & goto EXIT)
) 2>nul

rem Busy wait: with external call significantly reduces CPU consumption while in a waiting state
pathping localhost -n -q 1 -p 20 >nul 2>&1
goto REPEAT_LOCK_LOOP

:EXIT
exit /b %LASTERROR%

:MAIN
rem drop last error
type nul>nul

if %~30 NEQ 0 goto SET_WITH_PRINT

rem trick with simultaneous iteration over 2 lists in the same time
(
  for /f "usebackq eol=# tokens=* delims=" %%i in ("%~1") do (
    set /p "%%i="
  )
) < "%~2"

exit /b 0

:SET_WITH_PRINT
rem trick with simultaneous iteration over 2 lists in the same time
(
  for /f "usebackq eol=# tokens=* delims=" %%i in ("%~1") do (
    set /p "%%i="
    rem to filter out wrong matches of a variable from the `set "%%i"`
    for /f "usebackq eol=# tokens=1,* delims==" %%j in (`set "%%i"`) do if /i "%%j" == "%%i" echo.%%i=%%k
  )
) < "%~2"

exit /b 0

testlock.bat

@echo off

(
  pause
) 9> ./lock

Для записи файлов просто сделайте таким же образом блокировку в своем коде.

Andry
источник