Процесс, запущенный из системной команды в C, наследует родительские fd's
У меня есть пример приложения SIP-сервера, который прослушивает как TCP, так и UDP-порты 5060.
В какой-то момент кода я делаю систему ( "pppd файл /etc/ppp/myoptions &" );
После этого, если я делаю netstat -apn, он показывает мне, что порты 5060 также открыты для pppd!
Есть ли способ избежать этого? Является ли это стандартным поведением системной функции в Linux?
Спасибо,
Elison
Ответы
Ответ 1
Да, по умолчанию всякий раз, когда вы форкируете процесс (который system
делает), ребенок наследует все дескрипторы родительского файла. Если ребенку не нужны эти дескрипторы, он ДОЛЖЕН закрыть их. Способ сделать это с помощью system
(или любого другого метода, который выполняет fork + exec) - установить флаг FD_CLOEXEC во всех дескрипторах файлов, которые не должны использоваться дочерними процессами вашего процесса. Это заставит их автоматически закрываться всякий раз, когда какой-либо дочерний элемент выполняет какую-либо другую программу.
В общем, ЛЮБОЕ ВРЕМЯ ваша программа открывает ЛЮБОЙ ВИДЕНЬ файлового дескриптора, который будет жить в течение длительного периода времени (например, в качестве примера в качестве примера в гнезде для прослушивания), и который не должен использоваться совместно с детьми, вы должны сделать
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
в дескрипторе файла.
По состоянию на 2016 год? пересмотр POSIX.1, вы можете использовать флаг SOCK_CLOEXEC
or'd в качестве сокета, чтобы автоматически получить это поведение при создании сокета:
listenfd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0);
bind(listenfd, ...
listen(listemfd, ...
который гарантирует, что он будет закрыт должным образом, даже если какой-либо другой одновременно выполняющийся поток выполняет вызов system
или fork
+ exec
. К счастью, этот флаг долгое время поддерживался на Linux и BSD-Unix (но не OSX, к сожалению).
Ответ 2
Вероятно, вы должны вообще избегать функции system()
. Это по своей сути опасно, поскольку оно вызывает оболочку, которая может быть изменена и, скорее, не переносима, даже между Unicies.
Что вы должны сделать, это танец fork()/exec()
. Это похоже на это
if(!fork()){
//close file descriptors
...
execlp("pppd", "pppd", "file", "/etc/ppp/myoptions", NULL);
perror("exec");
exit(-1);
}
Ответ 3
Да, это стандартное поведение fork()
в Linux, из которого реализовано system()
.
Идентификатор, возвращаемый из вызова socket()
, является допустимым файловым дескриптором. Это значение можно использовать с файловыми функциями, такими как read()
, write()
, ioctl()
и close()
.
Обратно, что каждый дескриптор файла является сокетом, это не так. Нельзя открыть обычный файл с помощью open()
и передать этот дескриптор, например, bind()
или listen()
.
Когда вы вызываете system()
, дочерний процесс наследует те же файловые дескрипторы, что и родительский. Вот как stdout
(0), stdin
(1) и stderr
(2) наследуются дочерними процессами. Если вы организуете открытие сокета с файловым дескриптором 0, 1 или 2, дочерний процесс наследует этот сокет как один из стандартных дескрипторов файла ввода/вывода.
Ваш дочерний процесс наследует каждый открытый файловый дескриптор от родителя, включая сокет, который вы открыли.
Ответ 4
Как утверждали другие, это стандартное поведение, от которого зависят программы.
Когда дело доходит до предотвращения этого, у вас есть несколько вариантов. Во-первых, закрывает все дескрипторы файлов после fork()
, как предлагает Дейв. Во-вторых, существует поддержка POSIX для использования fcntl
с FD_CLOEXEC
для установки бит "close on exec" на основе fd.
Наконец, однако, поскольку вы упоминаете, что вы работаете в Linux, есть набор изменений, которые позволят вам установить бит прямо в момент открытия. Естественно, это зависит от платформы. Обзор можно найти на http://udrepper.livejournal.com/20407.html
Это означает, что вы можете использовать поразрядный или с типом в вызове создания сокета, чтобы установить флаг SOCK_CLOEXEC
. Если вы используете ядро 2.6.27 или новее, то есть.
Ответ 5
system()
копирует текущий процесс, а затем запускает дочерний элемент поверх него. (текущий процесс больше не существует. Вероятно, поэтому pppd использует 5060. Вы можете попробовать fork()/exec()
создать дочерний процесс и сохранить родителя в живых.