Unix & Linux Asked on December 21, 2021
How might it be possible to alter some variable in the env
of an already running process, for example through /proc/PID/environ?
That “file” is read-only
.
Need to change or unset the DISPLAY variable of a long-running batch job without killing it.
If I attach gdb to the current shell and try to set env variable it remains the same(or doesn't exist):
$] sudo gdb -p $$
(gdb) call putenv("TEST=1234")
$1 = 0
(gdb) call (char*) getenv("TEST")
$2 = 0x0
(gdb) detach
(gdb) quit
$] echo "TEST=$TEST"
TEST=
I've found out that putenv doesn't work, but setenv does:
$] sudo gdb -p $$
(gdb) call (int) setenv("TEST", "1234", 1)
$1 = 0
(gdb) call (char*) getenv("TEST")
$2 = 0x55f19ff5edc0 "1234"
(gdb) detach
(gdb) quit
$] echo "TEST=$TEST"
TEST=1234
Answered by Kakash1hatake on December 21, 2021
There is no need to do so if a batch job can read from a file system to retrieve a change. Just run a job with path to a temporary unique directory and pass the same path to the child shell script. Script will lock a file in that directory and write a file with new values near the lock file. A job script time to time will lock the same file, parse and read changes back from the values file. To find out how to make a lock in the unix shell just search for unix shell lock file
or bash lock file
, there is already exist a plenty solutions for that.
Benefits from this solution:
Issues in implementation below:
flock
in Linux to achieve exclusion effect, in Windows has a builtin exclusion)Implementation is stored here: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools/std https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/bash/tacklelib
The bash
implementation:
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//[$'rn']}"
__VarValue="${__VarValue//[$'rn']}"
# 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"
The same on Windows (as an example of portability):
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
To write the files in just make the same way lock in your code.
Answered by Andry on December 21, 2021
You can't do this without a nasty hacks - there's no API for this, no way to notify the process that its environment has changed (since that's not really possible anyway).
Even if you do manage to do that, there is no way to be sure that it will have any effect - the process could very well have cached the environment variable you're trying to poke (since nothing is supposed to be able to change it).
If you really do want to do this, and are prepared to pick up the pieces if things go wrong, you can use a debugger. See for instance this Stack Overflow question:
Is there a way to change another process's environment variables?
Essentially:
(gdb) attach process_id
(gdb) call putenv ("DISPLAY=your.new:value")
(gdb) detach
Other possible functions you could try to call are setenv
or unsetenv
.
Please do really keep in mind that this may not work, or have dire consequences if the process you target does "interesting" things with its environment block. Do test it out on non-critical processes first, but make sure these test processes mirror as close as possible the one you're trying to poke.
Answered by Mat on December 21, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP