alterar o ambiente de um processo em execução

18

Como é possível alterar alguma variável no envprocesso que já está em execução, por exemplo, através de /proc/PID/environ?Esse "arquivo" é read-only.

É necessário alterar ou desabilitar a variável DISPLAY de um trabalho em lotes de longa execução sem matá-lo.

Marcos
fonte
3
Agora é tarde demais, mas para referência futura, xprapode ser interessante.
sr_
xpraparece útil. Normalmente, redireciono para exibições de não usuários hospedadas por Xvfbor Xephyr, mas hoje eu esqueci e executei o cli em vez de cron / at para solucionar problemas de saída, por isso está me incomodando em:0
Marcos

Respostas:

19

Você não pode fazer isso sem hacks desagradáveis ​​- não há API para isso, não há maneira de notificar o processo de que seu ambiente foi alterado (já que isso não é realmente possível).
Mesmo que você consiga fazer isso, não há como ter certeza de que isso terá algum efeito - o processo poderia muito bem ter armazenado em cache a variável de ambiente que você está tentando cutucar (já que nada deveria ser capaz de alterá-la). )

Se você realmente deseja fazer isso e está preparado para juntar as peças, se algo der errado, você pode usar um depurador. Veja, por exemplo, a pergunta Stack Overflow:
Existe uma maneira de alterar as variáveis ​​de ambiente de outro processo?

Essencialmente:

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

Outras funções possíveis que você pode tentar chamar são setenvou unsetenv.

Por favor, tenha em mente que isso pode não funcionar ou ter conseqüências terríveis se o processo que você direcionar fizer coisas "interessantes" com seu bloqueio de ambiente. Teste-o primeiro em processos não críticos, mas certifique-se de que esses processos sejam o mais próximo possível do que você está tentando cutucar.

Esteira
fonte
3
Sim, eu sei que é um hack, arriscado e não garantido pelos motivos que você mencionou. (Parte do motivo pelo qual eu visito esse grupo é para essas necessidades não convencionais que normalmente não consigo encontrar.) Nesse caso, configurar DISPLAY para lixo eletrônico ou vazio apenas resolve um aborrecimento e atraso (capturas de tela frequentes e desnecessárias na rede, tudo bem se eles falham). Como filho copia pai, preciso apenas modificar o ambiente pai. Muitos processos novos de filhos e sub-filhos estão sendo gerados e saem rapidamente no meu trabalho em lotes; aqueles importam. Eu achei que um depurador poderia fazer isso, obrigado - eu poderia colocar isso em uma função shell.
Marcos
0

Não é necessário fazer isso se um trabalho em lotes puder ler de um sistema de arquivos para recuperar uma alteração. Basta executar um trabalho com caminho para um diretório exclusivo temporário e passar o mesmo caminho para o script do shell filho. O script bloqueará um arquivo nesse diretório e gravará um arquivo com novos valores próximo ao arquivo de bloqueio. De vez em quando, um script de tarefa bloqueia o mesmo arquivo, analisa e lê as alterações novamente no arquivo de valores. Para descobrir como fazer um bloqueio no shell unix, basta procurar unix shell lock fileou bash lock file, já existem muitas soluções para isso.

Benefícios desta solução:

  • portátil entre quase qualquer sistema operacional como Windows ou Unix
  • não há necessidade de escrever e duplicar analisadores complexos para cada intérprete (unix / windows / etc) para ler valores do arquivo contanto que o arquivo de valores permaneça simples

Problemas na implementação abaixo:

  • A implementação depende de um bloqueio de arquivo em uma fase de redirecionamento de shell ( flockno Linux, para obter efeito de exclusão, o Windows possui uma exclusão interna)
  • Cada valor para uma variável é um valor de linha única (não uma multilinha)

A implementação é armazenada aqui: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools

A bashimplementação:

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"

O mesmo no Windows (como um exemplo de portabilidade):

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 escrever os arquivos, faça o mesmo bloqueio do seu código.

Andry
fonte