zmień środowisko uruchomionego procesu

18

Jak może być możliwa zmiana jakiejś zmiennej w envjuż uruchomionym procesie, na przykład poprzez /proc/PID/environ?„Plik” read-only.

Konieczna jest zmiana lub rozbrojenie zmiennej DISPLAY długotrwałego zadania wsadowego bez jej zabijania.

Marcos
źródło
3
Jest już za późno, ale na przyszłość xpramoże być interesujące.
sr_
xprabrzmi użytecznie. Zwykle przekierowuję na ekrany inne niż hostowane przez Xvfblub Xephyr, ale dziś zapomniałem i uruchomiłem z cli zamiast cron / at, aby rozwiązać problemy z wyjściem, więc denerwowało mnie to:0
Marcos

Odpowiedzi:

19

Nie możesz tego zrobić bez paskudnych włamań - nie ma do tego API, nie ma sposobu, aby powiadomić proces, że zmieniło się jego środowisko (ponieważ tak naprawdę nie jest to możliwe).
Nawet jeśli uda ci się to zrobić, nie ma sposobu, aby upewnić się, że będzie to miało jakikolwiek efekt - proces mógł bardzo dobrze buforować zmienną środowiskową, którą próbujesz zaczepić (ponieważ nic nie powinno być w stanie jej zmienić ).

Jeśli naprawdę chcesz to zrobić i jesteś przygotowany na zbieranie elementów, jeśli coś pójdzie nie tak, możesz użyć debugera. Zobacz na przykład pytanie Przepełnienie stosu:
czy istnieje sposób na zmianę zmiennych środowiskowych innego procesu?

Głównie:

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

Inne możliwe funkcje, które możesz próbować wywołać, to setenvlub unsetenv.

Należy pamiętać, że może to nie działać lub mieć tragiczne konsekwencje, jeśli proces, na który celujesz, „blokuje” elementy środowiska. Najpierw przetestuj to na niekrytycznych procesach, ale upewnij się, że te procesy testowe odzwierciedlają jak najbliżej tego, który próbujesz zaczepić.

Mata
źródło
3
Tak, zdaję sobie sprawę, że jest to trochę hack, ryzykowne i nie ma gwarancji z powodów, o których wspomniałeś. (Częściowo powód, dla którego odwiedzam tę grupę, to takie niekonwencjonalne potrzeby, których zwykle nie mogę znaleźć). W takim przypadku ustawienie WYŚWIETLACZA na śmieci lub pustkę rozwiązuje po prostu irytację i opóźnienie (niepotrzebne częste zrzuty ekranu przez sieć, w porządku, jeśli nie udaje im się). Ponieważ dziecko kopiuje rodzica, potrzebuję tylko modyfikować środowisko nadrzędne. Odradza się wiele nowych procesów potomnych i podrzędnych i szybko wychodzę z zadania wsadowego; te sprawy. Pomyślałem, że debugger może to zrobić, dzięki - mógłbym zawinąć to w funkcję powłoki.
Marcos,
0

Nie ma takiej potrzeby, jeśli zadanie wsadowe może odczytać z systemu plików w celu pobrania zmiany. Wystarczy uruchomić zadanie ze ścieżką do tymczasowego unikalnego katalogu i przekazać tę samą ścieżkę do skryptu powłoki potomnej. Skrypt zablokuje plik w tym katalogu i zapisze plik z nowymi wartościami w pobliżu pliku blokady. Od czasu do czasu skrypt zadania blokuje ten sam plik, analizuje i odczytuje zmiany z pliku wartości. Aby dowiedzieć się, jak zrobić blokadę w powłoce uniksowej, po prostu wyszukaj unix shell lock filelub bash lock fileistnieje już wiele takich rozwiązań.

Korzyści z tego rozwiązania:

  • przenośny między prawie każdym systemem operacyjnym, takim jak Windows lub Unix
  • nie trzeba pisać i duplikować złożonych parserów dla każdego interpretera (unix / windows / etc), aby odczytać wartości z pliku, o ile plik wartości pozostaje prosty

Problemy we wdrażaniu poniżej:

  • Implementacja polega na zablokowaniu pliku w fazie przekierowania powłoki ( flockw systemie Linux w celu uzyskania efektu wykluczenia, w systemie Windows ma wbudowane wykluczenie)
  • Każda wartość dla zmiennej jest wartością pojedynczego wiersza (nie wielowierszową)

Implementacja jest przechowywana tutaj: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools

bashRealizacja:

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"

To samo w systemie Windows (jako przykład przenośności):

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

Aby zapisać pliki, po prostu w ten sam sposób zablokuj kod.

Andry
źródło