cambiar el entorno de un proceso en ejecución

18

¿Cómo podría ser posible alterar alguna variable en envun proceso que ya se está ejecutando, por ejemplo a través de /proc/PID/environ?ese "archivo" read-only?

Necesita cambiar o desarmar la variable DISPLAY de un trabajo por lotes de larga ejecución sin eliminarlo.

Marcos
fuente
3
Ahora es demasiado tarde, pero para futuras referencias, xprapodría ser interesante.
Sr_
xpraSuena útil Normalmente, me redirijo a pantallas que no son de usuario alojadas por Xvfbo Xephyr, pero hoy olvidé y ejecuté desde cli en lugar de cron / at para solucionar problemas de salida, por lo que me ha molestado:0
Marcos

Respuestas:

19

No puede hacer esto sin un truco desagradable: no hay API para esto, no hay forma de notificar al proceso que su entorno ha cambiado (ya que de todos modos eso no es posible).
Incluso si logras hacer eso, no hay forma de estar seguro de que tendrá algún efecto: el proceso podría haber almacenado en caché la variable de entorno que estás tratando de meter (ya que se supone que nada puede cambiarlo) )

Si realmente desea hacer esto, y está preparado para recoger las piezas si las cosas salen mal, puede usar un depurador. Consulte, por ejemplo, esta pregunta de desbordamiento de pila:
¿hay alguna forma de cambiar las variables de entorno de otro proceso?

Esencialmente:

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

Otras funciones posibles que podría intentar llamar son setenvo unsetenv.

Por favor, tenga en cuenta que esto puede no funcionar o tener consecuencias nefastas si el proceso al que se dirige hace cosas "interesantes" con su bloqueo del entorno. Primero pruébelo en procesos no críticos, pero asegúrese de que estos procesos de prueba reflejen lo más cerca posible del que está intentando meter.

Estera
fuente
3
Sí, me doy cuenta de que es un truco, arriesgado y no garantizado por las razones que mencionaste. (Parte de la razón por la que visito este grupo es para esas necesidades no convencionales que parece que no puedo encontrar normalmente). En este caso, configurar DISPLAY como basura o vacío simplemente resuelve una molestia y un retraso (capturas de pantalla frecuentes innecesarias en la red, bien si Ellos fallan). Como el niño copia al padre, solo necesito modificar el entorno del padre. Muchos nuevos procesos secundarios y secundarios se generan y salen rápidamente en mi trabajo por lotes; Esos importan. Pensé que un depurador podría hacer esto, gracias, podría envolverlo en una función de shell.
Marcos
0

No es necesario hacerlo si un trabajo por lotes puede leer desde un sistema de archivos para recuperar un cambio. Simplemente ejecute un trabajo con ruta a un directorio único temporal y pase la misma ruta al script de shell hijo. El script bloqueará un archivo en ese directorio y escribirá un archivo con nuevos valores cerca del archivo de bloqueo. Un script de trabajo de vez en cuando bloqueará el mismo archivo, analizará y leerá los cambios del archivo de valores. Para descubrir cómo hacer un bloqueo en el shell de Unix solo busque unix shell lock fileo bash lock file, ya existen muchas soluciones para eso.

Beneficios de esta solución:

  • portátil entre casi cualquier sistema operativo como Windows o Unix
  • no es necesario escribir y duplicar analizadores complejos para cada intérprete (unix / windows / etc.) para volver a leer los valores del archivo siempre que el archivo de valores permanezca simple

Problemas en la implementación a continuación:

  • La implementación se basa en un bloqueo de archivo en una fase de redirección de shell ( flocken Linux para lograr el efecto de exclusión, en Windows tiene una exclusión incorporada)
  • Cada valor para una variable es un valor de línea única (no una línea múltiple)

La implementación se almacena aquí: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools

La bashimplementación:

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"

Lo mismo en Windows (como ejemplo de portabilidad):

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

Para escribir los archivos de la misma manera, bloquee su código.

Andry
fuente