Отображение переменных окружения в Redis


    Redis — это такое хранилище вида ключ-значение. Переменные окружения (environment variables) — напоминают то же самое. А что если это как-то объединить?


    Для любителей пятничных постов, несложного хакинга и странных желаний — прошу под кат


    Существует внушительный список клиентских библиотек для Redis на почти всех языках программирования. Но что, если:


    1. есть уже существующие приложения, изменять которые нехорошо;
    2. необходимо/хочется сделать приложение, умеющее работать как с Redis, так и без него;
    3. проще — лучше. Довольно часто работа с кэшом носит чисто вспомогательный характер и привносит излишнюю сложность в приложение.

    В моем случае задача возникла после создание очередного CGI-like сервиса, которому необходимо было сохранять состояние. При этом выполнение этого скрипта может происходить на разных машинах.


    Так как дело было примерно часа в 2 ночи и для меня уже наступила пятница, было принято решение расслабится и сделать что-нибудь несложное и интересное.


    Вопрос и идея


    Можно ли перехватить системные вызовы так, что при записи в переменные окружения (setenv), данные
    записывались в Redis, а при чтении (getenv) наоборот доставались из кэша?


    Схематично выглядит так:


    Application <-- [syscall]--> [Wrapper] <-- [GET/DEL/...] --> [Redis]

    Исследование


    Это возможно?


    Да, есть хорошая статья, где описывается как делать перехватчики системных вызовов.


    Кто-то должен инициализировать подключение...


    Есть малопопулярная возможность указать функции инициализации (constructor) и финализирования (destructor) в разделяемой библиотеки. В них и будем подключаться.


    Реализация


    Пришлось изучить спецификацию POSIX'a и Linux'a по этой теме.


    Функции, которые необходимо было перехватить:


    • putenv
    • setenv
    • getenv
    • secure_getenv
    • clearenv
    • unsetenv

    Код тут.


    Зависимости


    • C11
    • hiredis 0.13

    Сборка


    Подвохов нет — типичный CMake с Github'a


    Зависимости


    sudo apt install libhiredis-dev cmake build-essential

    Сборка


    git clone https://github.com/reddec/envredis.git
    cd envredis
    mkdir build && cd build
    cmake -DCMAKE_BUILD_TYPE=Release ../  && make

    Использование


    Важный момент: некоторые приложения не меняют переменные окружения, а только внутренний массив. В таких приложениях данные из Redis будут получены, но обратно не отобразятся.


    Хороший вариант — python. Согласно документации, изменение в os.environ отображается в переменных окружения.


    Допустим, уже поднят Redis на локальной машине.


    • Выполним вывод переменной XYZ

    $ python -c 'import os; print("XYZ=" + os.environ.get("XYZ", ""))'
    # вывод будет такой:
    # XYZ=

    • Зададим переменную в Redis

    $ redis-cli SET XYZ "Hello world"
    # вывод будет такой:
    # OK

    • Выполним вместе с перехватчиком

    LD_PRELOAD=/path/to/libenvredis.so python -c 'import os; print("XYZ=" + os.environ.get("XYZ", ""))'
    # вывод будет
    # XYZ=Hello world

    • Теперь попробуем задать переменную

    LD_PRELOAD=/path/to/libenvredis.so python -c 'import os; os.environ["SAY"] = "Bye!"'

    • Проверим, что она отобразилась в Redis'e

    redis-cli GET SAY
    # вывод будет
    # "Bye!"

    P.S.


    Задача заняла примерно два часа, с учетом изучения предметной области. Сделано больше для фана, нежели для реальных целей.


    Тем не менее кому-то может пригодится.


    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое