Как я могу обмануть процесс, думая, что файл не существует?

31

У меня есть программа, которая хранит свои настройки в ~/.config/myprogramчто я использую как в интерактивном режиме, так и с системой пакетного обслуживания. При интерактивном запуске я хочу, чтобы эта программа использовала мои файлы конфигурации (и это так). Но при работе в пакетном режиме файлы конфигурации не нужны, потому что я указываю параметры командной строки, которые перезаписывают все соответствующие параметры. Кроме того, доступ к файлам конфигурации по сети увеличивает время запуска программы на несколько секунд; если файлы не существуют, программа запускается намного быстрее (поскольку каждое задание занимает около минуты, это существенно влияет на пропускную способность пакетного задания). Но поскольку я также использую программу в интерактивном режиме, я не хочу постоянно перемещать / удалять свои файлы конфигурации. В зависимости от того, когда мои пакетные задания запланированы в кластере (в зависимости от использования других пользователей),

(Кроме того: производительность сетевых файлов настолько низкая, что, вероятно, является ошибкой, но я всего лишь пользователь кластера, поэтому я могу только обойти его, а не исправлять.)

Я мог бы создать версию программы, которая не считывает файлы конфигурации (или не имеет опции командной строки) для пакетного использования, но среда сборки этой программы плохо спроектирована и сложна в настройке. Я бы предпочел использовать двоичные файлы, установленные через менеджер пакетов моей системы.

Как я могу обмануть отдельные экземпляры этой программы, притворившись, что мои файлы конфигурации не существуют (без изменения программы)? Я надеюсь на обертку формы pretendfiledoesntexist ~/.config/myprogram -- myprogram --various-options..., но я открыт для других решений.

Джеффри Босбом
источник
2
Вы можете запустить его как пользователь, у которого нет прав на чтение файла.
псим
1
@psimon Как «просто пользователь» кластера, я не могу создать нового пользователя для запуска моего пакетного задания. Хотя это умная идея, и если нет лучших предложений, я сделаю ошибку администратору кластера, чтобы сделать это для меня.
Джеффри Босбом
Или настройте скрипт, который сначала переименовывает файл конфигурации, запускает программу, а затем снова переименовывает файл конфигурации.
псим
@psimon Полагаю, я мог бы быть более ясным: возможно, я использую программу в интерактивном режиме и в пакетном режиме одновременно, в зависимости от того, когда мои пакетные задания запланированы в кластере.
Джеффри Босбоом
1
Да, если он динамически связан, вы можете использовать LD_PRELOADхук. Это проще (вы можете реализовать это через час или два, если вы знаете C), чем альтернатива, которая есть ptrace. Вы также можете использовать fakechroot для этого (я думаю, что это LD_PRELOAD).
Дероберт

Ответы:

33

Эта программа, вероятно, разрешает путь к этому файлу из $HOME/.config/myprogram. Таким образом, вы можете сказать, что ваш домашний каталог находится в другом месте, например:

HOME=/nowhere your-program

Теперь, возможно, вашей программе нужен какой-то другой ресурс в вашем домашнем каталоге. Если вы знаете, кто они, вы можете подготовить фальшивый дом для вашей программы со ссылками на необходимый ей ресурс.

mkdir -p ~/myprogram-home/.config
ln -s ~/.Xauthority ~/myprogram-home/
...
HOME=~/myprogram-home myprogram
Стефан Шазелас
источник
5
Этот ответ решает мою проблему, поэтому я принял его, хотя это и не общий ответ на вопрос в названии этого вопроса. Хук предварительной загрузки, как описано в другом ответе, является более общим (но также и более сложным) решением.
Джеффри Босбоом
28

Если все остальное терпит неудачу, напишите библиотеку-оболочку, которую вы будете использовать LD_PRELOAD, чтобы open("/home/you/my-program/config.interactive")перехватывать вызов, но через него проходил любой другой. Это работает для любого типа программы, даже сценариев оболочки, так как она будет фильтровать системные вызовы.

extern int errno;

int open(const char *pathname, int flags)
{
  char *config_path = get_config_file_path();
  if (!strstr(pathname, config_path))
  {
    return get_real_open(pathname, flags);
  }
  else
  {
    errno = ENOENT;
    return -1;
  }
}

Примечание: я не проверял этот код, и я не уверен на 100%, что эта errnoчасть работает.

Посмотрите как fakerootэто делается для звонков типа getuid(2)и stat(2).

По сути, компоновщик свяжет это приложение с вашей библиотекой, которая переопределяет openсимвол. Поскольку вы не можете использовать две разные функции, названные openв вашей собственной библиотеке, вы должны разделить ее во второй части (например get_real_open), которая, в свою очередь, будет ссылаться на исходный openвызов.

Оригинал: ./Application

Application -----> libc.so
            open()

Перехватывается: LD_PRELOAD=yourlib_wrap.so ./Application

Application -----> yourlib_wrap.so --------------> yourlib_impl.so -----> libc.so
            open()                 get_real_open()                 open()

Редактировать: По-видимому, есть ldфлаг, который вы можете включить ( --wrap <symbol>), который позволяет вам писать обертки, не прибегая к двойным ссылкам:

/* yourlib.c */
#include <stdio.h>

int __real_open(const char *pathname, int flags)

int __wrap_open(const char *pathname, int flags)
{
  char *config_path = get_config_file_path();
  if (!strstr(pathname, config_path))
  {
    /* the undefined reference here will resolve to "open" at linking time */
    return __real_open(pathname, flags);
  }
  else
  {
    errno = ENOENT;
    return -1; 
  }
}
sleblanc
источник
2

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

Дзынь
источник
См. Мое недавнее редактирование: я не контролирую пакетное планирование, поэтому я могу использовать программу в интерактивном режиме и одновременно как часть пакетного задания.
Джеффри Босбом
1

Это должно быть возможно с unionfs / aufs. Вы создаете chrootсреду для процесса. Вы используете реальный каталог в качестве слоя только для чтения и помещаете пустой поверх него. Затем вы монтируете том unionfs в соответствующий каталог в chrootсреде и удаляете там файл. Процесс не увидит этого, но все остальные увидят.

Хауке Лагинг
источник
0

Переименуйте файл конфигурации, например, в config.interactive. Создайте еще один пустой файл с именем например config.script.

Теперь создайте программную ссылку под названием config(или то, что приложение ожидает в качестве файла конфигурации) для любой необходимой конфигурации и запустите ваше приложение.

ln -s config.interactive config

Не забудьте убрать свою ссылку позже.

garethTheRed
источник
См. Мое недавнее редактирование: я не контролирую пакетное планирование, поэтому я могу использовать программу в интерактивном режиме и одновременно как часть пакетного задания. Этот ответ по сути аналогичен перемещению файлов вручную или с помощью сценария.
Джеффри Босбом
1
Doh! Мне нужно думать и печатать быстрее. Это большое приложение? Может ли он быть перенесен в chroot и запущен оттуда в интерактивном режиме? Полагаю, все зависит от того, с чем взаимодействует программа. Это также может быть очень утомительным занятием собрать все, что требуется, в chroot. (Я думаю, что я отговорил себя от этого варианта!)
garethTheRed
0

Если вы точно охарактеризовали, как ваша программа использует файл конфигурации, я пропустил его. Многие программы (такие как bashи vi) проверяют файл конфигурации сразу после запуска; если файл существует, прочитайте его и закройте. Эти программы никогда не обращаются к этим файлам инициализации снова. Если ваша программа такая, читайте дальше.

Я знаю, что вы отклонили ответы, которые делают файл конфигурации действительно несуществующим (переименовывая его), но у меня есть морщинка, которую я не видел, предложенный кем-либо еще. Сделайте это, когда вы вызываете программу в пакетном режиме:

DELAY_TIME=1
REALPATH="~/.config/myprogram"
HOLDPATH="${REALPATH}.hold"

mv "$REALPATH" "$HOLDPATH"
(sleep "$DELAY_TIME"; mv "$HOLDPATH" "$REALPATH")&
myprogram

Это удаляет файл конфигурации с пути, но затем перемещает его обратно на одну секунду позже, даже если myprogramон все еще работает. Это создает очень короткое окно времени, в течение которого файл недоступен - какова вероятность того, что вы запустите программу в интерактивном режиме в течение этого окна? (Даже если вы это сделаете, вы можете просто выйти и перезапустить, и файл конфигурации, вероятно, вернется на свое место.)

Это создает условия гонки; если программе требуется слишком много времени, чтобы открыть файл, она может получить реальный файл. Если это происходит достаточно часто, что является проблемой, просто увеличьте значение DELAY_TIME.

Скотт
источник
1
Что самое худшее, что может пойти не так? Я буквально видел, как это произошло. В производстве.
Хенк Лангевельд
-1

Мне нравится ответ Стефана, но это заставит любую программу поверить, что любой файл пуст - (потому что его dentry временно указывает на файл, который на самом деле пуст) :

cat <./test.txt
###OUTPUT###
notempty

mount --bind /dev/null ./test.txt
cat <./test.txt
###NO OUTPUT###

umount ./test.txt
cat <./test.txt
###OUTPUT###
notempty

Вы также можете:

mount --bind ./someotherconfig.conf ./unwanted.conf

Если бы вы хотели.

mikeserv
источник
По сути, это эквивалентно нескольким более ранним ответам (за исключением, я полагаю, этот требует, чтобы пользователь был привилегированным). ОП отклонил эти другие ответы, потому что он не хочет обманывать какой-либо процесс - он хочет обмануть пакетный вызов программы, позволяя интерактивному вызову нормально видеть файл конфигурации.
Скотт
@ Скотт - я не согласен - каждый второй ответ до этого рекомендовал некоторые изменения mvв файле - что может иметь иные последствия, кроме влияния на его зубцы, такие как фактическое усечение файла или другие и т. Д. - тогда как это действует только на ничего. Тем не менее, я должен unshareэто, mountя думаю ...
mikeserv