stdbuf с setuid/ возможностями
Я читаю вывод из другого процесса, который генерирует вывод (медленный и бесконечный). Поскольку я хочу читать эти данные в режиме реального времени, я использую "stdbuf -oL" (с буферизацией строки, данные являются текстовыми). У меня нет контроля над процессом генерации, поэтому я не могу изменить источник для принудительной очистки.
Пока что stdbuf работает просто отлично, однако процесс использует SOCK_RAW и должен быть запущен от имени пользователя root, иметь setuid(0) или cap_net_raw
возможность. При запуске без полномочий root с setuid или возможностями stdbuf, похоже, игнорируется. Позвольте мне продемонстрировать проблему:
Это простой писатель:
#include <stdio.h>
#include <unistd.h>
int main(){
int i;
for ( i = 0;; i++){
fprintf(stdout, "%d\n", i);
sleep(1);
}
}
И простой читатель:
#include <stdio.h>
int main(){
char* line = NULL;
size_t n = 0;
while (getline(&line, &n, stdin) != -1 ) {
fputs(line, stdout);
}
}
Как и ожидалось, выполняя ./writer | ./reader
ничего не появляется, пока буфер не заполнен. Предварение stdbuf -oL
включает буферизацию строки, и я получаю строки в считыватель:
% stdbuf -oL ./writer | ./reader
0
1
2
...
Но если я добавлю cap_net_raw+ep
перестает работать:
% sudo setcap cap_net_raw+ep ./writer
% stdbuf -oL ./writer | ./reader
(no output)
Такое же поведение наблюдается при использовании setuid:
% sudo chown root:root ./writer
% sudo chmod +s ./writer
% stdbuf -oL ./writer | ./reader
(no output)
Мне интересно понять, почему это происходит и как я могу продолжать использовать stdbuf, не работая от имени пользователя root. Я признаю, что не до конца понимаю, что setuid делает за кулисами.
2 ответа
Если посмотреть на исходный код stdbuf, похоже, что он работает, установив LD_PRELOAD. Конечно, существуют проблемы безопасности при использовании LD_PRELOAD с исполняемыми файлами setuid или sudo.
Одно предложение, которое я нашел, было отключить атрибут noatsecure selinux для вашего исполняемого файла.
Другой, более простой вариант - избегать stdbuf и просто вызывать fflush(stdout)
из вашего исходного кода напрямую.
Решение без LD_PRELOAD
Вы можете использовать unbuffer
утилита, которая является частью expect
(expect-devel
) пакет. unbuffer
очень короткий сценарий ожидания. Не нужно LD_PRELOAD
потому что он использует другой трюк. expect
создает псевдо-терминал (как xterm
или же ssh
) поэтому процесс выполняется с использованием unbuffer
обманут, думая, что пишет на интерактивное устройство, поэтому по умолчанию он использует буферизацию строки stdout
,
Использование в вашем случае:
unbuffer ./writer | ./reader
Если stdbuf
работает с программой unbuffer
будет работать с большой вероятностью тоже. Так как LD_PRELOAD
накладывает некоторые ограничения unbuffer
имеет преимущества перед stdbuf
, Вопреки stdbuf
он будет работать с такими исполняемыми файлами:
- УИП
- с файловыми возможностями
- статически связаны
- не используя стандарт
libc