diff --git a/src/client.cpp b/src/client.cpp index 44fbacd..241967c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,430 +1,409 @@ /* * This file is part of the KDE project, module kdesu. * Copyright (C) 1999,2000 Geert Jansen * * This is free software; you can use this library under the GNU Library * General Public License, version 2. See the file "COPYING.LIB" for the * exact licensing terms. * * client.cpp: A client for kdesud. */ #include "client.h" #include #include #include #include #include #include #include #include #include #include extern int kdesuDebugArea(); namespace KDESu { class Q_DECL_HIDDEN KDEsuClient::KDEsuClientPrivate { public: KDEsuClientPrivate() : sockfd(-1) {} QString daemon; int sockfd; QByteArray sock; }; #ifndef SUN_LEN #define SUN_LEN(ptr) ((QT_SOCKLEN_T) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif KDEsuClient::KDEsuClient() : d(new KDEsuClientPrivate) { #if HAVE_X11 QString display = QString::fromLocal8Bit(qgetenv("DISPLAY")); if (display.isEmpty()) { // we might be on Wayland display = QString::fromLocal8Bit(qgetenv("WAYLAND_DISPLAY")); } if (display.isEmpty()) { qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "$DISPLAY is not set."; return; } // strip the screen number from the display display.remove(QRegularExpression(QStringLiteral("\\.[0-9]+$"))); #else QString display = QStringLiteral("NODISPLAY"); #endif d->sock = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QStringLiteral("/kdesud_") + display); connect(); } KDEsuClient::~KDEsuClient() { if (d->sockfd >= 0) { close(d->sockfd); } delete d; } int KDEsuClient::connect() { if (d->sockfd >= 0) { close(d->sockfd); } if (access(d->sock.constData(), R_OK | W_OK)) { d->sockfd = -1; return -1; } d->sockfd = socket(PF_UNIX, SOCK_STREAM, 0); if (d->sockfd < 0) { qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "socket():" << strerror(errno); return -1; } struct sockaddr_un addr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path, d->sock.constData()); if (QT_SOCKET_CONNECT(d->sockfd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0) { qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "connect():" << strerror(errno); close(d->sockfd); d->sockfd = -1; return -1; } #if !defined(SO_PEERCRED) || ! HAVE_STRUCT_UCRED # if HAVE_GETPEEREID uid_t euid; gid_t egid; // Security: if socket exists, we must own it if (getpeereid(d->sockfd, &euid, &egid) == 0 && euid != getuid()) { qWarning() << "socket not owned by me! socket uid =" << euid; close(d->sockfd); d->sockfd = -1; return -1; } # else # ifdef __GNUC__ # warning "Using sloppy security checks" # endif // We check the owner of the socket after we have connected. // If the socket was somehow not ours an attacker will be able // to delete it after we connect but shouldn't be able to // create a socket that is owned by us. QT_STATBUF s; if (QT_LSTAT(d->sock.constData(), &s) != 0) { qWarning() << "stat failed (" << d->sock << ")"; close(d->sockfd); d->sockfd = -1; return -1; } if (s.st_uid != getuid()) { qWarning() << "socket not owned by me! socket uid =" << s.st_uid; close(d->sockfd); d->sockfd = -1; return -1; } if (!S_ISSOCK(s.st_mode)) { qWarning() << "socket is not a socket (" << d->sock << ")"; close(d->sockfd); d->sockfd = -1; return -1; } # endif #else struct ucred cred; QT_SOCKLEN_T siz = sizeof(cred); // Security: if socket exists, we must own it if (getsockopt(d->sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) == 0 && cred.uid != getuid()) { qWarning() << "socket not owned by me! socket uid =" << cred.uid; close(d->sockfd); d->sockfd = -1; return -1; } #endif return 0; } QByteArray KDEsuClient::escape(const QByteArray &str) { QByteArray copy; copy.reserve(str.size() + 4); copy.append('"'); for (int i = 0; i < str.size(); i++) { uchar c = str.at(i); if (c < 32) { copy.append('\\'); copy.append('^'); copy.append(c + '@'); } else { if (c == '\\' || c == '"') { copy.append('\\'); } copy.append(c); } } copy.append('"'); return copy; } int KDEsuClient::command(const QByteArray &cmd, QByteArray *result) { if (d->sockfd < 0) { return -1; } if (send(d->sockfd, cmd.constData(), cmd.length(), 0) != (int)cmd.length()) { return -1; } char buf[1024]; int nbytes = recv(d->sockfd, buf, 1023, 0); if (nbytes <= 0) { qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "no reply from daemon."; return -1; } buf[nbytes] = '\000'; QByteArray reply = buf; if (reply.left(2) != "OK") { return -1; } if (result) { *result = reply.mid(3, reply.length() - 4); } return 0; } int KDEsuClient::setPass(const char *pass, int timeout) { QByteArray cmd = "PASS "; cmd += escape(pass); cmd += ' '; cmd += QByteArray().setNum(timeout); cmd += '\n'; return command(cmd); } int KDEsuClient::exec(const QByteArray &prog, const QByteArray &user, const QByteArray &options, const QList &env) { QByteArray cmd; cmd = "EXEC "; cmd += escape(prog); cmd += ' '; cmd += escape(user); if (!options.isEmpty() || !env.isEmpty()) { cmd += ' '; cmd += escape(options); for (int i = 0; i < env.count(); ++i) { cmd += ' '; cmd += escape(env.at(i)); } } cmd += '\n'; return command(cmd); } int KDEsuClient::setHost(const QByteArray &host) { QByteArray cmd = "HOST "; cmd += escape(host); cmd += '\n'; return command(cmd); } int KDEsuClient::setPriority(int prio) { QByteArray cmd; cmd += "PRIO "; cmd += QByteArray::number(prio); cmd += '\n'; return command(cmd); } int KDEsuClient::setScheduler(int sched) { QByteArray cmd; cmd += "SCHD "; cmd += QByteArray::number(sched); cmd += '\n'; return command(cmd); } int KDEsuClient::delCommand(const QByteArray &key, const QByteArray &user) { QByteArray cmd = "DEL "; cmd += escape(key); cmd += ' '; cmd += escape(user); cmd += '\n'; return command(cmd); } int KDEsuClient::setVar(const QByteArray &key, const QByteArray &value, int timeout, const QByteArray &group) { QByteArray cmd = "SET "; cmd += escape(key); cmd += ' '; cmd += escape(value); cmd += ' '; cmd += escape(group); cmd += ' '; cmd += QByteArray().setNum(timeout); cmd += '\n'; return command(cmd); } QByteArray KDEsuClient::getVar(const QByteArray &key) { QByteArray cmd = "GET "; cmd += escape(key); cmd += '\n'; QByteArray reply; command(cmd, &reply); return reply; } QList KDEsuClient::getKeys(const QByteArray &group) { QByteArray cmd = "GETK "; cmd += escape(group); cmd += '\n'; QByteArray reply; command(cmd, &reply); int index = 0, pos; QList list; if (!reply.isEmpty()) { while (1) { pos = reply.indexOf('\007', index); if (pos == -1) { if (index == 0) { list.append(reply); } else { list.append(reply.mid(index)); } break; } else { list.append(reply.mid(index, pos - index)); } index = pos + 1; } } return list; } bool KDEsuClient::findGroup(const QByteArray &group) { QByteArray cmd = "CHKG "; cmd += escape(group); cmd += '\n'; if (command(cmd) == -1) { return false; } return true; } int KDEsuClient::delVar(const QByteArray &key) { QByteArray cmd = "DELV "; cmd += escape(key); cmd += '\n'; return command(cmd); } int KDEsuClient::delGroup(const QByteArray &group) { QByteArray cmd = "DELG "; cmd += escape(group); cmd += '\n'; return command(cmd); } int KDEsuClient::delVars(const QByteArray &special_key) { QByteArray cmd = "DELS "; cmd += escape(special_key); cmd += '\n'; return command(cmd); } int KDEsuClient::ping() { return command("PING\n"); } int KDEsuClient::exitCode() { QByteArray result; if (command("EXIT\n", &result) != 0) { return -1; } return result.toInt(); } int KDEsuClient::stopServer() { return command("STOP\n"); } static QString findDaemon() { QString daemon = QFile::decodeName(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "/kdesud"); if (!QFile::exists(daemon)) { // if not in libexec, find it in PATH daemon = QStandardPaths::findExecutable(QStringLiteral("kdesud")); if (daemon.isEmpty()) { qWarning() << "kdesud daemon not found."; } } return daemon; } -bool KDEsuClient::isServerSGID() -{ - if (d->daemon.isEmpty()) { - d->daemon = findDaemon(); - } - if (d->daemon.isEmpty()) { - return false; - } - - QT_STATBUF sbuf; - if (QT_STAT(QFile::encodeName(d->daemon).constData(), &sbuf) < 0) { - qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "stat():" << strerror(errno); - return false; - } - return (sbuf.st_mode & S_ISGID); -} - int KDEsuClient::startServer() { if (d->daemon.isEmpty()) { d->daemon = findDaemon(); } if (d->daemon.isEmpty()) { return -1; } - if (!isServerSGID()) { - qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "kdesud not setgid!"; - } - // kdesud only forks to the background after it is accepting // connections. // We start it via kdeinit to make sure that it doesn't inherit // any fd's from the parent process. int ret = KToolInvocation::kdeinitExecWait(d->daemon); connect(); return ret; } } // namespace KDESu diff --git a/src/client.h b/src/client.h index 987f137..539df81 100644 --- a/src/client.h +++ b/src/client.h @@ -1,206 +1,198 @@ /* * This file is part of the KDE project, module kdesu. * Copyright (C) 1999,2000 Geert Jansen * * This is free software; you can use this library under the GNU Library * General Public License, version 2. See the file "COPYING.LIB" for the * exact licensing terms. * * client.h: client to access kdesud. */ #ifndef KDESUCLIENT_H #define KDESUCLIENT_H #include #include #include #ifdef Q_OS_UNIX namespace KDESu { /** \class KDEsuClient client.h KDESu/Client * A client class to access kdesud, the KDE su daemon. Kdesud can assist in * password caching in two ways: * * @li For high security passwords, like for su and ssh, it executes the * password requesting command for you. It feeds the password to the - * command, without ever returning it to you, the user. The daemon should - * be installed setgid nogroup, in order to be able to act as an inaccessible, - * trusted 3rd party. + * command, without ever returning it to you, the user. * See exec, setPass, delCommand. * * @li For lower security passwords, like web and ftp passwords, it can act * as a persistent storage for string variables. These variables are - * returned to the user, and the daemon doesn't need to be setgid nogroup - * for this. + * returned to the user. * See setVar, delVar, delGroup. */ class KDESU_EXPORT KDEsuClient { public: KDEsuClient(); ~KDEsuClient(); KDEsuClient(const KDEsuClient &) = delete; KDEsuClient &operator=(const KDEsuClient &) = delete; /** * Lets kdesud execute a command. If the daemon does not have a password * for this command, this will fail and you need to call setPass(). * * @param command The command to execute. * @param user The user to run the command as. * @param options Extra options. * @param env Extra environment variables. * @return Zero on success, -1 on failure. */ int exec(const QByteArray &command, const QByteArray &user, const QByteArray &options = nullptr, const QList &env = QList()); /** * Wait for the last command to exit and return the exit code. * @return Exit code of last command, -1 on failure. */ int exitCode(); /** * Set root's password, lasts one session. * * @param pass Root's password. * @param timeout The time that a password will live. * @return Zero on success, -1 on failure. */ int setPass(const char *pass, int timeout); /** * Set the target host (optional). */ int setHost(const QByteArray &host); /** * Set the desired priority (optional), see StubProcess. */ int setPriority(int priority); /** * Set the desired scheduler (optional), see StubProcess. */ int setScheduler(int scheduler); /** * Remove a password for a user/command. * @param command The command. * @param user The user. * @return zero on success, -1 on an error */ int delCommand(const QByteArray &command, const QByteArray &user); /** * Set a persistent variable. * @param key The name of the variable. * @param value Its value. * @param timeout The timeout in seconds for this key. Zero means * no timeout. * @param group Make the key part of a group. See delGroup. * @return zero on success, -1 on failure. */ int setVar(const QByteArray &key, const QByteArray &value, int timeout = 0, const QByteArray &group = nullptr); /** * Get a persistent variable. * @param key The name of the variable. * @return Its value. */ QByteArray getVar(const QByteArray &key); /** * Gets all the keys that are membes of the given group. * @param group the group name of the variables. * @return a list of the keys in the group. */ QList getKeys(const QByteArray &group); /** * Returns true if the specified group exists is * cached. * * @param group the group key * @return true if the group is found */ bool findGroup(const QByteArray &group); /** * Delete a persistent variable. * @param key The name of the variable. * @return zero on success, -1 on failure. */ int delVar(const QByteArray &key); /** * Delete all persistent variables with the given key. * * A specicalized variant of delVar(QByteArray) that removes all * subsets of the cached varaibles given by @p key. In order for all * cached variables related to this key to be deleted properly, the * value given to the @p group argument when the setVar function * was called, must be a subset of the argument given here and the key * * @note Simply supplying the group key here WILL not necessarily * work. If you only have a group key, then use delGroup instead. * * @param special_key the name of the variable. * @return zero on success, -1 on failure. */ int delVars(const QByteArray &special_key); /** * Delete all persistent variables in a group. * * @param group the group name. See setVar. * @return */ int delGroup(const QByteArray &group); /** * Ping kdesud. This can be used for diagnostics. * @return Zero on success, -1 on failure */ int ping(); /** * Stop the daemon. */ int stopServer(); /** * Try to start up kdesud */ int startServer(); - /** - * Returns true if the server is safe (installed setgid), false otherwise. - */ - bool isServerSGID(); - private: int connect(); int command(const QByteArray &cmd, QByteArray *result = nullptr); QByteArray escape(const QByteArray &str); class KDEsuClientPrivate; KDEsuClientPrivate *const d; }; } //END namespace KDESu #endif //Q_OS_UNIX #endif //KDESUCLIENT_H diff --git a/src/kdesud/CMakeLists.txt b/src/kdesud/CMakeLists.txt index 279af35..6844902 100644 --- a/src/kdesud/CMakeLists.txt +++ b/src/kdesud/CMakeLists.txt @@ -1,35 +1,31 @@ include(CheckFunctionExists) check_function_exists(getpeereid HAVE_GETPEEREID) # openbsd style check_function_exists(getpeereucred HAVE_GETPEERUCRED) # solaris style configure_file (config-kdesud.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kdesud.h ) set(kdesud_SRCS kdesud.cpp repo.cpp lexer.cpp handler.cpp secure.cpp ) add_executable(kdesud ${kdesud_SRCS}) ecm_mark_nongui_executable(kdesud) target_link_libraries(kdesud KF5::Su KF5::I18n ${X11_LIBRARIES}) if(HAVE_X11) target_include_directories(kdesud PRIVATE ${X11_X11_INCLUDE_PATH}) endif() if(KDE4_ENABLE_FPIE) macro_add_compile_flags(kdesud ${KDE4_CXX_FPIE_FLAGS}) macro_add_link_flags(kdesud ${KDE4_PIE_LDFLAGS}) endif() ########### install files ############### install(TARGETS kdesud DESTINATION ${KDE_INSTALL_LIBEXECDIR_KF5}) -install(CODE " - set(KDESUD_PATH \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LIBEXECDIR_KF5}/kdesud\") - execute_process(COMMAND sh -c \"chgrp nogroup '\${KDESUD_PATH}' && chmod g+s '\${KDESUD_PATH}'\") -") diff --git a/src/kdesud/kdesud.cpp b/src/kdesud/kdesud.cpp index 3cb7737..d16b04f 100644 --- a/src/kdesud/kdesud.cpp +++ b/src/kdesud/kdesud.cpp @@ -1,452 +1,474 @@ /* vi: ts=8 sts=4 sw=4 * * This file is part of the KDE project, module kdesu. * Copyright (C) 1999,2000 Geert Jansen * * * kdesud.cpp: KDE su daemon. Offers "keep password" functionality to kde su. * * The socket $KDEHOME/socket-$(HOSTNAME)/kdesud_$(display) is used for communication with * client programs. * * The protocol: Client initiates the connection. All commands and responses * are terminated by a newline. * * Client Server Description * ------ ------ ----------- * * PASS OK Set password for commands in * this session. Password is * valid for seconds. * * USER OK Set the target user [required] * * EXEC OK Execute command . If * NO has been executed * before (< timeout) no PASS * command is needed. * * DEL OK Delete password for command * NO . * * PING OK Ping the server (diagnostics). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SELECT_H #include // Needed on some systems. #endif #include #include #include #include #include #include #include #include #include #include #include #include "repo.h" #include "handler.h" #if HAVE_X11 #include #include #endif +#ifdef __FreeBSD__ +#include +#else +#include +#endif + #ifndef SUN_LEN #define SUN_LEN(ptr) ((socklen_t) \ (offsetof(struct sockaddr_un, sun_path) + strlen ((ptr)->sun_path))) #endif #define ERR strerror(errno) static QLoggingCategory category("org.kde.kdesud"); using namespace KDESu; // Globals Repository *repo; QString Version(QStringLiteral("1.01")); QByteArray sock; #if HAVE_X11 Display *x11Display; #endif int pipeOfDeath[2]; void kdesud_cleanup() { unlink(sock.constData()); } // Borrowed from kdebase/kaudio/kaudioserver.cpp #if HAVE_X11 extern "C" int xio_errhandler(Display *); int xio_errhandler(Display *) { qCritical() << "Fatal IO error, exiting...\n"; kdesud_cleanup(); exit(1); return 1; //silence compilers } int initXconnection() { x11Display = XOpenDisplay(nullptr); if (x11Display != nullptr) { XSetIOErrorHandler(xio_errhandler); XCreateSimpleWindow(x11Display, DefaultRootWindow(x11Display), 0, 0, 1, 1, 0, BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)), BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display))); return XConnectionNumber(x11Display); } else { qCWarning(category) << "Can't connect to the X Server.\n"; qCWarning(category) << "Might not terminate at end of session.\n"; return -1; } } #endif extern "C" { void signal_exit(int); void sigchld_handler(int); } void signal_exit(int sig) { qCDebug(category) << "Exiting on signal " << sig << "\n"; kdesud_cleanup(); exit(1); } void sigchld_handler(int) { char c = ' '; write(pipeOfDeath[1], &c, 1); } /** * Creates an AF_UNIX socket in socket resource, mode 0600. */ int create_socket() { int sockfd; socklen_t addrlen; struct stat s; QString display = QString::fromLocal8Bit(qgetenv("DISPLAY")); if (display.isEmpty()) { qCWarning(category) << "$DISPLAY is not set\n"; return -1; } // strip the screen number from the display display.remove(QRegularExpression(QStringLiteral("\\.[0-9]+$"))); sock = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QStringLiteral("/kdesud_%1").arg(display)); int stat_err=lstat(sock.constData(), &s); if(!stat_err && S_ISLNK(s.st_mode)) { qCWarning(category) << "Someone is running a symlink attack on you\n"; if(unlink(sock.constData())) { qCWarning(category) << "Could not delete symlink\n"; return -1; } } if (!access(sock.constData(), R_OK|W_OK)) { KDEsuClient client; if (client.ping() == -1) { qCWarning(category) << "stale socket exists\n"; if (unlink(sock.constData())) { qCWarning(category) << "Could not delete stale socket\n"; return -1; } } else { qCWarning(category) << "kdesud is already running\n"; return -1; } } sockfd = socket(PF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) { qCritical() << "socket(): " << ERR << "\n"; return -1; } // Ensure socket closed on error struct fd_ScopeGuard { fd_ScopeGuard(int fd) : _fd(fd) { } ~fd_ScopeGuard() { if(_fd >= 0) { close(_fd); } } fd_ScopeGuard(const fd_ScopeGuard &) = delete; fd_ScopeGuard &operator=(const fd_ScopeGuard &) = delete; void reset() { _fd = -1; } int _fd; } guard(sockfd); struct sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sock.constData(), sizeof(addr.sun_path)-1); addr.sun_path[sizeof(addr.sun_path)-1] = '\000'; addrlen = SUN_LEN(&addr); if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0) { qCritical() << "bind(): " << ERR << "\n"; return -1; } struct linger lin; lin.l_onoff = lin.l_linger = 0; if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &lin, sizeof(linger)) < 0) { qCritical() << "setsockopt(SO_LINGER): " << ERR << "\n"; return -1; } int opt = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) { qCritical() << "setsockopt(SO_REUSEADDR): " << ERR << "\n"; return -1; } opt = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt)) < 0) { qCritical() << "setsockopt(SO_KEEPALIVE): " << ERR << "\n"; return -1; } chmod(sock.constData(), 0600); guard.reset(); return sockfd; } +/* The daemon stores passwords, which we don't want any other process to be able to read. */ +static void prevent_tracing() +{ + int r; +#ifdef PR_SET_DUMPABLE + r = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); +#elif defined(PROC_TRACE_CTL) + int disable = PROC_TRACE_CTL_DISABLE_EXEC; + r = procctl(P_PID, getpid(), PROC_TRACE_CTL, &disable); +#endif + if (r == -1) { + qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "failed to prevent process memory from being read" << strerror(errno) << "\n"; + } +} /** * Main program */ int main(int argc, char *argv[]) { + prevent_tracing(); + QCoreApplication app(argc, argv); KAboutData aboutData( QStringLiteral("kdesud") /* componentName */, i18n("KDE su daemon"), Version, i18n("Daemon used by kdesu"), KAboutLicense::Artistic, i18n("Copyright (c) 1999,2000 Geert Jansen")); aboutData.addAuthor(i18n("Geert Jansen"), i18n("Author"), QStringLiteral("jansen@kde.org"), QStringLiteral("http://www.stack.nl/~geertj/")); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); // Set core dump size to 0 struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rlim) < 0) { qCritical() << "setrlimit(): " << ERR << "\n"; exit(1); } // Create the Unix socket. int sockfd = create_socket(); if (sockfd < 0) exit(1); if (listen(sockfd, 10) < 0) { qCritical() << "listen(): " << ERR << "\n"; kdesud_cleanup(); exit(1); } int maxfd = sockfd; // Ok, we're accepting connections. Fork to the background. pid_t pid = fork(); if (pid == -1) { qCritical() << "fork():" << ERR << "\n"; kdesud_cleanup(); exit(1); } if (pid) _exit(0); #if HAVE_X11 // Make sure we exit when the display gets closed. int x11Fd = initXconnection(); maxfd = qMax(maxfd, x11Fd); #endif repo = new Repository; QVector handler; pipe(pipeOfDeath); maxfd = qMax(maxfd, pipeOfDeath[0]); // Signal handlers struct sigaction sa; sa.sa_handler = signal_exit; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGHUP, &sa, nullptr); sigaction(SIGINT, &sa, nullptr); sigaction(SIGTERM, &sa, nullptr); sigaction(SIGQUIT, &sa, nullptr); sa.sa_handler = sigchld_handler; sa.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &sa, nullptr); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, nullptr); // Main execution loop socklen_t addrlen; struct sockaddr_un clientname; fd_set tmp_fds, active_fds; FD_ZERO(&active_fds); FD_SET(sockfd, &active_fds); FD_SET(pipeOfDeath[0], &active_fds); #if HAVE_X11 if (x11Fd != -1) FD_SET(x11Fd, &active_fds); #endif while (1) { tmp_fds = active_fds; #if HAVE_X11 if(x11Display) XFlush(x11Display); #endif if (select(maxfd+1, &tmp_fds, nullptr, nullptr, nullptr) < 0) { if (errno == EINTR) continue; qCritical() << "select(): " << ERR << "\n"; exit(1); } repo->expire(); for (int i=0; i<=maxfd; i++) { if (!FD_ISSET(i, &tmp_fds)) continue; if (i == pipeOfDeath[0]) { char buf[101]; read(pipeOfDeath[0], buf, 100); pid_t result; do { int status; result = waitpid((pid_t)-1, &status, WNOHANG); if (result > 0) { for(int j=handler.size(); j--;) { if (handler[j] && (handler[j]->m_pid == result)) { handler[j]->m_exitCode = WEXITSTATUS(status); handler[j]->m_hasExitCode = true; handler[j]->sendExitCode(); handler[j]->m_pid = 0; break; } } } } while(result > 0); } #if HAVE_X11 if (i == x11Fd) { // Discard X events XEvent event_return; if (x11Display) while(XPending(x11Display)) XNextEvent(x11Display, &event_return); continue; } #endif if (i == sockfd) { // Accept new connection int fd; addrlen = 64; fd = accept(sockfd, (struct sockaddr *) &clientname, &addrlen); if (fd < 0) { qCritical() << "accept():" << ERR << "\n"; continue; } while (fd+1 > (int) handler.size()) handler.append(nullptr); delete handler[fd]; handler[fd] = new ConnectionHandler(fd); maxfd = qMax(maxfd, fd); FD_SET(fd, &active_fds); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); continue; } // handle already established connection if (handler[i] && handler[i]->handle() < 0) { delete handler[i]; handler[i] = nullptr; FD_CLR(i, &active_fds); } } } qCWarning(category) << "???\n"; }