summaryrefslogtreecommitdiffstats
path: root/languages/cpp/debugger/stty.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'languages/cpp/debugger/stty.cpp')
-rw-r--r--languages/cpp/debugger/stty.cpp386
1 files changed, 386 insertions, 0 deletions
diff --git a/languages/cpp/debugger/stty.cpp b/languages/cpp/debugger/stty.cpp
new file mode 100644
index 00000000..f0bc2627
--- /dev/null
+++ b/languages/cpp/debugger/stty.cpp
@@ -0,0 +1,386 @@
+/***************************************************************************
+ begin : Mon Sep 13 1999
+ copyright : (C) 1999 by John Birch
+ email : jbb@kdevelop.org
+
+ This code was originally written by Judin Maxim, from the
+ KDEStudio project.
+
+ It was then updated with later code from konsole (KDE).
+
+ It has also been enhanced with an idea from the code in kdbg
+ written by Johannes Sixt<Johannes.Sixt@telecom.at>
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef __osf__
+#define _XOPEN_SOURCE_EXTENDED
+#define O_NDELAY O_NONBLOCK
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>
+#define _NEW_TTY_CTRL
+#endif
+
+#include <assert.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if defined (_HPUX_SOURCE)
+#define _TERMIOS_INCLUDED
+#include <bsdtty.h>
+#endif
+
+#include <qintdict.h>
+#include <qsocketnotifier.h>
+#include <qstring.h>
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+
+#include "stty.h"
+
+#define PTY_FILENO 3
+#define BASE_CHOWN "konsole_grantpty"
+
+namespace GDBDebugger
+{
+
+static int chownpty(int fd, int grant)
+// param fd: the fd of a master pty.
+// param grant: 1 to grant, 0 to revoke
+// returns 1 on success 0 on fail
+{
+ void(*tmp)(int) = signal(SIGCHLD,SIG_DFL);
+ pid_t pid = fork();
+ if (pid < 0) {
+ signal(SIGCHLD,tmp);
+ return 0;
+ }
+ if (pid == 0) {
+ /* We pass the master pseudo terminal as file descriptor PTY_FILENO. */
+ if (fd != PTY_FILENO && dup2(fd, PTY_FILENO) < 0)
+ ::exit(1);
+
+ QString path = locate("exe", BASE_CHOWN);
+ execle(QFile::encodeName(path), BASE_CHOWN, grant?"--grant":"--revoke", (void *)0, NULL);
+ ::exit(1); // should not be reached
+ }
+ if (pid > 0) {
+ int w;
+ // retry:
+ int rc = waitpid (pid, &w, 0);
+ if (rc != pid)
+ ::exit(1);
+
+ // { // signal from other child, behave like catchChild.
+ // // guess this gives quite some control chaos...
+ // Shell* sh = shells.find(rc);
+ // if (sh) { shells.remove(rc); sh->doneShell(w); }
+ // goto retry;
+ // }
+ signal(SIGCHLD,tmp);
+ return (rc != -1 && WIFEXITED(w) && WEXITSTATUS(w) == 0);
+ }
+ signal(SIGCHLD,tmp);
+ return 0; //dummy.
+}
+
+// **************************************************************************
+
+STTY::STTY(bool ext, const QString &termAppName)
+ : QObject(),
+ out(0),
+ ttySlave(""),
+ pid_(0),
+ external_(ext)
+{
+ if (ext) {
+ findExternalTTY(termAppName);
+ } else {
+ fout = findTTY();
+ if (fout >= 0) {
+ ttySlave = QString(tty_slave);
+ out = new QSocketNotifier(fout, QSocketNotifier::Read, this);
+ connect( out, SIGNAL(activated(int)), this, SLOT(OutReceived(int)) );
+ }
+ }
+}
+
+// **************************************************************************
+
+STTY::~STTY()
+{
+ if (pid_)
+ ::kill(pid_, SIGTERM);
+
+ if (out) {
+ ::close(fout);
+ delete out;
+ }
+}
+
+// **************************************************************************
+
+int STTY::findTTY()
+{
+ int ptyfd = -1;
+ bool needGrantPty = TRUE;
+
+ // Find a master pty that we can open ////////////////////////////////
+
+#ifdef __sgi__
+ ptyfd = open("/dev/ptmx",O_RDWR);
+ if (ptyfd < 0) {
+ perror("Can't open a pseudo teletype");
+ return(-1);
+ }
+ strncpy(tty_slave, ptsname(ptyfd), 50);
+ grantpt(ptyfd);
+ unlockpt(ptyfd);
+ needGrantPty = FALSE;
+#endif
+
+ // first we try UNIX PTY's
+#ifdef TIOCGPTN
+ strcpy(pty_master,"/dev/ptmx");
+ strcpy(tty_slave,"/dev/pts/");
+ ptyfd = open(pty_master,O_RDWR);
+ if (ptyfd >= 0) { // got the master pty
+ int ptyno;
+ if (ioctl(ptyfd, TIOCGPTN, &ptyno) == 0) {
+ struct stat sbuf;
+ sprintf(tty_slave,"/dev/pts/%d",ptyno);
+ if (stat(tty_slave,&sbuf) == 0 && S_ISCHR(sbuf.st_mode))
+ needGrantPty = FALSE;
+ else {
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ } else {
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ }
+#endif
+
+#if defined(_SCO_DS) || defined(__USLC__) /* SCO OSr5 and UnixWare */
+ if (ptyfd < 0) {
+ for (int idx = 0; idx < 256; idx++)
+ { sprintf(pty_master, "/dev/ptyp%d", idx);
+ sprintf(tty_slave, "/dev/ttyp%d", idx);
+ if (access(tty_slave, F_OK) < 0) { idx = 256; break; }
+ if ((ptyfd = open (pty_master, O_RDWR)) >= 0)
+ { if (access (tty_slave, R_OK|W_OK) == 0) break;
+ close(ptyfd); ptyfd = -1;
+ }
+ }
+ }
+#endif
+ if (ptyfd < 0) { /// \FIXME Linux, Trouble on other systems?
+ for (const char* s3 = "pqrstuvwxyzabcde"; *s3 != 0; s3++) {
+ for (const char* s4 = "0123456789abcdef"; *s4 != 0; s4++) {
+ sprintf(pty_master,"/dev/pty%c%c",*s3,*s4);
+ sprintf(tty_slave,"/dev/tty%c%c",*s3,*s4);
+ if ((ptyfd = open(pty_master, O_RDWR)) >= 0) {
+ if (geteuid() == 0 || access(tty_slave, R_OK|W_OK) == 0)
+ break;
+
+ close(ptyfd);
+ ptyfd = -1;
+ }
+ }
+
+ if (ptyfd >= 0)
+ break;
+ }
+ }
+
+ if (ptyfd >= 0) {
+ if (needGrantPty && !chownpty(ptyfd, TRUE)) {
+ fprintf(stderr,"kdevelop: chownpty failed for device %s::%s.\n",pty_master,tty_slave);
+ fprintf(stderr," : This means the session can be eavesdroped.\n");
+ fprintf(stderr," : Make sure konsole_grantpty is installed and setuid root.\n");
+ }
+
+ ::fcntl(ptyfd, F_SETFL, O_NDELAY);
+#ifdef TIOCSPTLCK
+ int flag = 0;
+ ioctl(ptyfd, TIOCSPTLCK, &flag); // unlock pty
+#endif
+ }
+
+ return ptyfd;
+}
+
+// **************************************************************************
+
+void STTY::OutReceived(int f)
+{
+ char buf[1024];
+ int n;
+
+ // read until socket is empty. We shouldn't be receiving a continuous
+ // stream of data, so the loop is unlikely to cause problems.
+ while ((n = ::read(f, buf, sizeof(buf)-1)) > 0) {
+ *(buf+n) = 0; // a standard string
+ emit OutOutput(buf);
+ }
+ // Note: for some reason, n can be 0 here.
+ // I can understand that non-blocking read returns 0,
+ // but I don't understand how OutRecieved can be even
+ // called when there's no input.
+ if (n == 0 /* eof */
+ || (n == -1 && errno != EAGAIN))
+ {
+ // Found eof or error. Disable socket notifier, otherwise Qt
+ // will repeatedly call this method, eating CPU
+ // cycles.
+ out->setEnabled(false);
+ }
+
+}
+
+void STTY::readRemaining()
+{
+ if (!external_)
+ OutReceived(fout);
+}
+
+// **************************************************************************
+
+#define FIFO_FILE "/tmp/debug_tty.XXXXXX"
+
+bool STTY::findExternalTTY(const QString &termApp)
+{
+ QString appName(termApp.isEmpty() ? QString("xterm") : termApp);
+
+ if ( KStandardDirs::findExe( termApp ).isEmpty() )
+ {
+ return false;
+ }
+
+ char fifo[] = FIFO_FILE;
+ int fifo_fd;
+ if ((fifo_fd = mkstemp(fifo)) == -1)
+ return false;
+
+ ::close(fifo_fd);
+ ::unlink(fifo);
+
+ // create a fifo that will pass in the tty name
+#ifdef HAVE_MKFIFO
+ if (::mkfifo(fifo, S_IRUSR|S_IWUSR) < 0)
+#else
+ if (::mknod(fifo, S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0)
+#endif
+ return false;
+
+ int pid = ::fork();
+ if (pid < 0) { // No process
+ ::unlink(fifo);
+ return false;
+ }
+
+ if (pid == 0) { // child process
+ /*
+ * Spawn a console that in turn runs a shell script that passes us
+ * back the terminal name and then only sits and waits.
+ */
+
+ const char* prog = appName.latin1();
+ QString script = QString("tty>") + QString(fifo) +
+ QString(";" // fifo name
+ "trap \"\" INT QUIT TSTP;" // ignore various signals
+ "exec<&-;exec>&-;" // close stdin and stdout
+ "while :;do sleep 3600;done");
+ const char* scriptStr = script.latin1();
+ const char* end = 0;
+
+ if ( termApp == "konsole" )
+ {
+ ::execlp( prog, prog,
+ "-caption", i18n("kdevelop: Debug application console").local8Bit().data(),
+ "-e", "sh",
+ "-c", scriptStr,
+ end);
+ }
+ else
+ {
+ ::execlp( prog, prog,
+ "-e", "sh",
+ "-c", scriptStr,
+ end);
+ }
+
+ // Should not get here, as above should always work
+ ::exit(1);
+ }
+
+ // parent process
+ if (pid <= 0)
+ ::exit(1);
+
+ // Open the communication between us (the parent) and the
+ // child (the process running on a tty console)
+ fifo_fd = ::open(fifo, O_RDONLY);
+ if (fifo_fd < 0)
+ return false;
+
+ // Get the ttyname from the fifo buffer that the child process
+ // has sent.
+ char ttyname[50];
+ int n = ::read(fifo_fd, ttyname, sizeof(ttyname)-sizeof(char));
+
+ ::close(fifo_fd);
+ ::unlink(fifo);
+
+ // No name??
+ if (n <= 0)
+ return false;
+
+ // remove whitespace
+ ttyname[n] = 0;
+ if (char* newline = strchr(ttyname, '\n'))
+ *newline = 0; // clobber the new line
+
+ ttySlave = ttyname;
+ pid_ = pid;
+
+ return true;
+}
+
+}
+
+// **************************************************************************
+#include "stty.moc"