/* * This file is part of Cockpit. * * Copyright (C) 2014 Red Hat, Inc. * * Cockpit is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * Cockpit is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Cockpit; If not, see . */ #include "config.h" #include "cockpitunixfd.h" #include #include #include #include #include #include typedef struct { GSource source; GPollFD pollfd; GIOCondition condition; } CockpitUnixFdSource; static gboolean unix_fd_prepare (GSource *source, gint *timeout) { CockpitUnixFdSource *us = (CockpitUnixFdSource *)source; *timeout = -1; us->pollfd.revents = 0; return FALSE; } static gboolean unix_fd_check (GSource *source) { CockpitUnixFdSource *us = (CockpitUnixFdSource *)source; return ((us->condition & us->pollfd.revents) != 0); } static gboolean unix_fd_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { CockpitUnixFdFunc func = (CockpitUnixFdFunc)callback; CockpitUnixFdSource *us = (CockpitUnixFdSource *)source; return (* func) (us->pollfd.fd, us->pollfd.revents & us->condition, user_data); } static GSourceFuncs unix_fd_funcs = { unix_fd_prepare, unix_fd_check, unix_fd_dispatch, }; GSource * cockpit_unix_fd_source_new (gint fd, GIOCondition condition) { GSource *source; CockpitUnixFdSource *us; condition |= G_IO_HUP | G_IO_ERR | G_IO_NVAL; source = g_source_new (&unix_fd_funcs, sizeof (CockpitUnixFdSource)); us = (CockpitUnixFdSource *)source; us->pollfd.fd = fd; us->condition = condition; us->pollfd.events = condition; us->pollfd.revents = 0; g_source_add_poll (source, &us->pollfd); return source; } guint cockpit_unix_fd_add (gint fd, GIOCondition condition, CockpitUnixFdFunc callback, gpointer user_data) { return cockpit_unix_fd_add_full (G_PRIORITY_DEFAULT, fd, condition, callback, user_data, NULL); } guint cockpit_unix_fd_add_full (gint priority, gint fd, GIOCondition condition, GUnixFDSourceFunc function, gpointer user_data, GDestroyNotify notify) { GSource *source; guint ret; source = cockpit_unix_fd_source_new (fd, condition); g_source_set_priority (source, priority); g_source_set_callback (source, (GSourceFunc)function, user_data, notify); ret = g_source_attach (source, NULL); g_source_unref (source); return ret; } typedef struct { int from; int except; int until; } CloseAll; static int closefd (void *data, gint fd) { CloseAll *ca = data; if (fd >= ca->from && fd != ca->except && fd < ca->until) { while (close (fd) < 0) { if (errno == EAGAIN || errno == EINTR) continue; if (errno == EBADF || errno == EINVAL) break; g_critical ("couldn't close fd in child process: %s", g_strerror (errno)); return -1; } } return 0; } #ifndef HAVE_FDWALK static int fdwalk (int (*cb)(void *data, int fd), void *data) { gint open_max; gint fd; gint res = 0; struct rlimit rl; #ifdef __linux__ DIR *d; if ((d = opendir("/proc/self/fd"))) { struct dirent *de; while ((de = readdir(d))) { glong l; gchar *e = NULL; if (de->d_name[0] == '.') continue; errno = 0; l = strtol(de->d_name, &e, 10); if (errno != 0 || !e || *e) continue; fd = (gint) l; if ((glong) fd != l) continue; if (fd == dirfd(d)) continue; if ((res = cb (data, fd)) != 0) break; } closedir(d); return res; } /* If /proc is not mounted or not accessible we fall back to the old * rlimit trick */ #endif if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY) open_max = rl.rlim_max; else open_max = sysconf (_SC_OPEN_MAX); for (fd = 0; fd < open_max; fd++) if ((res = cb (data, fd)) != 0) break; return res; } #endif /* HAVE_FDWALK */ /** * cockpit_unix_fd_close_all: * @from: minimum FD to close, or -1 * @except: an FD to leave open, or -1 * * Close all open file descriptors starting from @from * and skipping @except. * * Will set errno if a failure happens. * * Returns: zero if successful, -1 if not */ int cockpit_unix_fd_close_all (int from, int except) { CloseAll ca = { from, except, G_MAXINT }; return fdwalk (closefd, &ca); } /** * cockpit_unix_fd_close_until: * @from: minimum FD to close, or -1 * @except: an FD to leave open, or -1 * @until: stop closing fds when this number is hit. * * Close all open file descriptors starting from @from * and skipping @except up to but not including @until. * * Will set errno if a failure happens. * * Returns: zero if successful, -1 if not */ int cockpit_unix_fd_close_until (int from, int except, int until) { CloseAll ca = { from, except, until }; return fdwalk (closefd, &ca); }