Получение наивысшего выделенного файлового дескриптора

Есть ли переносимый способ (POSIX), чтобы получить наивысший номер дескриптора файла для текущего процесса?

Я знаю, что есть хороший способ получить номер в AIX, например, но я ищу переносимый метод.

Причина, по которой я спрашиваю, заключается в том, что я хочу закрыть все дескрипторы открытых файлов. Моя программа – это сервер, который работает под управлением root и forks и выполняет дочерние программы для пользователей без полномочий root. Предоставление привилегированных дескрипторов файлов, открытых в дочернем процессе, является проблемой безопасности. Некоторые файловые дескрипторы могут быть открыты кодом, который я не могу контролировать (библиотека C, сторонние библиотеки и т. Д.), Поэтому я не могу полагаться и на FD_CLOEXEC .

Хотя переносимость, закрытие всех дескрипторов файлов до sysconf(_SC_OPEN_MAX) не является надежным, потому что в большинстве систем этот вызов возвращает текущий мягкий лимит дескриптора файла, который можно было бы опустить ниже наивысшего используемого дескриптора файла. Другая проблема заключается в том, что во многих системах sysconf(_SC_OPEN_MAX) может возвращать INT_MAX , что может привести к INT_MAX , что этот подход будет неприемлемо медленным. К сожалению, нет надежной переносимой альтернативы, которая не предполагает повторения всех возможных неотрицательных дескрипторов файла.

Хотя они не переносимы, большинство современных операционных систем сегодня предлагают одно или несколько из следующих решений этой проблемы:

  1. Функция библиотеки, чтобы закрыть все дескрипторы файлов > = fd . Это самое простое решение для общего случая закрытия всех дескрипторов файлов, хотя его нельзя использовать для других. Чтобы закрыть все дескрипторы файлов, за исключением определенного набора, dup2 можно использовать, чтобы заранее переместить их в нижний конец и, если необходимо, переместить их назад.

    • closefrom(fd) (Solaris 9 или более поздняя closefrom(fd) , FreeBSD 7.3 или 8.0 и выше, NetBSD 3.0 или новее, OpenBSD 3.5 или новее).

    • fcntl(fd, F_CLOSEM, 0) (AIX, IRIX, NetBSD)

  2. Функция библиотеки, обеспечивающая максимальный файловый дескриптор, используемый в данный момент процессом. Чтобы закрыть все дескрипторы файлов выше определенного числа, либо закройте все их до этого максимума, либо постоянно получите и закройте наивысший дескриптор файла в цикле, пока не будет достигнута нижняя граница. Что более эффективно, зависит от плотности дескриптора файла.

    • fcntl(0, F_MAXFD) (NetBSD)

    • pstat_getproc(&ps, sizeof(struct pst_status), (size_t)0, (int)getpid())
      Возвращает информацию о процессе, включая самый высокий файловый дескриптор, открытый в настоящее время в ps.pst_highestfd . (HP-UX)

  3. Каталог, содержащий запись для каждого дескриптора открытого файла . Это самый гибкий подход, поскольку он позволяет закрывать все дескрипторы файлов, находить наивысший дескриптор файла или делать что-либо еще в каждом дескрипторе открытого файла, даже в отношении другого процесса (в большинстве систем). Однако это может быть более сложным, чем другие подходы к общему использованию. Кроме того, он может выйти из строя по нескольким причинам, таким как proc / fdescfs, не установленный, среда chroot или отсутствующие файловые дескрипторы для открытия каталога (процесс или системный лимит). Поэтому использование этого подхода часто сочетается с резервным механизмом. Пример (OpenSSH) , еще один пример (glib) .

    • /proc/ pid /fd/ or /proc/self/fd/ (Linux, Solaris, AIX, Cygwin, NetBSD)
      (AIX не поддерживает « self »)

    • /dev/fd/ (FreeBSD, Darwin, OS X)

    С этим подходом трудно справиться со всеми угловыми корпусами. Например, рассмотрим ситуацию, когда все дескрипторы файлов> = fd должны быть закрыты, но используются все файловые дескрипторы < fd , текущий предел ресурса процесса – fd , и используются файловые дескрипторы> = fd . Поскольку предел ресурса процесса достигнут, каталог не может быть открыт. Если закрытие каждого дескриптора файла из fd через ограничение ресурса или sysconf(_SC_OPEN_MAX) используется в качестве резервной sysconf(_SC_OPEN_MAX) , ничего не будет закрыто.

Способ POSIX:

 int maxfd=sysconf(_SC_OPEN_MAX); for(int fd=3; fd 

(обратите внимание, что закрытие с 3 до, чтобы сохранить stdin / stdout / stderr открытым)

close () безобидно возвращает EBADF, если файловый дескриптор не открыт. Нет необходимости тратить еще один системный вызов.

Некоторые Unixes поддерживают closefrom (). Это позволяет избежать чрезмерного количества вызовов для закрытия () в зависимости от максимально возможного номера дескриптора файла. В то время как лучшее решение, о котором я знаю, это совершенно непереносимо.

Я написал код, чтобы иметь дело со всеми специфичными для платформы функциями. Все функции являются безопасными для асинхронного сигнала. Мысль людей может найти это полезным. В настоящее время тестируется только на OS X, не стесняйтесь улучшать / исправлять.

 // Async-signal safe way to get the current process's hard file descriptor limit. static int getFileDescriptorLimit() { long long sysconfResult = sysconf(_SC_OPEN_MAX); struct rlimit rl; long long rlimitResult; if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { rlimitResult = 0; } else { rlimitResult = (long long) rl.rlim_max; } long result; if (sysconfResult > rlimitResult) { result = sysconfResult; } else { result = rlimitResult; } if (result < 0) { // Both calls returned errors. result = 9999; } else if (result < 2) { // The calls reported broken values. result = 2; } return result; } // Async-signal safe function to get the highest file // descriptor that the process is currently using. // See also http://stackoverflow.com/questions/899038/getting-the-highest-allocated-file-descriptor static int getHighestFileDescriptor() { #if defined(F_MAXFD) int ret; do { ret = fcntl(0, F_MAXFD); } while (ret == -1 && errno == EINTR); if (ret == -1) { ret = getFileDescriptorLimit(); } return ret; #else int p[2], ret, flags; pid_t pid = -1; int result = -1; /* Since opendir() may not be async signal safe and thus may lock up * or crash, we use it in a child process which we kill if we notice * that things are going wrong. */ // Make a pipe. p[0] = p[1] = -1; do { ret = pipe(p); } while (ret == -1 && errno == EINTR); if (ret == -1) { goto done; } // Make the read side non-blocking. do { flags = fcntl(p[0], F_GETFL); } while (flags == -1 && errno == EINTR); if (flags == -1) { goto done; } do { fcntl(p[0], F_SETFL, flags | O_NONBLOCK); } while (ret == -1 && errno == EINTR); if (ret == -1) { goto done; } do { pid = fork(); } while (pid == -1 && errno == EINTR); if (pid == 0) { // Don't close p[0] here or it might affect the result. resetSignalHandlersAndMask(); struct sigaction action; action.sa_handler = _exit; action.sa_flags = SA_RESTART; sigemptyset(&action.sa_mask); sigaction(SIGSEGV, &action, NULL); sigaction(SIGPIPE, &action, NULL); sigaction(SIGBUS, &action, NULL); sigaction(SIGILL, &action, NULL); sigaction(SIGFPE, &action, NULL); sigaction(SIGABRT, &action, NULL); DIR *dir = NULL; #ifdef __APPLE__ /* /dev/fd can always be trusted on OS X. */ dir = opendir("/dev/fd"); #else /* On FreeBSD and possibly other operating systems, /dev/fd only * works if fdescfs is mounted. If it isn't mounted then /dev/fd * still exists but always returns [0, 1, 2] and thus can't be * trusted. If /dev and /dev/fd are on different filesystems * then that probably means fdescfs is mounted. */ struct stat dirbuf1, dirbuf2; if (stat("/dev", &dirbuf1) == -1 || stat("/dev/fd", &dirbuf2) == -1) { _exit(1); } if (dirbuf1.st_dev != dirbuf2.st_dev) { dir = opendir("/dev/fd"); } #endif if (dir == NULL) { dir = opendir("/proc/self/fd"); if (dir == NULL) { _exit(1); } } struct dirent *ent; union { int highest; char data[sizeof(int)]; } u; u.highest = -1; while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] != '.') { int number = atoi(ent->d_name); if (number > u.highest) { u.highest = number; } } } if (u.highest != -1) { ssize_t ret, written = 0; do { ret = write(p[1], u.data + written, sizeof(int) - written); if (ret == -1) { _exit(1); } written += ret; } while (written < (ssize_t) sizeof(int)); } closedir(dir); _exit(0); } else if (pid == -1) { goto done; } else { do { ret = close(p[1]); } while (ret == -1 && errno == EINTR); p[1] = -1; union { int highest; char data[sizeof(int)]; } u; ssize_t ret, bytesRead = 0; struct pollfd pfd; pfd.fd = p[0]; pfd.events = POLLIN; do { do { // The child process must finish within 30 ms, otherwise // we might as well query sysconf. ret = poll(&pfd, 1, 30); } while (ret == -1 && errno == EINTR); if (ret <= 0) { goto done; } do { ret = read(p[0], u.data + bytesRead, sizeof(int) - bytesRead); } while (ret == -1 && ret == EINTR); if (ret == -1) { if (errno != EAGAIN) { goto done; } } else if (ret == 0) { goto done; } else { bytesRead += ret; } } while (bytesRead < (ssize_t) sizeof(int)); result = u.highest; goto done; } done: if (p[0] != -1) { do { ret = close(p[0]); } while (ret == -1 && errno == EINTR); } if (p[1] != -1) { do { close(p[1]); } while (ret == -1 && errno == EINTR); } if (pid != -1) { do { ret = kill(pid, SIGKILL); } while (ret == -1 && errno == EINTR); do { ret = waitpid(pid, NULL, 0); } while (ret == -1 && errno == EINTR); } if (result == -1) { result = getFileDescriptorLimit(); } return result; #endif } void closeAllFileDescriptors(int lastToKeepOpen) { #if defined(F_CLOSEM) int ret; do { ret = fcntl(lastToKeepOpen + 1, F_CLOSEM); } while (ret == -1 && errno == EINTR); if (ret != -1) { return; } #elif defined(HAS_CLOSEFROM) closefrom(lastToKeepOpen + 1); return; #endif for (int i = getHighestFileDescriptor(); i > lastToKeepOpen; i--) { int ret; do { ret = close(i); } while (ret == -1 && errno == EINTR); } } 

Прямо, когда ваша программа началась и ничего не открыла. Например, как начало main (). трубу и вилку, сразу же запускающие сервер запуска. Таким образом, память и другие детали чисты, и вы можете просто передать ее вещам fork & exec.

 #include  #include  #include  #include  struct PipeStreamHandles { /** Write to this */ int output; /** Read from this */ int input; /** true if this process is the child after a fork */ bool isChild; pid_t childProcessId; }; PipeStreamHandles forkFullDuplex(){ int childInput[2]; int childOutput[2]; pipe(childInput); pipe(childOutput); pid_t pid = fork(); PipeStreamHandles streams; if(pid == 0){ // child close(childInput[1]); close(childOutput[0]); streams.output = childOutput[1]; streams.input = childInput[0]; streams.isChild = true; streams.childProcessId = getpid(); } else { close(childInput[0]); close(childOutput[1]); streams.output = childInput[1]; streams.input = childOutput[0]; streams.isChild = false; streams.childProcessId = pid; } return streams; } struct ExecuteData { char command[2048]; bool shouldExit; }; ExecuteData getCommand() { // maybe use json or semething to read what to execute // environment if any and etc.. // you can read via stdin because of the dup setup we did // in setupExecutor ExecuteData data; memset(&data, 0, sizeof(data)); data.shouldExit = fgets(data.command, 2047, stdin) == NULL; return data; } void executorServer(){ while(true){ printf("executor server waiting for command\n"); // maybe use json or semething to read what to execute // environment if any and etc.. ExecuteData command = getCommand(); // one way is for getCommand() to check if stdin is gone // that way you can set shouldExit to true if(command.shouldExit){ break; } printf("executor server doing command %s", command.command); system(command.command); // free command resources. } } static PipeStreamHandles executorStreams; void setupExecutor(){ PipeStreamHandles handles = forkFullDuplex(); if(handles.isChild){ // This simplifies so we can just use standard IO dup2(handles.input, 0); // we comment this out so we see output. // dup2(handles.output, 1); close(handles.input); // we uncomment this one so we can see hello world // if you want to capture the output you will want this. //close(handles.output); handles.input = 0; handles.output = 1; printf("started child\n"); executorServer(); printf("exiting executor\n"); exit(0); } executorStreams = handles; } /** Only has 0, 1, 2 file descriptiors open */ pid_t cleanForkAndExecute(const char *command) { // You can do json and use a json parser might be better // so you can pass other data like environment perhaps. // and also be able to return details like new proccess id so you can // wait if it's done and ask other relevant questions. write(executorStreams.output, command, strlen(command)); write(executorStreams.output, "\n", 1); } int main () { // needs to be done early so future fds do not get open setupExecutor(); // run your program as usual. cleanForkAndExecute("echo hello world"); sleep(3); } в #include  #include  #include  #include  struct PipeStreamHandles { /** Write to this */ int output; /** Read from this */ int input; /** true if this process is the child after a fork */ bool isChild; pid_t childProcessId; }; PipeStreamHandles forkFullDuplex(){ int childInput[2]; int childOutput[2]; pipe(childInput); pipe(childOutput); pid_t pid = fork(); PipeStreamHandles streams; if(pid == 0){ // child close(childInput[1]); close(childOutput[0]); streams.output = childOutput[1]; streams.input = childInput[0]; streams.isChild = true; streams.childProcessId = getpid(); } else { close(childInput[0]); close(childOutput[1]); streams.output = childInput[1]; streams.input = childOutput[0]; streams.isChild = false; streams.childProcessId = pid; } return streams; } struct ExecuteData { char command[2048]; bool shouldExit; }; ExecuteData getCommand() { // maybe use json or semething to read what to execute // environment if any and etc.. // you can read via stdin because of the dup setup we did // in setupExecutor ExecuteData data; memset(&data, 0, sizeof(data)); data.shouldExit = fgets(data.command, 2047, stdin) == NULL; return data; } void executorServer(){ while(true){ printf("executor server waiting for command\n"); // maybe use json or semething to read what to execute // environment if any and etc.. ExecuteData command = getCommand(); // one way is for getCommand() to check if stdin is gone // that way you can set shouldExit to true if(command.shouldExit){ break; } printf("executor server doing command %s", command.command); system(command.command); // free command resources. } } static PipeStreamHandles executorStreams; void setupExecutor(){ PipeStreamHandles handles = forkFullDuplex(); if(handles.isChild){ // This simplifies so we can just use standard IO dup2(handles.input, 0); // we comment this out so we see output. // dup2(handles.output, 1); close(handles.input); // we uncomment this one so we can see hello world // if you want to capture the output you will want this. //close(handles.output); handles.input = 0; handles.output = 1; printf("started child\n"); executorServer(); printf("exiting executor\n"); exit(0); } executorStreams = handles; } /** Only has 0, 1, 2 file descriptiors open */ pid_t cleanForkAndExecute(const char *command) { // You can do json and use a json parser might be better // so you can pass other data like environment perhaps. // and also be able to return details like new proccess id so you can // wait if it's done and ask other relevant questions. write(executorStreams.output, command, strlen(command)); write(executorStreams.output, "\n", 1); } int main () { // needs to be done early so future fds do not get open setupExecutor(); // run your program as usual. cleanForkAndExecute("echo hello world"); sleep(3); } 

Если вы хотите выполнить IO в исполняемой программе, серверу-исполнителю придется выполнять переадресацию сокетов, и вы можете использовать сокеты unix.

Почему бы вам не закрыть все дескрипторы от 0 до, скажем, 10000.

Это будет довольно быстро, и самое худшее, что произойдет, – это EBADF.

  • Подключение n команд с помощью труб в оболочке?
  • Автоматически выполняемые функции при загрузке разделяемых библиотек
  • Как открываются флаги O_SYNC и O_DIRECT (2) разные / одинаковые?
  • Почему регистр NTFS чувствителен?
  • Задача Cron для работы в последний день месяца
  • Как удалить файл со странным именем?
  • Почему существует SIGPIPE?
  • Действительно ли printf всегда очищает буфер при столкновении с новой линией?
  • Различия между / usr / bin / login и / usr / bin / bash
  • Давайте будем гением компьютера.