summaryrefslogtreecommitdiffstats
path: root/socket.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 02:20:21 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 02:20:21 +0000
commit4f64827f3a395072f300b86ef17f730805b47048 (patch)
treec16297f7d68903dc80c86e3003b8e3ab5f030f7d /socket.c
parentInitial commit. (diff)
downloadscreen-upstream.tar.xz
screen-upstream.zip
Adding upstream version 4.6.2.upstream/4.6.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'socket.c')
-rw-r--r--socket.c1930
1 files changed, 1930 insertions, 0 deletions
diff --git a/socket.c b/socket.c
new file mode 100644
index 0000000..88c3dd8
--- /dev/null
+++ b/socket.c
@@ -0,0 +1,1930 @@
+/* Copyright (c) 2008, 2009
+ * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
+ * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
+ * Micah Cowan (micah@cowan.name)
+ * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
+ * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
+ * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
+ * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
+ * Copyright (c) 1987 Oliver Laumann
+ *
+ * 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 3, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING); if not, see
+ * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ *
+ ****************************************************************
+ */
+
+#include "config.h"
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+# include <sys/socket.h>
+# ifdef _OpenBSD_
+# include <sys/uio.h>
+# endif
+# include <sys/un.h>
+
+#ifndef SIGINT
+# include <signal.h>
+#endif
+
+#include "screen.h"
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#else
+# include <sys/dir.h>
+# define dirent direct
+#endif
+
+#ifndef CMSG_LEN
+#define CMSG_LEN(length) ((_CMSG_DATA_ALIGN(sizeof(struct cmsghdr))) + (length))
+#endif
+#ifndef CMSG_SPACE
+#define CMSG_SPACE(length) ((_CMSG_DATA_ALIGN(sizeof(struct cmsghdr))) + (_CMSG_DATA_ALIGN(length)))
+#endif
+
+#include "extern.h"
+#include "list_generic.h"
+
+static int CheckPid __P((int));
+static void ExecCreate __P((struct msg *));
+static void DoCommandMsg __P((struct msg *));
+#if defined(_SEQUENT_)
+# define connect sconnect /* _SEQUENT_ has braindamaged connect */
+static int sconnect __P((int, struct sockaddr *, int));
+#endif
+static void FinishAttach __P((struct msg *));
+static void FinishDetach __P((struct msg *));
+static void AskPassword __P((struct msg *));
+
+
+extern char *RcFileName, *extra_incap, *extra_outcap;
+extern int ServerSocket, real_uid, real_gid, eff_uid, eff_gid;
+extern int dflag, iflag, rflag, lsflag, quietflag, wipeflag, xflag;
+extern int queryflag;
+extern char *attach_tty, *LoginName, HostName[];
+extern struct display *display, *displays;
+extern struct win *fore, **wtab, *console_window, *windows;
+extern struct layer *flayer;
+extern struct layout *layout_attach, *layout_last, layout_last_marker;
+extern struct NewWindow nwin_undef;
+#ifdef MULTIUSER
+extern char *multi;
+#endif
+extern int maxwin;
+
+extern char *getenv();
+
+extern char SockPath[];
+extern struct event serv_read;
+extern char *rc_name;
+extern struct comm comms[];
+
+#ifdef MULTIUSER
+# define SOCKMODE (S_IWRITE | S_IREAD | (displays ? S_IEXEC : 0) | (multi ? 1 : 0))
+#else
+# define SOCKMODE (S_IWRITE | S_IREAD | (displays ? S_IEXEC : 0))
+#endif
+
+
+/*
+ * Socket directory manager
+ *
+ * fdp: pointer to store the first good socket.
+ * nfoundp: pointer to store the number of sockets found matching.
+ * notherp: pointer to store the number of sockets not matching.
+ * match: string to match socket name.
+ *
+ * The socket directory must be in SockPath!
+ * The global variables LoginName, multi, rflag, xflag, dflag,
+ * quietflag, SockPath are used.
+ *
+ * The first good socket is stored in fdp and its name is
+ * appended to SockPath.
+ * If none exists or fdp is NULL SockPath is not changed.
+ *
+ * Returns: number of good sockets.
+ *
+ */
+
+int
+FindSocket(fdp, nfoundp, notherp, match, is_sock)
+int *fdp;
+int *nfoundp, *notherp;
+char *match;
+bool *is_sock;
+{
+ DIR *dirp;
+ struct dirent *dp;
+ struct stat st;
+ int mode;
+ int sdirlen;
+ int matchlen = 0;
+ char *name, *n;
+ int firsts = -1, sockfd;
+ char *firstn = NULL;
+ int nfound = 0, ngood = 0, ndead = 0, nwipe = 0, npriv = 0;
+ int nperfect = 0;
+ struct sent
+ {
+ struct sent *next;
+ int mode;
+ char *name;
+ } *slist, **slisttail, *sent, *nsent;
+
+ if (match)
+ {
+ matchlen = strlen(match);
+#ifdef NAME_MAX
+ if (matchlen > NAME_MAX)
+ matchlen = NAME_MAX;
+#endif
+ }
+
+ /*
+ * SockPath contains the socket directory.
+ * At the end of FindSocket the socket name will be appended to it.
+ * Thus FindSocket() can only be called once!
+ */
+ sdirlen = strlen(SockPath);
+
+#ifdef USE_SETEUID
+ xseteuid(real_uid);
+ xsetegid(real_gid);
+#endif
+
+ if ((dirp = opendir(SockPath)) == 0)
+ Panic(errno, "Cannot opendir %s", SockPath);
+
+ slist = 0;
+ slisttail = &slist;
+ while ((dp = readdir(dirp)))
+ {
+ int cmatch = 0;
+ name = dp->d_name;
+ debug1("- %s\n", name);
+ if (*name == 0 || *name == '.' || strlen(name) > 2*MAXSTR)
+ continue;
+ if (matchlen)
+ {
+ n = name;
+ /* if we don't want to match digits. Skip them */
+ if ((*match <= '0' || *match > '9') && (*n > '0' && *n <= '9'))
+ {
+ while (*n >= '0' && *n <= '9')
+ n++;
+ if (*n == '.')
+ n++;
+ }
+ /* the tty prefix is optional */
+ if (strncmp(match, "tty", 3) && strncmp(n, "tty", 3) == 0)
+ n += 3;
+ if (strncmp(match, n, matchlen))
+ {
+ if (n == name && *match > '0' && *match <= '9')
+ {
+ while (*n >= '0' && *n <= '9')
+ n++;
+ if (*n == '.')
+ n++;
+ if (strncmp(match, n, matchlen))
+ continue;
+ }
+ else
+ continue;
+ }
+ else
+ cmatch = (*(n + matchlen) == 0);
+ debug1(" -> matched %s\n", match);
+ }
+ sprintf(SockPath + sdirlen, "/%s", name);
+
+ debug1("stat %s\n", SockPath);
+ errno = 0;
+ debug2("uid = %d, gid = %d\n", getuid(), getgid());
+ debug2("euid = %d, egid = %d\n", geteuid(), getegid());
+ if (stat(SockPath, &st))
+ {
+ debug1("errno = %d\n", errno);
+ continue;
+ }
+
+ *is_sock = S_ISSOCK(st.st_mode);
+ if (!(*is_sock) && !S_ISFIFO(st.st_mode))
+ continue;
+
+ debug2("st.st_uid = %d, real_uid = %d\n", st.st_uid, real_uid);
+#ifdef SOCKDIR /* if SOCKDIR is not defined, the socket is in $HOME.
+ in that case it does not make sense to compare uids. */
+ if ((int)st.st_uid != real_uid)
+ continue;
+#endif
+ mode = (int)st.st_mode & 0777;
+ debug1(" has mode 0%03o\n", mode);
+#ifdef MULTIUSER
+ if (multi && ((mode & 0677) != 0601))
+ {
+ debug(" is not a MULTI-USER session");
+ if (strcmp(multi, LoginName))
+ {
+ debug(" and we are in a foreign directory.\n");
+ mode = -4;
+ }
+ else
+ {
+ debug(", but it is our own session.\n");
+ }
+ }
+#endif
+ debug(" store it.\n");
+ if ((sent = (struct sent *)malloc(sizeof(struct sent))) == 0)
+ continue;
+ sent->next = 0;
+ sent->name = SaveStr(name);
+ sent->mode = mode;
+ *slisttail = sent;
+ slisttail = &sent->next;
+ nfound++;
+ sockfd = MakeClientSocket(0, *is_sock);
+#ifdef USE_SETEUID
+ /* MakeClientSocket sets ids back to eff */
+ xseteuid(real_uid);
+ xsetegid(real_gid);
+#endif
+ if (sockfd == -1)
+ {
+ debug2(" MakeClientSocket failed, unreachable? %d %d\n",
+ matchlen, wipeflag);
+ sent->mode = -3;
+#ifndef SOCKDIR_IS_LOCAL_TO_HOST
+ /* Unreachable - it is dead if we detect that it's local
+ * or we specified a match
+ */
+ n = name + strlen(name) - 1;
+ while (n != name && *n != '.')
+ n--;
+ if (matchlen == 0 && !(*n == '.' && n[1] && strncmp(HostName, n + 1, strlen(n + 1)) == 0))
+ {
+ npriv++; /* a good socket that was not for us */
+ continue;
+ }
+#endif
+ ndead++;
+ sent->mode = -1;
+ if (wipeflag)
+ {
+ if (unlink(SockPath) == 0)
+ {
+ sent->mode = -2;
+ nwipe++;
+ }
+ }
+ continue;
+ }
+
+ mode &= 0776;
+ /* Shall we connect ? */
+ debug2(" connecting: mode=%03o, rflag=%d, ", mode, rflag);
+ debug2("xflag=%d, dflag=%d ?\n", xflag, dflag);
+
+ /*
+ * mode 600: socket is detached.
+ * mode 700: socket is attached.
+ * xflag implies rflag here.
+ *
+ * fail, when socket mode mode is not 600 or 700
+ * fail, when we want to detach w/o reattach, but it already is detached.
+ * fail, when we only want to attach, but mode 700 and not xflag.
+ * fail, if none of dflag, rflag, xflag is set.
+ */
+ if ((mode != 0700 && mode != 0600) ||
+ (dflag && !rflag && !xflag && mode == 0600) ||
+ (!dflag && rflag && mode == 0700 && !xflag) ||
+ (!dflag && !rflag && !xflag))
+ {
+ close(sockfd);
+ debug(" no!\n");
+ npriv++; /* a good socket that was not for us */
+ continue;
+ }
+ ngood++;
+ if (cmatch)
+ nperfect++;
+ if (fdp && (firsts == -1 || (cmatch && nperfect == 1)))
+ {
+ if (firsts != -1)
+ close(firsts);
+ firsts = sockfd;
+ firstn = sent->name;
+ debug(" taken.\n");
+ }
+ else
+ {
+ debug(" discarded.\n");
+ close(sockfd);
+ }
+ }
+ (void)closedir(dirp);
+ if (!lsflag && nperfect == 1)
+ ngood = nperfect;
+ if (nfound && (lsflag || ngood != 1) && !quietflag)
+ {
+ switch(ngood)
+ {
+ case 0:
+ Msg(0, nfound > 1 ? "There are screens on:" : "There is a screen on:");
+ break;
+ case 1:
+ Msg(0, nfound > 1 ? "There are several screens on:" : "There is a suitable screen on:");
+ break;
+ default:
+ Msg(0, "There are several suitable screens on:");
+ break;
+ }
+ for (sent = slist; sent; sent = sent->next)
+ {
+ switch (sent->mode)
+ {
+ case 0700:
+ printf("\t%s\t(Attached)\n", sent->name);
+ break;
+ case 0600:
+ printf("\t%s\t(Detached)\n", sent->name);
+ break;
+#ifdef MULTIUSER
+ case 0701:
+ printf("\t%s\t(Multi, attached)\n", sent->name);
+ break;
+ case 0601:
+ printf("\t%s\t(Multi, detached)\n", sent->name);
+ break;
+#endif
+ case -1:
+ /* No trigraphs here! */
+ printf("\t%s\t(Dead ?%c?)\n", sent->name, '?');
+ break;
+ case -2:
+ printf("\t%s\t(Removed)\n", sent->name);
+ break;
+ case -3:
+ printf("\t%s\t(Remote or dead)\n", sent->name);
+ break;
+ case -4:
+ printf("\t%s\t(Private)\n", sent->name);
+ break;
+ }
+ }
+ }
+ if (ndead && !quietflag)
+ {
+ char *m = "Remove dead screens with 'screen -wipe'.";
+ if (wipeflag)
+ Msg(0, "%d socket%s wiped out.", nwipe, nwipe > 1 ? "s" : "");
+ else
+ Msg(0, m, ndead > 1 ? "s" : "", ndead > 1 ? "" : "es"); /* other args for nethack */
+ }
+ if (firsts != -1)
+ {
+ sprintf(SockPath + sdirlen, "/%s", firstn);
+ *fdp = firsts;
+ }
+ else
+ SockPath[sdirlen] = 0;
+ for (sent = slist; sent; sent = nsent)
+ {
+ nsent = sent->next;
+ free(sent->name);
+ free((char *)sent);
+ }
+#ifdef USE_SETEUID
+ xseteuid(eff_uid);
+ xsetegid(eff_gid);
+#endif
+ if (notherp)
+ *notherp = npriv;
+ if (nfoundp)
+ *nfoundp = nfound - nwipe;
+ return ngood;
+}
+
+/* FIFO (legacy mode) */
+static int
+MakeServerFifo()
+{
+ register int s;
+ struct stat st;
+
+#ifdef USE_SETEUID
+ xseteuid(real_uid);
+ xsetegid(real_gid);
+#endif
+ s = open(SockPath, O_WRONLY | O_NONBLOCK);
+ if (s >= 0)
+ {
+ debug("huii, my fifo already exists??\n");
+ if (quietflag)
+ {
+ Kill(D_userpid, SIG_BYE);
+ eexit(11);
+ }
+
+ Msg(0, "There is already a screen running on %s.", Filename(SockPath));
+
+ if (stat(SockPath, &st) < 0)
+ Panic(errno, "stat");
+
+#ifdef SOCKDIR /* if SOCKDIR is not defined, the socket is in $HOME. \
+ in that case it does not make sense to compare uids. */
+ if ((int)st.st_uid != real_uid)
+ Panic(0, "Unfortunately you are not its owner.");
+#endif
+ if ((st.st_mode & 0700) == 0600)
+ Panic(0, "To resume it, use \"screen -r\"");
+ else
+ Panic(0, "It is not detached.");
+ /* NOTREACHED */
+ }
+# ifdef USE_SETEUID
+ (void) unlink(SockPath);
+ if (mkfifo(SockPath, SOCKMODE) < 0)
+ Panic(0, "mkfifo %s failed", SockPath);
+# ifdef BROKEN_PIPE
+ s = open(SockPath, O_RDWR | O_NONBLOCK, 0);
+# else
+ s = open(SockPath, O_RDONLY | O_NONBLOCK, 0);
+# endif
+ if (s < 0)
+ Panic(errno, "open fifo %s", SockPath);
+
+ xseteuid(eff_uid);
+ xsetegid(eff_gid);
+
+ return s;
+
+# else /* !USE_SETEUID */
+ if (UserContext() > 0)
+ {
+ (void) unlink(SockPath);
+ UserReturn(mkfifo(SockPath, SOCKMODE));
+ }
+ if (UserStatus())
+ Panic(0, "mkfifo %s failed", SockPath);
+# ifdef BROKEN_PIPE
+ s = secopen(SockPath, O_RDWR | O_NONBLOCK, 0);
+# else
+ s = secopen(SockPath, O_RDONLY | O_NONBLOCK, 0);
+# endif
+ if (s < 0)
+ Panic(errno, "open fifo %s", SockPath);
+
+ return s;
+# endif /* !USE_SETEUID */
+}
+
+static int
+MakeClientFifo(err)
+int err;
+{
+ register int s = 0;
+
+ s = secopen(SockPath, O_WRONLY | O_NONBLOCK, 0);
+ if (s >= 0)
+ {
+ (void) fcntl(s, F_SETFL, 0);
+ return s;
+ }
+
+ if (err)
+ Msg(errno, "%s", SockPath);
+ debug2("MakeClientSocket() open %s failed (%d)\n", SockPath, errno);
+
+ return -1;
+}
+
+/* Unix Domain Sockets */
+static int
+MakeServerUnixSocket()
+{
+ register int s;
+ struct sockaddr_un a;
+ struct stat st;
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ Panic(errno, "socket");
+
+ a.sun_family = AF_UNIX;
+ strncpy(a.sun_path, SockPath, sizeof(a.sun_path));
+ a.sun_path[sizeof(a.sun_path) - 1] = 0;
+
+# ifdef USE_SETEUID
+ xseteuid(real_uid);
+ xsetegid(real_gid);
+# endif
+ if (connect(s, (struct sockaddr *) &a, strlen(SockPath) + 2) != -1)
+ {
+ debug("oooooh! socket already is alive!\n");
+ if (quietflag)
+ {
+ Kill(D_userpid, SIG_BYE);
+ /*
+ * oh, well. nobody receives that return code. papa
+ * dies by signal.
+ */
+ eexit(11);
+ }
+ Msg(0, "There is already a screen running on %s.", Filename(SockPath));
+
+ if (stat(SockPath, &st) < 0)
+ Panic(errno, "stat");
+
+#ifdef SOCKDIR /* if SOCKDIR is not defined, the socket is in $HOME. \
+ in that case it does not make sense to compare uids. */
+ if (st.st_uid != real_uid)
+ Panic(0, "Unfortunately you are not its owner.");
+#endif
+ if ((st.st_mode & 0700) == 0600)
+ Panic(0, "To resume it, use \"screen -r\"");
+ else
+ Panic(0, "It is not detached.");
+ /* NOTREACHED */
+ }
+
+#if defined(m88k) || defined(sysV68)
+ close(s); /* we get bind: Invalid argument if this is not done */
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ Panic(errno, "reopen socket");
+#endif
+ (void) unlink(SockPath);
+ if (bind(s, (struct sockaddr *) & a, strlen(SockPath) + 2) == -1)
+ Panic(errno, "bind (%s)", SockPath);
+#ifdef SOCK_NOT_IN_FS
+ {
+ int f = secopen(SockPath, O_RDWR | O_CREAT, SOCKMODE);
+ if (f < 0)
+ Panic(errno, "shadow socket open");
+ close(f);
+ }
+#else
+ chmod(SockPath, SOCKMODE);
+#ifndef USE_SETEUID
+ chown(SockPath, real_uid, real_gid);
+#endif
+#endif /* SOCK_NOT_IN_FS */
+ if (listen(s, 5) == -1)
+ Panic(errno, "listen");
+#ifdef F_SETOWN
+ fcntl(s, F_SETOWN, getpid());
+ debug1("Serversocket owned by %d\n", fcntl(s, F_GETOWN, 0));
+#endif /* F_SETOWN */
+#ifdef USE_SETEUID
+ xseteuid(eff_uid);
+ xsetegid(eff_gid);
+#endif
+ return s;
+}
+
+static int
+MakeClientUnixSocket(err)
+int err;
+{
+ register int s;
+ struct sockaddr_un a;
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ Panic(errno, "socket");
+
+ a.sun_family = AF_UNIX;
+ strncpy(a.sun_path, SockPath, sizeof(a.sun_path));
+ a.sun_path[sizeof(a.sun_path) - 1] = 0;
+#ifdef USE_SETEUID
+ xseteuid(real_uid);
+ xsetegid(real_gid);
+#else
+ if (access(SockPath, W_OK))
+ {
+ if (err)
+ Msg(errno, "%s", SockPath);
+ debug2("MakeClientSocket: access(%s): %d.\n", SockPath, errno);
+ close(s);
+ return -1;
+ }
+#endif
+ if (connect(s, (struct sockaddr *)&a, strlen(SockPath) + 2) == -1)
+ {
+ if (err)
+ Msg(errno, "%s: connect", SockPath);
+ debug("MakeClientSocket: connect failed.\n");
+ close(s);
+ s = -1;
+ }
+#ifdef USE_SETEUID
+ xseteuid(eff_uid);
+ xsetegid(eff_gid);
+#endif
+ return s;
+}
+
+int
+MakeServerSocket(socket)
+bool socket;
+{
+ if (socket)
+ return MakeServerUnixSocket();
+
+ return MakeServerFifo();
+}
+
+int
+MakeClientSocket(err, socket)
+int err;
+bool socket;
+{
+ if (socket)
+ return MakeClientUnixSocket(err);
+
+ return MakeClientFifo(err);
+}
+
+/*
+**
+** Message send and receive routines
+**
+*/
+
+void
+SendCreateMsg(sty, nwin)
+char *sty;
+struct NewWindow *nwin;
+{
+ int s;
+ struct msg m;
+ register char *p;
+ register int len, n;
+ char **av;
+ bool is_socket;
+
+#ifdef NAME_MAX
+ if (strlen(sty) > NAME_MAX)
+ sty[NAME_MAX] = 0;
+#endif
+ if (strlen(sty) > 2 * MAXSTR - 1)
+ sty[2 * MAXSTR - 1] = 0;
+ sprintf(SockPath + strlen(SockPath), "/%s", sty);
+ is_socket = IsSocket(SockPath);
+ if ((s = MakeClientSocket(1, is_socket)) == -1)
+ exit(1);
+ debug1("SendCreateMsg() to '%s'\n", SockPath);
+ bzero((char *)&m, sizeof(m));
+ m.type = MSG_CREATE;
+ strncpy(m.m_tty, attach_tty, sizeof(m.m_tty) - 1);
+ m.m_tty[sizeof(m.m_tty) - 1] = 0;
+ p = m.m.create.line;
+ n = 0;
+ if (nwin->args != nwin_undef.args)
+ for (av = nwin->args; *av && n < MAXARGS - 1; ++av, ++n)
+ {
+ len = strlen(*av) + 1;
+ if (p + len >= m.m.create.line + sizeof(m.m.create.line) - 1)
+ break;
+ strcpy(p, *av);
+ p += len;
+ }
+ if (nwin->aka != nwin_undef.aka && p + strlen(nwin->aka) + 1 < m.m.create.line + sizeof(m.m.create.line))
+ strcpy(p, nwin->aka);
+ else
+ *p = '\0';
+ m.m.create.nargs = n;
+ m.m.create.aflag = nwin->aflag;
+ m.m.create.flowflag = nwin->flowflag;
+ m.m.create.lflag = nwin->lflag;
+ m.m.create.hheight = nwin->histheight;
+ if (getcwd(m.m.create.dir, sizeof(m.m.create.dir)) == 0)
+ {
+ Msg(errno, "getcwd");
+ return;
+ }
+ if (nwin->term != nwin_undef.term)
+ strncpy(m.m.create.screenterm, nwin->term, MAXTERMLEN);
+ m.m.create.screenterm[MAXTERMLEN] = '\0';
+ m.protocol_revision = MSG_REVISION;
+ debug1("SendCreateMsg writing '%s'\n", m.m.create.line);
+ if (write(s, (char *) &m, sizeof m) != sizeof m)
+ Msg(errno, "write");
+ close(s);
+}
+
+int
+SendErrorMsg(tty, buf)
+char *tty, *buf;
+{
+ int s;
+ struct msg m;
+ bool is_socket;
+
+ strncpy(m.m.message, buf, sizeof(m.m.message) - 1);
+ m.m.message[sizeof(m.m.message) - 1] = 0;
+ is_socket = IsSocket(SockPath);
+ s = MakeClientSocket(0, is_socket);
+ if (s < 0)
+ return -1;
+ m.type = MSG_ERROR;
+ strncpy(m.m_tty, tty, sizeof(m.m_tty) - 1);
+ m.m_tty[sizeof(m.m_tty) - 1] = 0;
+ m.protocol_revision = MSG_REVISION;
+ debug1("SendErrorMsg(): writing to '%s'\n", SockPath);
+ (void) write(s, (char *) &m, sizeof m);
+ close(s);
+ return 0;
+}
+
+static void
+ExecCreate(mp)
+struct msg *mp;
+{
+ struct NewWindow nwin;
+ char *args[MAXARGS];
+ register int n;
+ register char **pp = args, *p = mp->m.create.line;
+ char buf[20];
+
+ nwin = nwin_undef;
+ n = mp->m.create.nargs;
+ if (n > MAXARGS - 1)
+ n = MAXARGS - 1;
+ /* ugly hack alert... should be done by the frontend! */
+ if (n)
+ {
+ int l, num;
+
+ l = strlen(p);
+ if (IsNumColon(p, 10, buf, sizeof(buf)))
+ {
+ if (*buf)
+ nwin.aka = buf;
+ num = atoi(p);
+ if (num < 0 || num > maxwin - 1)
+ num = 0;
+ nwin.StartAt = num;
+ p += l + 1;
+ n--;
+ }
+ }
+ for (; n > 0; n--)
+ {
+ *pp++ = p;
+ p += strlen(p) + 1;
+ }
+ *pp = 0;
+ if (*p)
+ nwin.aka = p;
+ if (*args)
+ nwin.args = args;
+ nwin.aflag = mp->m.create.aflag;
+ nwin.flowflag = mp->m.create.flowflag;
+ if (*mp->m.create.dir)
+ nwin.dir = mp->m.create.dir;
+ nwin.lflag = mp->m.create.lflag;
+ nwin.histheight = mp->m.create.hheight;
+ if (*mp->m.create.screenterm)
+ nwin.term = mp->m.create.screenterm;
+ MakeWindow(&nwin);
+}
+
+static int
+CheckPid(pid)
+int pid;
+{
+ debug1("Checking pid %d\n", pid);
+ if (pid < 2)
+ return -1;
+ if (eff_uid == real_uid)
+ return kill(pid, 0);
+ if (UserContext() > 0)
+ UserReturn(kill(pid, 0));
+ return UserStatus();
+}
+
+#ifdef hpux
+/*
+ * From: "F. K. Bruner" <napalm@ugcs.caltech.edu>
+ * From: "Dan Egnor" <egnor@oracorp.com> Tue Aug 10 06:56:45 1993
+ * The problem is that under HPUX (and possibly other systems too) there are
+ * two equivalent device files for each pty/tty device:
+ * /dev/ttyxx == /dev/pty/ttyxx
+ * /dev/ptyxx == /dev/ptym/ptyxx
+ * I didn't look into the exact specifics, but I've run across this problem
+ * before: Even if you open /dev/ttyxx as fds 0 1 & 2 for a process, if that
+ * process calls the system to determine its tty, it'll get /dev/pty/ttyxx.
+ *
+ * Earlier versions seemed to work -- wonder what they did.
+ */
+static int
+ttycmp(s1, s2)
+char *s1, *s2;
+{
+ if (strlen(s1) > 5) s1 += strlen(s1) - 5;
+ if (strlen(s2) > 5) s2 += strlen(s2) - 5;
+ return strcmp(s1, s2);
+}
+# define TTYCMP(a, b) ttycmp(a, b)
+#else
+# define TTYCMP(a, b) strcmp(a, b)
+#endif
+
+static int
+CreateTempDisplay(m, recvfd, wi)
+struct msg *m;
+int recvfd;
+struct win *wi;
+{
+ int pid;
+ int attach;
+ char *user;
+ int i;
+ struct mode Mode;
+ struct display *olddisplays = displays;
+
+ switch (m->type)
+ {
+ case MSG_CONT:
+ case MSG_ATTACH:
+ pid = m->m.attach.apid;
+ user = m->m.attach.auser;
+ attach = 1;
+ break;
+#ifdef REMOTE_DETACH
+ case MSG_DETACH:
+# ifdef POW_DETACH
+ case MSG_POW_DETACH:
+# endif /* POW_DETACH */
+ pid = m->m.detach.dpid;
+ user = m->m.detach.duser;
+ attach = 0;
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ if (CheckPid(pid))
+ {
+ Msg(0, "Attach attempt with bad pid(%d)!", pid);
+ return -1;
+ }
+ if (recvfd != -1)
+ {
+ int ret;
+ char ttyname_in_ns[MAXPATHLEN];
+ char *myttyname;
+
+ i = recvfd;
+ recvfd = -1;
+ memset(&ttyname_in_ns, 0, sizeof(ttyname_in_ns));
+ errno = 0;
+ myttyname = GetPtsPathOrSymlink(i);
+ if (myttyname && errno == ENODEV)
+ {
+ ret = readlink(myttyname, ttyname_in_ns, sizeof(ttyname_in_ns));
+ if (ret < 0 || (size_t)ret >= sizeof(ttyname_in_ns))
+ {
+ Msg(errno, "Could not perform necessary sanity checks on pts device.");
+ close(i);
+ Kill(pid, SIG_BYE);
+ return -1;
+ }
+ if (strcmp(ttyname_in_ns, m->m_tty))
+ {
+ Msg(errno, "Attach: passed fd does not match tty: %s - %s!", ttyname_in_ns, m->m_tty[0] != '\0' ? m->m_tty : "(null)");
+ close(i);
+ Kill(pid, SIG_BYE);
+ return -1;
+ }
+ /* m->m_tty so far contains the actual name of the pts device in the
+ * its (e.g. /dev/pts/0). This name however is not valid in the
+ * current namespace. So after we verified that the symlink returned
+ * by GetPtsPathOrSymlink() refers to the same pts device in this
+ * namespace we need to update m->m_tty to use that symlink for all
+ * future operations.
+ */
+ strncpy(m->m_tty, myttyname, sizeof(m->m_tty) - 1);
+ m->m_tty[sizeof(m->m_tty) - 1] = 0;
+ }
+ else if (myttyname == 0 || strcmp(myttyname, m->m_tty))
+ {
+ Msg(errno, "Attach: passed fd does not match tty: %s - %s!", m->m_tty, myttyname ? myttyname : "NULL");
+ close(i);
+ Kill(pid, SIG_BYE);
+ return -1;
+ }
+ }
+ else if ((i = secopen(m->m_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
+ {
+ Msg(errno, "Attach: Could not open %s!", m->m_tty);
+ Kill(pid, SIG_BYE);
+ return -1;
+ }
+#ifdef MULTIUSER
+ if (attach)
+ Kill(pid, SIGCONT);
+#endif
+
+#if defined(ultrix) || defined(pyr) || defined(NeXT)
+ brktty(i); /* for some strange reason this must be done */
+#endif
+
+ if (attach)
+ {
+ if (display || wi)
+ {
+ write(i, "Attaching from inside of screen?\n", 33);
+ close(i);
+ Kill(pid, SIG_BYE);
+ Msg(0, "Attach msg ignored: coming from inside.");
+ return -1;
+ }
+
+#ifdef MULTIUSER
+ if (strcmp(user, LoginName))
+ if (*FindUserPtr(user) == 0)
+ {
+ write(i, "Access to session denied.\n", 26);
+ close(i);
+ Kill(pid, SIG_BYE);
+ Msg(0, "Attach: access denied for user %s.", user);
+ return -1;
+ }
+#endif
+
+ debug2("RecMsg: apid %d is o.k. and we just opened '%s'\n", pid, m->m_tty);
+#ifndef MULTI
+ if (displays)
+ {
+ write(i, "Screen session in use.\n", 23);
+ close(i);
+ Kill(pid, SIG_BYE);
+ return -1;
+ }
+#endif
+ }
+
+ /* create new display */
+ GetTTY(i, &Mode);
+ if (MakeDisplay(user, m->m_tty, attach ? m->m.attach.envterm : "", i, pid, &Mode) == 0)
+ {
+ write(i, "Could not make display.\n", 24);
+ close(i);
+ Msg(0, "Attach: could not make display for user %s", user);
+ Kill(pid, SIG_BYE);
+ return -1;
+ }
+#ifdef ENCODINGS
+ if (attach)
+ {
+# ifdef UTF8
+ D_encoding = m->m.attach.encoding == 1 ? UTF8 : m->m.attach.encoding ? m->m.attach.encoding - 1 : 0;
+# else
+ D_encoding = m->m.attach.encoding ? m->m.attach.encoding - 1 : 0;
+# endif
+ if (D_encoding < 0 || !EncodingName(D_encoding))
+ D_encoding = 0;
+ }
+#endif
+
+ if (iflag && olddisplays)
+ {
+ iflag = 0;
+#if defined(TERMIO) || defined(POSIX)
+ olddisplays->d_NewMode.tio.c_cc[VINTR] = VDISABLE;
+ olddisplays->d_NewMode.tio.c_lflag &= ~ISIG;
+#else /* TERMIO || POSIX */
+ olddisplays->d_NewMode.m_tchars.t_intrc = -1;
+#endif /* TERMIO || POSIX */
+ SetTTY(olddisplays->d_userfd, &olddisplays->d_NewMode);
+ }
+ SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
+ SetTTY(D_userfd, &D_NewMode);
+ if (fcntl(D_userfd, F_SETFL, FNBLOCK))
+ Msg(errno, "Warning: NBLOCK fcntl failed");
+ return 0;
+}
+
+void
+ReceiveMsg()
+{
+ int left, len;
+ static struct msg m;
+ char *p;
+ int ns = ServerSocket;
+ struct win *wi;
+ int recvfd = -1;
+ struct acluser *user;
+ bool is_socket;
+
+ /* Socket specific variables. */
+ struct sockaddr_un a;
+ struct msghdr msg;
+ struct iovec iov;
+ char control[1024];
+
+ is_socket = IsSocket(SockPath);
+ if (!is_socket)
+ {
+ debug("Ha, there was someone knocking on my fifo??\n");
+ if (fcntl(ServerSocket, F_SETFL, 0) == -1)
+ Panic(errno, "BLOCK fcntl");
+ p = (char *)&m;
+ left = sizeof(m);
+ }
+ else
+ {
+ len = sizeof(a);
+ debug("Ha, there was someone knocking on my socket??\n");
+ if ((ns = accept(ns, (struct sockaddr *)&a, (void *)&len)) < 0)
+ {
+ Msg(errno, "accept");
+ return;
+ }
+
+ p = (char *)&m;
+ left = sizeof(m);
+ bzero(&msg, sizeof(msg));
+ iov.iov_base = &m;
+ iov.iov_len = left;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_controllen = sizeof(control);
+ msg.msg_control = &control;
+ while (left > 0)
+ {
+ len = recvmsg(ns, &msg, 0);
+ if (len < 0 && errno == EINTR)
+ continue;
+ if (len < 0)
+ {
+ close(ns);
+ Msg(errno, "read");
+ return;
+ }
+ if (msg.msg_controllen)
+ {
+ struct cmsghdr *cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg))
+ {
+ int cl;
+ char *cp;
+ if (cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS)
+ continue;
+ cp = (char *)CMSG_DATA(cmsg);
+ cl = cmsg->cmsg_len;
+ while (cl >= CMSG_LEN(sizeof(int)))
+ {
+ int passedfd;
+ bcopy(cp, &passedfd, sizeof(int));
+ if (recvfd >= 0 && passedfd != recvfd)
+ close(recvfd);
+ recvfd = passedfd;
+ cl -= CMSG_LEN(sizeof(int));
+ }
+ }
+ }
+ p += len;
+ left -= len;
+ break;
+ }
+ }
+
+ while (left > 0)
+ {
+ len = read(ns, p, left);
+ if (len < 0 && errno == EINTR)
+ continue;
+ if (len <= 0)
+ break;
+ p += len;
+ left -= len;
+ }
+
+ if (!is_socket)
+ {
+#ifndef BROKEN_PIPE
+ /* Reopen pipe to prevent EOFs at the select() call */
+ close(ServerSocket);
+ if ((ServerSocket = secopen(SockPath, O_RDONLY | O_NONBLOCK, 0)) < 0)
+ Panic(errno, "reopen fifo %s", SockPath);
+ evdeq(&serv_read);
+ serv_read.fd = ServerSocket;
+ evenq(&serv_read);
+#endif
+ }
+ else
+ {
+ close(ns);
+ }
+
+ if (len < 0)
+ {
+ Msg(errno, "read");
+ if (recvfd != -1)
+ close(recvfd);
+ return;
+ }
+ if (left > 0)
+ {
+ if (left != sizeof(m))
+ Msg(0, "Message %d of %d bytes too small", left, (int)sizeof(m));
+ else
+ debug("No data on socket.\n");
+ return;
+ }
+ if (m.protocol_revision != MSG_REVISION)
+ {
+ if (recvfd != -1)
+ close(recvfd);
+ Msg(0, "Invalid message (magic 0x%08x).", m.protocol_revision);
+ return;
+ }
+
+ debug2("*** RecMsg: type %d tty %s\n", m.type, m.m_tty);
+ if (m.type != MSG_ATTACH && recvfd != -1)
+ {
+ close(recvfd);
+ recvfd = -1;
+ }
+
+ for (display = displays; display; display = display->d_next)
+ if (TTYCMP(D_usertty, m.m_tty) == 0)
+ break;
+ debug2("display: %s display %sfound\n", m.m_tty, display ? "" : "not ");
+ wi = 0;
+ if (!display)
+ {
+ for (wi = windows; wi; wi = wi->w_next)
+ if (!TTYCMP(m.m_tty, wi->w_tty))
+ {
+ /* XXX: hmmm, rework this? */
+ display =
+ wi->w_layer.l_cvlist ? wi->w_layer.l_cvlist->c_display : 0;
+ debug2("but window %s %sfound.\n", m.m_tty,
+ display ? "" : "(backfacing)");
+ break;
+ }
+ }
+
+ /* Remove the status to prevent garbage on the screen */
+ if (display && D_status)
+ RemoveStatus();
+
+ if (display && !D_tcinited && m.type != MSG_HANGUP)
+ {
+ if (recvfd != -1)
+ close(recvfd);
+ return; /* ignore messages for bad displays */
+ }
+
+ switch (m.type)
+ {
+ case MSG_WINCH:
+ if (display)
+ CheckScreenSize(1); /* Change fore */
+ break;
+ case MSG_CREATE:
+ /*
+ * the window that issued the create message need not be an
+ * active
+ * window. Then we create the window without having a display.
+ * Resulting in another inactive window.
+ */
+ ExecCreate(&m);
+ break;
+ case MSG_CONT:
+ if (display && D_userpid != 0 && kill(D_userpid, 0) == 0)
+ break; /* Intruder Alert */
+ debug2("RecMsg: apid=%d,was %d\n", m.m.attach.apid,
+ display ? D_userpid : 0);
+ /* FALLTHROUGH */
+
+ case MSG_ATTACH:
+ if (CreateTempDisplay(&m, recvfd, wi))
+ break;
+#ifdef PASSWORD
+ if (D_user->u_password && *D_user->u_password)
+ AskPassword(&m);
+ else
+#endif
+ FinishAttach(&m);
+ break;
+ case MSG_ERROR:
+ Msg(0, "%s", m.m.message);
+ break;
+ case MSG_HANGUP:
+ if (!wi) /* ignore hangups from inside */
+ Hangup();
+ break;
+#ifdef REMOTE_DETACH
+ case MSG_DETACH:
+#ifdef POW_DETACH
+ case MSG_POW_DETACH:
+#endif /* POW_DETACH */
+#ifdef PASSWORD
+ user = *FindUserPtr(m.m.detach.duser);
+ if (user && user->u_password && *user->u_password)
+ {
+ if (CreateTempDisplay(&m, recvfd, 0))
+ break;
+ AskPassword(&m);
+ }
+ else
+#endif /* PASSWORD */
+ FinishDetach(&m);
+ break;
+#endif
+ case MSG_QUERY:
+ {
+ char *oldSockPath = SaveStr(SockPath);
+ strcpy(SockPath, m.m.command.writeback);
+ bool is_socket = IsSocket(SockPath);
+ int s = MakeClientSocket(0, is_socket);
+ strcpy(SockPath, oldSockPath);
+ Free(oldSockPath);
+ if (s >= 0)
+ {
+ queryflag = s;
+ DoCommandMsg(&m);
+ close(s);
+ }
+ else
+ queryflag = -1;
+
+ Kill(m.m.command.apid,
+ (queryflag >= 0)
+ ? SIGCONT
+ : SIG_BYE); /* Send SIG_BYE if an error happened */
+ queryflag = -1;
+ }
+ break;
+ case MSG_COMMAND:
+ DoCommandMsg(&m);
+ break;
+ default:
+ Msg(0, "Invalid message (type %d).", m.type);
+ }
+}
+
+void
+ReceiveRaw(s)
+int s;
+{
+ char rd[256];
+ int len = 0;
+ struct sockaddr_un a;
+ bool is_socket;
+
+ is_socket = IsSocket(SockPath);
+ if (!is_socket)
+ {
+ if (fcntl(s, F_SETFL, 0) < 0)
+ Panic(errno, "BLOCK fcntl");
+ }
+ else
+ {
+ len = sizeof(a);
+ s = accept(s, (struct sockaddr *)&a, (void *)&len);
+ if (s < 0)
+ {
+ Msg(errno, "accept");
+ return;
+ }
+ }
+
+ while ((len = read(s, rd, 255)) > 0)
+ {
+ rd[len] = 0;
+ printf("%s", rd);
+ }
+ close(s);
+}
+
+#if defined(_SEQUENT_)
+#undef connect
+/*
+ * sequent_ptx socket emulation must have mode 000 on the socket!
+ */
+static int
+sconnect(s, sapp, len)
+int s, len;
+struct sockaddr *sapp;
+{
+ register struct sockaddr_un *sap;
+ struct stat st;
+ int x;
+
+ sap = (struct sockaddr_un *)sapp;
+ if (stat(sap->sun_path, &st))
+ return -1;
+ chmod(sap->sun_path, 0);
+ x = connect(s, (struct sockaddr *) sap, len);
+ chmod(sap->sun_path, st.st_mode);
+ return x;
+}
+#endif
+
+
+/*
+ * Set the mode bits of the socket to the current status
+ */
+int
+chsock()
+{
+ int r, euid = geteuid();
+ if (euid != real_uid)
+ {
+ if (UserContext() <= 0)
+ return UserStatus();
+ }
+ r = chmod(SockPath, SOCKMODE);
+ /*
+ * Sockets usually reside in the /tmp/ area, where sysadmin scripts
+ * may be happy to remove old files. We manually prevent the socket
+ * from becoming old. (chmod does not touch mtime).
+ */
+ (void)utimes(SockPath, NULL);
+
+ if (euid != real_uid)
+ UserReturn(r);
+ return r;
+}
+
+/*
+ * Try to recreate the socket/pipe
+ */
+int
+RecoverSocket()
+{
+ bool is_socket;
+
+ close(ServerSocket);
+ if ((int)geteuid() != real_uid)
+ {
+ if (UserContext() > 0)
+ UserReturn(unlink(SockPath));
+ (void)UserStatus();
+ }
+ else
+ (void) unlink(SockPath);
+
+ is_socket = IsSocket(SockPath);
+ if ((ServerSocket = MakeServerSocket(is_socket)) < 0)
+ return 0;
+ evdeq(&serv_read);
+ serv_read.fd = ServerSocket;
+ evenq(&serv_read);
+ return 1;
+}
+
+
+static void
+FinishAttach(m)
+struct msg *m;
+{
+ char *p;
+ int pid;
+ int noshowwin;
+ struct win *wi;
+
+ ASSERT(display);
+ pid = D_userpid;
+
+#ifdef REMOTE_DETACH
+ if (m->m.attach.detachfirst == MSG_DETACH
+# ifdef POW_DETACH
+ || m->m.attach.detachfirst == MSG_POW_DETACH
+# endif
+ )
+ FinishDetach(m);
+#endif
+
+#if defined(pyr) || defined(xelos) || defined(sequent)
+ /*
+ * Kludge for systems with braindamaged termcap routines,
+ * which evaluate $TERMCAP, regardless whether it describes
+ * the correct terminal type or not.
+ */
+ debug("unsetenv(TERMCAP) in case of a different terminal");
+ unsetenv("TERMCAP");
+#endif
+
+ /*
+ * We reboot our Terminal Emulator. Forget all we knew about
+ * the old terminal, reread the termcap entries in .screenrc
+ * (and nothing more from .screenrc is read. Mainly because
+ * I did not check, whether a full reinit is safe. jw)
+ * and /etc/screenrc, and initialise anew.
+ */
+ if (extra_outcap)
+ free(extra_outcap);
+ if (extra_incap)
+ free(extra_incap);
+ extra_incap = extra_outcap = 0;
+ debug2("Message says size (%dx%d)\n", m->m.attach.columns, m->m.attach.lines);
+#ifdef ETCSCREENRC
+# ifdef ALLOW_SYSSCREENRC
+ if ((p = getenv("SYSSCREENRC")))
+ StartRc(p, 1);
+ else
+# endif
+ StartRc(ETCSCREENRC, 1);
+#endif
+ StartRc(RcFileName, 1);
+ if (InitTermcap(m->m.attach.columns, m->m.attach.lines))
+ {
+ FreeDisplay();
+ Kill(pid, SIG_BYE);
+ return;
+ }
+ MakeDefaultCanvas();
+ InitTerm(m->m.attach.adaptflag); /* write init string on fd */
+ if (displays->d_next == 0)
+ (void) chsock();
+ signal(SIGHUP, SigHup);
+ if (m->m.attach.esc != -1 && m->m.attach.meta_esc != -1)
+ {
+ D_user->u_Esc = m->m.attach.esc;
+ D_user->u_MetaEsc = m->m.attach.meta_esc;
+ }
+
+#ifdef UTMPOK
+ /*
+ * we set the Utmp slots again, if we were detached normally
+ * and if we were detached by ^Z.
+ * don't log zomies back in!
+ */
+ RemoveLoginSlot();
+ if (displays->d_next == 0)
+ for (wi = windows; wi; wi = wi->w_next)
+ if (wi->w_ptyfd >= 0 && wi->w_slot != (slot_t) -1)
+ SetUtmp(wi);
+#endif
+
+ D_fore = NULL;
+ if (layout_attach)
+ {
+ struct layout *lay = layout_attach;
+ if (lay == &layout_last_marker)
+ lay = layout_last;
+ if (lay)
+ {
+ LoadLayout(lay, &D_canvas);
+ SetCanvasWindow(D_forecv, 0);
+ }
+ }
+ /*
+ * there may be a window that we remember from last detach:
+ */
+ debug1("D_user->u_detachwin = %d\n", D_user->u_detachwin);
+ if (D_user->u_detachwin >= 0)
+ fore = wtab[D_user->u_detachwin];
+ else
+ fore = 0;
+
+ /* Wayne wants us to restore the other window too. */
+ if (D_user->u_detachotherwin >= 0)
+ D_other = wtab[D_user->u_detachotherwin];
+
+ noshowwin = 0;
+ if (*m->m.attach.preselect)
+ {
+ if (!strcmp(m->m.attach.preselect, "="))
+ fore = 0;
+ else if (!strcmp(m->m.attach.preselect, "-"))
+ {
+ fore = 0;
+ noshowwin = 1;
+ }
+ else if (!strcmp(m->m.attach.preselect, "+"))
+ {
+ struct action newscreen;
+ char *na = 0;
+ newscreen.nr = RC_SCREEN;
+ newscreen.args = &na;
+ newscreen.quiet = 0;
+ DoAction(&newscreen, -1);
+ }
+ else
+ fore = FindNiceWindow(fore, m->m.attach.preselect);
+ }
+ else
+ fore = FindNiceWindow(fore, 0);
+ if (fore)
+ SetForeWindow(fore);
+ else if (!noshowwin)
+ {
+#ifdef MULTIUSER
+ if (!AclCheckPermCmd(D_user, ACL_EXEC, &comms[RC_WINDOWLIST]))
+#endif
+ {
+ struct display *olddisplay = display;
+ flayer = D_forecv->c_layer;
+ display_windows(1, WLIST_NUM, (struct win *)0);
+ noshowwin = 1;
+ display = olddisplay; /* display_windows can change display */
+ }
+ }
+ Activate(0);
+ ResetIdle();
+ if (!D_fore && !noshowwin)
+ ShowWindows(-1);
+ if (displays->d_next == 0 && console_window)
+ {
+ if (TtyGrabConsole(console_window->w_ptyfd, 1, "reattach") == 0)
+ Msg(0, "console %s is on window %d", HostName, console_window->w_number);
+ }
+ debug("activated...\n");
+
+# if defined(DEBUG) && defined(SIG_NODEBUG)
+ if (!dfp)
+ {
+ sleep(1);
+ debug1("Attacher %d must not debug, as we have debug off.\n", pid);
+ kill(pid, SIG_NODEBUG);
+ }
+# endif /* SIG_NODEBUG */
+}
+
+static void
+FinishDetach(m)
+struct msg *m;
+{
+ struct display *next, **d, *det;
+ int pid;
+
+ if (m->type == MSG_ATTACH)
+ pid = D_userpid;
+ else
+ pid = m->m.detach.dpid;
+
+ /* Remove the temporary display prompting for the password from the list */
+ for (d = &displays; (det = *d); d = &det->d_next)
+ {
+ if (det->d_userpid == pid)
+ break;
+ }
+ if (det)
+ {
+ *d = det->d_next;
+ det->d_next = 0;
+ }
+
+ for (display = displays; display; display = next)
+ {
+ next = display->d_next;
+# ifdef POW_DETACH
+ if (m->type == MSG_POW_DETACH)
+ Detach(D_REMOTE_POWER);
+ else
+# endif /* POW_DETACH */
+ if (m->type == MSG_DETACH)
+ Detach(D_REMOTE);
+ else if (m->type == MSG_ATTACH)
+ {
+#ifdef POW_DETACH
+ if (m->m.attach.detachfirst == MSG_POW_DETACH)
+ Detach(D_REMOTE_POWER);
+ else
+#endif
+ if (m->m.attach.detachfirst == MSG_DETACH)
+ Detach(D_REMOTE);
+ }
+ }
+ display = displays = det;
+ if (m->type != MSG_ATTACH)
+ {
+ if (display)
+ FreeDisplay();
+ Kill(pid, SIGCONT);
+ }
+}
+
+#ifdef PASSWORD
+static void PasswordProcessInput __P((char *, int));
+
+struct pwdata {
+ int l;
+ char buf[MAXLOGINLEN + 1];
+ struct msg m;
+};
+
+static void
+AskPassword(m)
+struct msg *m;
+{
+ struct pwdata *pwdata;
+ ASSERT(display);
+ pwdata = (struct pwdata *)malloc(sizeof(struct pwdata));
+ if (!pwdata)
+ Panic(0, "%s", strnomem);
+ pwdata->l = 0;
+ pwdata->m = *m;
+ D_processinputdata = (char *)pwdata;
+ D_processinput = PasswordProcessInput;
+ AddStr("Screen password: ");
+}
+
+static void
+PasswordProcessInput(ibuf, ilen)
+char *ibuf;
+int ilen;
+{
+ struct pwdata *pwdata;
+ int c, l;
+ char *up;
+ int pid = D_userpid;
+
+ pwdata = (struct pwdata *)D_processinputdata;
+ l = pwdata->l;
+ while (ilen-- > 0)
+ {
+ c = *(unsigned char *)ibuf++;
+ if (c == '\r' || c == '\n')
+ {
+ char *buf = NULL;
+ up = D_user->u_password;
+ pwdata->buf[l] = 0;
+ buf = crypt(pwdata->buf, up);
+ if (!buf || strncmp(buf, up, strlen(up)))
+ {
+ /* uh oh, user failed */
+ bzero(pwdata->buf, sizeof(pwdata->buf));
+ if (!buf)
+ AddStr("\r\ncrypt() failed.\r\n");
+ else
+ AddStr("\r\nPassword incorrect.\r\n");
+ D_processinputdata = 0; /* otherwise freed by FreeDis */
+ FreeDisplay();
+ Msg(0, "Illegal reattach attempt from terminal %s.", pwdata->m.m_tty);
+ free(pwdata);
+ Kill(pid, SIG_BYE);
+ return;
+ }
+ /* great, pw matched, all is fine */
+ bzero(pwdata->buf, sizeof(pwdata->buf));
+ AddStr("\r\n");
+ D_processinputdata = 0;
+ D_processinput = ProcessInput;
+#ifdef REMOTE_DETACH
+ if (pwdata->m.type == MSG_DETACH
+# ifdef POW_DETACH
+ || pwdata->m.type == MSG_POW_DETACH
+# endif
+ )
+ FinishDetach(&pwdata->m);
+ else
+#endif
+ FinishAttach(&pwdata->m);
+ free(pwdata);
+ return;
+ }
+ if (c == Ctrl('c'))
+ {
+ AddStr("\r\n");
+ FreeDisplay();
+ Kill(pid, SIG_BYE);
+ return;
+ }
+ if (c == '\b' || c == 0177)
+ {
+ if (l > 0)
+ l--;
+ continue;
+ }
+ if (c == Ctrl('u'))
+ {
+ l = 0;
+ continue;
+ }
+ if (l < (int)sizeof(pwdata->buf) - 1)
+ pwdata->buf[l++] = c;
+ }
+ pwdata->l = l;
+}
+#endif
+
+/* 'end' is exclusive, i.e. you should *not* write in *end */
+static char *
+strncpy_escape_quote(dst, src, end)
+char *dst;
+const char *src, *end;
+{
+ while (*src && dst < end)
+ {
+ if (*src == '"')
+ {
+ if (dst + 2 < end) /* \\ \" \0 */
+ *dst++ = '\\';
+ else
+ return NULL;
+ }
+ *dst++ = *src++;
+ }
+ if (dst >= end)
+ return NULL;
+
+ *dst = '\0';
+ return dst;
+}
+
+static void
+DoCommandMsg(mp)
+struct msg *mp;
+{
+ char *args[MAXARGS];
+ int argl[MAXARGS];
+ char fullcmd[MAXSTR];
+ register char *fc;
+ int n;
+ register char *p = mp->m.command.cmd;
+ struct acluser *user;
+#ifdef MULTIUSER
+ extern struct acluser *EffectiveAclUser; /* acls.c */
+#else
+ extern struct acluser *users; /* acls.c */
+#endif
+
+ n = mp->m.command.nargs;
+ if (n > MAXARGS - 1)
+ n = MAXARGS - 1;
+ for (fc = fullcmd; n > 0; n--)
+ {
+ int len = strlen(p);
+ *fc++ = '"';
+ if (!(fc = strncpy_escape_quote(fc, p, fullcmd + sizeof(fullcmd) - 2))) /* '"' ' ' */
+ {
+ Msg(0, "Remote command too long.");
+ queryflag = -1;
+ return;
+ }
+ p += len + 1;
+ *fc++ = '"';
+ *fc++ = ' ';
+ }
+ if (fc != fullcmd)
+ *--fc = 0;
+ if (Parse(fullcmd, sizeof fullcmd, args, argl) <= 0)
+ {
+ queryflag = -1;
+ return;
+ }
+#ifdef MULTIUSER
+ user = *FindUserPtr(mp->m.attach.auser);
+ if (user == 0)
+ {
+ Msg(0, "Unknown user %s tried to send a command!", mp->m.attach.auser);
+ queryflag = -1;
+ return;
+ }
+#else
+ user = users;
+#endif
+#ifdef PASSWORD
+ if (user->u_password && *user->u_password)
+ {
+ Msg(0, "User %s has a password, cannot use remote commands (using -Q or -X option).", mp->m.attach.auser);
+ queryflag = -1;
+ return;
+ }
+#endif
+ if (!display)
+ for (display = displays; display; display = display->d_next)
+ if (D_user == user)
+ break;
+ for (fore = windows; fore; fore = fore->w_next)
+ if (!TTYCMP(mp->m_tty, fore->w_tty))
+ {
+ if (!display)
+ display = fore->w_layer.l_cvlist ? fore->w_layer.l_cvlist->c_display : 0;
+
+ /* If the window is not visibile in any display, then do not use the originating window as
+ * the foreground window for the command. This way, if there is an existing display, then
+ * the command will execute from the foreground window of that display. This is necessary so
+ * that commands that are relative to the window (e.g. 'next' etc.) do the right thing. */
+ if (!fore->w_layer.l_cvlist || !fore->w_layer.l_cvlist->c_display)
+ fore = NULL;
+ break;
+ }
+ if (!display)
+ display = displays; /* sigh */
+ if (*mp->m.command.preselect)
+ {
+ int i = -1;
+ if (strcmp(mp->m.command.preselect, "-"))
+ {
+ i = WindowByNoN(mp->m.command.preselect);
+ if (i < 0 || !wtab[i])
+ {
+ Msg(0, "Could not find pre-select window.");
+ queryflag = -1;
+ return;
+ }
+ }
+ fore = i >= 0 ? wtab[i] : 0;
+ }
+ else if (!fore)
+ {
+ if (display && D_user == user)
+ fore = Layer2Window(display->d_forecv->c_layer);
+ if (!fore)
+ {
+ fore = user->u_detachwin >= 0 ? wtab[user->u_detachwin] : 0;
+ fore = FindNiceWindow(fore, 0);
+ }
+ }
+ if (!fore)
+ fore = windows; /* sigh */
+#ifdef MULTIUSER
+ EffectiveAclUser = user;
+#endif
+ if (*args)
+ {
+ char *oldrcname = rc_name;
+ rc_name = "-X";
+ debug3("Running command on display %x window %x (%d)\n", display, fore, fore ? fore->w_number : -1);
+ flayer = fore ? &fore->w_layer : 0;
+ if (fore && fore->w_savelayer && (fore->w_blocked || fore->w_savelayer->l_cvlist == 0))
+ flayer = fore->w_savelayer;
+ DoCommand(args, argl);
+ rc_name = oldrcname;
+ }
+#ifdef MULTIUSER
+ EffectiveAclUser = 0;
+#endif
+}
+
+int
+SendAttachMsg(s, m, fd)
+int s;
+struct msg *m;
+int fd;
+{
+ int r;
+ struct msghdr msg;
+ struct iovec iov;
+ char buf[CMSG_SPACE(sizeof(int))];
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = (char *)m;
+ iov.iov_len = sizeof(*m);
+ bzero(&msg, sizeof(msg));
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof(buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ bcopy(&fd, CMSG_DATA(cmsg), sizeof(int));
+ msg.msg_controllen = cmsg->cmsg_len;
+ while(1)
+ {
+ r = sendmsg(s, &msg, 0);
+ if (r == -1 && errno == EINTR)
+ continue;
+ if (r == -1)
+ return -1;
+ return 0;
+ }
+}
+
+bool
+IsSocket(path)
+const char *path;
+{
+ struct stat st;
+
+ if (stat(path, &st) < 0)
+ return false;
+
+ return S_ISSOCK(st.st_mode);
+}