Annotation of src/usr.bin/tmux/client.c, Revision 1.24
1.24 ! nicm 1: /* $OpenBSD: client.c,v 1.23 2009/10/13 13:11:06 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
20: #include <sys/ioctl.h>
21: #include <sys/socket.h>
22: #include <sys/stat.h>
23: #include <sys/un.h>
24: #include <sys/wait.h>
25:
26: #include <errno.h>
27: #include <fcntl.h>
28: #include <pwd.h>
29: #include <stdlib.h>
30: #include <string.h>
31: #include <syslog.h>
32: #include <unistd.h>
33:
34: #include "tmux.h"
35:
1.11 nicm 36: void client_send_environ(struct client_ctx *);
1.1 nicm 37:
38: int
1.4 nicm 39: client_init(char *path, struct client_ctx *cctx, int cmdflags, int flags)
1.1 nicm 40: {
41: struct sockaddr_un sa;
42: struct stat sb;
43: struct msg_identify_data data;
44: struct winsize ws;
45: size_t size;
1.14 nicm 46: int fd, fd2, mode;
1.19 nicm 47: char *term;
1.2 nicm 48: char rpathbuf[MAXPATHLEN];
49:
50: if (realpath(path, rpathbuf) == NULL)
51: strlcpy(rpathbuf, path, sizeof rpathbuf);
52: setproctitle("client (%s)", rpathbuf);
1.1 nicm 53:
54: if (lstat(path, &sb) != 0) {
1.4 nicm 55: if (cmdflags & CMD_STARTSERVER && errno == ENOENT) {
1.10 nicm 56: if ((fd = server_start(path)) == -1)
1.1 nicm 57: goto start_failed;
58: goto server_started;
59: }
60: goto not_found;
61: }
62: if (!S_ISSOCK(sb.st_mode)) {
63: errno = ENOTSOCK;
64: goto not_found;
65: }
66:
67: memset(&sa, 0, sizeof sa);
68: sa.sun_family = AF_UNIX;
69: size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
70: if (size >= sizeof sa.sun_path) {
71: errno = ENAMETOOLONG;
72: goto not_found;
73: }
74:
1.10 nicm 75: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
1.18 nicm 76: fatal("socket failed");
1.1 nicm 77:
1.10 nicm 78: if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
1.1 nicm 79: if (errno == ECONNREFUSED) {
1.4 nicm 80: if (unlink(path) != 0 || !(cmdflags & CMD_STARTSERVER))
1.1 nicm 81: goto not_found;
1.10 nicm 82: if ((fd = server_start(path)) == -1)
1.1 nicm 83: goto start_failed;
84: goto server_started;
85: }
86: goto not_found;
87: }
88:
89: server_started:
1.10 nicm 90: if ((mode = fcntl(fd, F_GETFL)) == -1)
1.1 nicm 91: fatal("fcntl failed");
1.10 nicm 92: if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
1.22 nicm 93: fatal("fcntl failed");
94: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
1.1 nicm 95: fatal("fcntl failed");
1.12 nicm 96: imsg_init(&cctx->ibuf, fd);
1.1 nicm 97:
1.11 nicm 98: if (cmdflags & CMD_SENDENVIRON)
99: client_send_environ(cctx);
1.1 nicm 100: if (isatty(STDIN_FILENO)) {
101: if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
102: fatal("ioctl(TIOCGWINSZ)");
103: data.flags = flags;
1.7 nicm 104:
1.1 nicm 105: if (getcwd(data.cwd, sizeof data.cwd) == NULL)
106: *data.cwd = '\0';
107:
1.7 nicm 108: *data.term = '\0';
109: if ((term = getenv("TERM")) != NULL) {
110: if (strlcpy(data.term,
111: term, sizeof data.term) >= sizeof data.term)
112: *data.term = '\0';
113: }
114:
1.19 nicm 115: if ((fd2 = dup(STDIN_FILENO)) == -1)
116: fatal("dup failed");
1.13 nicm 117: imsg_compose(&cctx->ibuf, MSG_IDENTIFY,
1.14 nicm 118: PROTOCOL_VERSION, -1, fd2, &data, sizeof data);
1.1 nicm 119: }
120:
121: return (0);
122:
123: start_failed:
124: log_warnx("server failed to start");
125: return (1);
126:
127: not_found:
128: log_warn("server not found");
129: return (1);
130: }
131:
1.11 nicm 132: void
133: client_send_environ(struct client_ctx *cctx)
134: {
135: char **var;
136: struct msg_environ_data data;
137:
138: for (var = environ; *var != NULL; var++) {
139: if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
140: continue;
141: client_write_server(cctx, MSG_ENVIRON, &data, sizeof data);
142: }
143: }
144:
1.1 nicm 145: int
146: client_main(struct client_ctx *cctx)
147: {
148: struct pollfd pfd;
1.17 nicm 149: int n, nfds;
1.1 nicm 150:
151: siginit();
152:
153: logfile("client");
154:
1.17 nicm 155: /*
156: * imsg_read in the first client poll loop (before the terminal has
157: * been initialiased) may have read messages into the buffer after the
158: * MSG_READY switched to here. Process anything outstanding now so poll
159: * doesn't hang waiting for messages that have already arrived.
160: */
161: if (client_msg_dispatch(cctx) != 0)
162: goto out;
163:
1.8 nicm 164: for (;;) {
165: if (sigterm)
166: client_write_server(cctx, MSG_EXITING, NULL, 0);
1.1 nicm 167: if (sigchld) {
168: waitpid(WAIT_ANY, NULL, WNOHANG);
169: sigchld = 0;
170: }
1.20 nicm 171: if (sigwinch) {
172: client_write_server(cctx, MSG_RESIZE, NULL, 0);
173: sigwinch = 0;
174: }
1.1 nicm 175: if (sigcont) {
176: siginit();
177: client_write_server(cctx, MSG_WAKEUP, NULL, 0);
178: sigcont = 0;
179: }
180:
1.12 nicm 181: pfd.fd = cctx->ibuf.fd;
1.1 nicm 182: pfd.events = POLLIN;
1.12 nicm 183: if (cctx->ibuf.w.queued > 0)
1.1 nicm 184: pfd.events |= POLLOUT;
185:
1.12 nicm 186: if ((nfds = poll(&pfd, 1, INFTIM)) == -1) {
1.1 nicm 187: if (errno == EAGAIN || errno == EINTR)
188: continue;
189: fatal("poll failed");
190: }
1.12 nicm 191: if (nfds == 0)
192: continue;
193:
194: if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))
195: fatalx("socket error");
1.1 nicm 196:
1.12 nicm 197: if (pfd.revents & POLLIN) {
1.17 nicm 198: if ((n = imsg_read(&cctx->ibuf)) == -1 || n == 0) {
199: cctx->exittype = CCTX_DIED;
200: break;
201: }
1.12 nicm 202: if (client_msg_dispatch(cctx) != 0)
203: break;
1.5 nicm 204: }
1.9 nicm 205:
1.12 nicm 206: if (pfd.revents & POLLOUT) {
207: if (msgbuf_write(&cctx->ibuf.w) < 0) {
208: cctx->exittype = CCTX_DIED;
209: break;
210: }
211: }
1.1 nicm 212: }
1.17 nicm 213:
214: out:
1.23 nicm 215: /*
216: * Print exit status message, unless running as a login shell where it
217: * would either be pointless or irritating.
218: */
1.24 ! nicm 219: if (sigterm) {
! 220: printf("[terminated]\n");
! 221: return (1);
! 222: }
! 223: switch (cctx->exittype) {
! 224: case CCTX_DIED:
! 225: printf("[lost server]\n");
! 226: return (0);
! 227: case CCTX_SHUTDOWN:
! 228: if (!login_shell)
! 229: printf("[server exited]\n");
! 230: return (0);
! 231: case CCTX_EXIT:
! 232: if (cctx->errstr != NULL) {
! 233: printf("[error: %s]\n", cctx->errstr);
1.23 nicm 234: return (1);
235: }
1.24 ! nicm 236: if (!login_shell)
1.23 nicm 237: printf("[exited]\n");
1.24 ! nicm 238: return (0);
! 239: case CCTX_DETACH:
! 240: if (!login_shell)
1.23 nicm 241: printf("[detached]\n");
1.24 ! nicm 242: return (0);
! 243: default:
! 244: printf("[unknown error]\n");
! 245: return (1);
1.1 nicm 246: }
1.9 nicm 247: }
248:
249: int
250: client_msg_dispatch(struct client_ctx *cctx)
251: {
1.12 nicm 252: struct imsg imsg;
1.9 nicm 253: struct msg_print_data printdata;
1.21 nicm 254: struct msg_lock_data lockdata;
1.12 nicm 255: ssize_t n, datalen;
1.9 nicm 256:
257: for (;;) {
1.12 nicm 258: if ((n = imsg_get(&cctx->ibuf, &imsg)) == -1)
259: fatalx("imsg_get failed");
260: if (n == 0)
1.9 nicm 261: return (0);
1.12 nicm 262: datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
1.9 nicm 263:
1.12 nicm 264: switch (imsg.hdr.type) {
1.9 nicm 265: case MSG_DETACH:
1.12 nicm 266: if (datalen != 0)
1.9 nicm 267: fatalx("bad MSG_DETACH size");
268:
269: client_write_server(cctx, MSG_EXITING, NULL, 0);
270: cctx->exittype = CCTX_DETACH;
271: break;
272: case MSG_ERROR:
1.12 nicm 273: if (datalen != sizeof printdata)
274: fatalx("bad MSG_ERROR size");
275: memcpy(&printdata, imsg.data, sizeof printdata);
1.11 nicm 276:
1.9 nicm 277: printdata.msg[(sizeof printdata.msg) - 1] = '\0';
1.16 nicm 278: /* Error string used after exit message from server. */
1.9 nicm 279: cctx->errstr = xstrdup(printdata.msg);
1.12 nicm 280: imsg_free(&imsg);
1.9 nicm 281: return (-1);
282: case MSG_EXIT:
1.12 nicm 283: if (datalen != 0)
1.9 nicm 284: fatalx("bad MSG_EXIT size");
1.12 nicm 285:
1.9 nicm 286: client_write_server(cctx, MSG_EXITING, NULL, 0);
287: cctx->exittype = CCTX_EXIT;
288: break;
289: case MSG_EXITED:
1.12 nicm 290: if (datalen != 0)
1.9 nicm 291: fatalx("bad MSG_EXITED size");
292:
1.12 nicm 293: imsg_free(&imsg);
1.9 nicm 294: return (-1);
295: case MSG_SHUTDOWN:
1.12 nicm 296: if (datalen != 0)
1.9 nicm 297: fatalx("bad MSG_SHUTDOWN size");
298:
299: client_write_server(cctx, MSG_EXITING, NULL, 0);
300: cctx->exittype = CCTX_SHUTDOWN;
301: break;
302: case MSG_SUSPEND:
1.12 nicm 303: if (datalen != 0)
1.9 nicm 304: fatalx("bad MSG_SUSPEND size");
305:
306: client_suspend();
1.21 nicm 307: break;
308: case MSG_LOCK:
309: if (datalen != sizeof lockdata)
310: fatalx("bad MSG_LOCK size");
311: memcpy(&lockdata, imsg.data, sizeof lockdata);
312:
313: lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
314: system(lockdata.cmd);
315: client_write_server(cctx, MSG_UNLOCK, NULL, 0);
1.9 nicm 316: break;
317: default:
318: fatalx("unexpected message");
319: }
1.12 nicm 320:
321: imsg_free(&imsg);
1.9 nicm 322: }
1.1 nicm 323: }