diff --git a/src/kcrash.cpp b/src/kcrash.cpp --- a/src/kcrash.cpp +++ b/src/kcrash.cpp @@ -612,6 +612,12 @@ static int read_socket(int sock, char *buffer, int len); static int openSocket(); +#ifdef Q_OS_LINUX +static int openDrKonqiSocket(int pid); +static int pollDrKonqiSocket(int pid, int sockfd); +static void closeDrKonqiSocket(int pid, int sockfd); +#endif + void KCrash::startProcess(int argc, const char *argv[], bool waitAndExit) { bool startDirectly = true; @@ -648,17 +654,28 @@ //if the process was started directly, use waitpid(), as it's a child... while (waitpid(-1, nullptr, 0) != pid) {} } else { + //...else poll its status using kill() #ifdef Q_OS_LINUX // Declare the process that will be debugging the crashed KDE app (#245529) #ifndef PR_SET_PTRACER # define PR_SET_PTRACER 0x59616d61 #endif prctl(PR_SET_PTRACER, pid, 0, 0, 0); -#endif - //...else poll its status using kill() + int sockfd = openDrKonqiSocket(pid); + while (kill(pid, 0) >= 0) { + if (sockfd > 0) { + pollDrKonqiSocket(pid, sockfd); + } else { + sleep(1); + } + } + + closeDrKonqiSocket(pid, sockfd); +#else while (kill(pid, 0) >= 0) { sleep(1); } +#endif } if (!s_coreConfig->isProcess()) { // Only exit if we don't forward to core dumps @@ -828,4 +845,92 @@ return s; } +#ifdef Q_OS_LINUX + +static int openDrKonqiSocket(int pid) +{ + struct sockaddr_un drkonqi_server; + + int sockfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + perror("Warning: socket() for communication with DrKonqi failed"); + return -1; + } + + drkonqi_server.sun_family = AF_UNIX; + QString socketpath = QStringLiteral("%1/kcrash_%2").arg(QDir::tempPath()).arg(pid); + + if (socketpath.size() >= static_cast(sizeof(drkonqi_server.sun_path))) { + fprintf(stderr, "Warning: socket path is too long\n"); + close(sockfd); + return -1; + } + strcpy(drkonqi_server.sun_path, socketpath.toLocal8Bit().constData()); + + if (bind(sockfd, (struct sockaddr *)&drkonqi_server, sizeof(drkonqi_server)) < 0) { + perror("Warning: bind() for communication with DrKonqi failed"); + close(sockfd); + unlink(drkonqi_server.sun_path); + return -1; + } + + listen(sockfd, 1); + + return sockfd; +} + +static int pollDrKonqiSocket(int pid, int sockfd) +{ + static struct sockaddr_un drkonqi_client; + static socklen_t cllength = sizeof(drkonqi_client); + static struct timeval timeout; + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + fd_set set; + FD_ZERO(&set); + FD_SET(sockfd, &set); + + if (select(sockfd + 1, &set, NULL, NULL, &timeout) <= 0) + return -1; + + int clsockfd = accept(sockfd, (struct sockaddr *)&drkonqi_client, &cllength); + if (clsockfd > 0) { + static struct ucred ucred; + static socklen_t credlen = sizeof(struct ucred); + if (getsockopt(clsockfd, SOL_SOCKET, SO_PEERCRED, &ucred, &credlen) == -1) + return -1; + + if (ucred.pid != pid) { + perror("Warning: peer pid does not match DrKonqi pid"); + closeDrKonqiSocket(pid, sockfd); + return -1; + } + + char msg[22]; + if (read_socket(clsockfd, msg, 22) == 0) { + int dpid = atoi(msg); + prctl(PR_SET_PTRACER, dpid, 0, 0, 0); + if (write_socket(clsockfd, msg, 22) == 0) { + fprintf(stderr, "KCrash: ptrace access transferred to %s\n", msg); + } + } + close(clsockfd); + + return 0; + } else { + return -1; + } +} + +static void closeDrKonqiSocket(int pid, int sockfd) +{ + close(sockfd); + QString socketpath = QStringLiteral("%1/kcrash_%2").arg(QDir::tempPath()).arg(pid); + unlink(socketpath.toLocal8Bit().data()); +} + +#endif + #endif // Q_OS_UNIX