Annotation of src/usr.bin/tmux/client.c, Revision 1.29
1.29 ! nicm 1: /* $OpenBSD: client.c,v 1.28 2009/11/02 13:41:25 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.25 nicm 36: struct imsgbuf client_ibuf;
37: const char *client_exitmsg;
1.1 nicm 38:
1.26 nicm 39: void client_send_identify(int);
1.25 nicm 40: void client_send_environ(void);
41: void client_write_server(enum msgtype, void *, size_t);
42: int client_dispatch(void);
43: void client_suspend(void);
44:
45: struct imsgbuf *
46: client_init(char *path, int cmdflags, int flags)
1.1 nicm 47: {
1.26 nicm 48: struct sockaddr_un sa;
49: size_t size;
50: int fd, mode;
51: char rpathbuf[MAXPATHLEN];
1.2 nicm 52:
53: if (realpath(path, rpathbuf) == NULL)
54: strlcpy(rpathbuf, path, sizeof rpathbuf);
55: setproctitle("client (%s)", rpathbuf);
1.1 nicm 56:
57: memset(&sa, 0, sizeof sa);
58: sa.sun_family = AF_UNIX;
59: size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
60: if (size >= sizeof sa.sun_path) {
61: errno = ENAMETOOLONG;
62: goto not_found;
63: }
64:
1.10 nicm 65: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
1.18 nicm 66: fatal("socket failed");
1.1 nicm 67:
1.10 nicm 68: if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
1.28 nicm 69: if (!(cmdflags & CMD_STARTSERVER))
70: goto not_found;
71: switch (errno) {
72: case ECONNREFUSED:
73: if (unlink(path) != 0)
1.1 nicm 74: goto not_found;
1.28 nicm 75: /* FALLTHROUGH */
76: case ENOENT:
1.10 nicm 77: if ((fd = server_start(path)) == -1)
1.1 nicm 78: goto start_failed;
79: goto server_started;
80: }
81: goto not_found;
82: }
83:
84: server_started:
1.10 nicm 85: if ((mode = fcntl(fd, F_GETFL)) == -1)
1.1 nicm 86: fatal("fcntl failed");
1.10 nicm 87: if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
1.22 nicm 88: fatal("fcntl failed");
89: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
1.1 nicm 90: fatal("fcntl failed");
1.25 nicm 91: imsg_init(&client_ibuf, fd);
1.1 nicm 92:
1.11 nicm 93: if (cmdflags & CMD_SENDENVIRON)
1.25 nicm 94: client_send_environ();
1.26 nicm 95: if (isatty(STDIN_FILENO))
96: client_send_identify(flags);
1.1 nicm 97:
1.25 nicm 98: return (&client_ibuf);
1.1 nicm 99:
100: start_failed:
101: log_warnx("server failed to start");
1.25 nicm 102: return (NULL);
1.1 nicm 103:
104: not_found:
105: log_warn("server not found");
1.25 nicm 106: return (NULL);
1.1 nicm 107: }
108:
1.11 nicm 109: void
1.26 nicm 110: client_send_identify(int flags)
111: {
112: struct msg_identify_data data;
113: struct winsize ws;
114: char *term;
115: int fd;
116:
117: if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
118: fatal("ioctl(TIOCGWINSZ)");
119: data.flags = flags;
120:
121: if (getcwd(data.cwd, sizeof data.cwd) == NULL)
122: *data.cwd = '\0';
123:
124: term = getenv("TERM");
125: if (term == NULL ||
126: strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
127: *data.term = '\0';
128:
129: if ((fd = dup(STDIN_FILENO)) == -1)
130: fatal("dup failed");
131: imsg_compose(&client_ibuf,
132: MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
133: }
134:
135: void
1.25 nicm 136: client_send_environ(void)
1.11 nicm 137: {
1.26 nicm 138: struct msg_environ_data data;
1.11 nicm 139: char **var;
140:
141: for (var = environ; *var != NULL; var++) {
142: if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
143: continue;
1.25 nicm 144: client_write_server(MSG_ENVIRON, &data, sizeof data);
1.11 nicm 145: }
146: }
147:
1.25 nicm 148: void
149: client_write_server(enum msgtype type, void *buf, size_t len)
150: {
151: imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
152: }
153:
154: __dead void
155: client_main(void)
1.1 nicm 156: {
157: struct pollfd pfd;
1.17 nicm 158: int n, nfds;
1.1 nicm 159:
160: siginit();
161:
162: logfile("client");
163:
1.17 nicm 164: /*
165: * imsg_read in the first client poll loop (before the terminal has
166: * been initialiased) may have read messages into the buffer after the
167: * MSG_READY switched to here. Process anything outstanding now so poll
168: * doesn't hang waiting for messages that have already arrived.
169: */
1.25 nicm 170: if (client_dispatch() != 0)
1.17 nicm 171: goto out;
172:
1.8 nicm 173: for (;;) {
1.25 nicm 174: if (sigterm) {
175: client_exitmsg = "terminated";
176: client_write_server(MSG_EXITING, NULL, 0);
177: }
1.1 nicm 178: if (sigchld) {
1.27 nicm 179: sigchld = 0;
1.1 nicm 180: waitpid(WAIT_ANY, NULL, WNOHANG);
1.27 nicm 181: continue;
1.1 nicm 182: }
1.20 nicm 183: if (sigwinch) {
1.27 nicm 184: sigwinch = 0;
1.25 nicm 185: client_write_server(MSG_RESIZE, NULL, 0);
1.27 nicm 186: continue;
1.20 nicm 187: }
1.1 nicm 188: if (sigcont) {
1.27 nicm 189: sigcont = 0;
1.1 nicm 190: siginit();
1.25 nicm 191: client_write_server(MSG_WAKEUP, NULL, 0);
1.27 nicm 192: continue;
1.1 nicm 193: }
194:
1.25 nicm 195: pfd.fd = client_ibuf.fd;
1.1 nicm 196: pfd.events = POLLIN;
1.25 nicm 197: if (client_ibuf.w.queued > 0)
1.1 nicm 198: pfd.events |= POLLOUT;
199:
1.12 nicm 200: if ((nfds = poll(&pfd, 1, INFTIM)) == -1) {
1.1 nicm 201: if (errno == EAGAIN || errno == EINTR)
202: continue;
203: fatal("poll failed");
204: }
1.12 nicm 205: if (nfds == 0)
206: continue;
207:
208: if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))
209: fatalx("socket error");
1.1 nicm 210:
1.12 nicm 211: if (pfd.revents & POLLIN) {
1.25 nicm 212: if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) {
213: client_exitmsg = "lost server";
1.17 nicm 214: break;
215: }
1.25 nicm 216: if (client_dispatch() != 0)
1.12 nicm 217: break;
1.5 nicm 218: }
1.9 nicm 219:
1.12 nicm 220: if (pfd.revents & POLLOUT) {
1.25 nicm 221: if (msgbuf_write(&client_ibuf.w) < 0) {
222: client_exitmsg = "lost server";
1.12 nicm 223: break;
224: }
225: }
1.1 nicm 226: }
1.17 nicm 227:
228: out:
1.25 nicm 229: /* Print the exit message, if any, and exit. */
230: if (client_exitmsg != NULL) {
1.24 nicm 231: if (!login_shell)
1.25 nicm 232: printf("[%s]\n", client_exitmsg);
233: exit(1);
1.1 nicm 234: }
1.25 nicm 235: exit(0);
1.9 nicm 236: }
237:
238: int
1.25 nicm 239: client_dispatch(void)
1.9 nicm 240: {
1.12 nicm 241: struct imsg imsg;
1.21 nicm 242: struct msg_lock_data lockdata;
1.12 nicm 243: ssize_t n, datalen;
1.9 nicm 244:
245: for (;;) {
1.25 nicm 246: if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
1.12 nicm 247: fatalx("imsg_get failed");
248: if (n == 0)
1.9 nicm 249: return (0);
1.12 nicm 250: datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
1.9 nicm 251:
1.12 nicm 252: switch (imsg.hdr.type) {
1.9 nicm 253: case MSG_DETACH:
1.12 nicm 254: if (datalen != 0)
1.9 nicm 255: fatalx("bad MSG_DETACH size");
256:
1.25 nicm 257: client_write_server(MSG_EXITING, NULL, 0);
258: client_exitmsg = "detached";
1.9 nicm 259: break;
260: case MSG_EXIT:
1.12 nicm 261: if (datalen != 0)
1.9 nicm 262: fatalx("bad MSG_EXIT size");
1.12 nicm 263:
1.25 nicm 264: client_write_server(MSG_EXITING, NULL, 0);
265: client_exitmsg = "exited";
1.9 nicm 266: break;
267: case MSG_EXITED:
1.12 nicm 268: if (datalen != 0)
1.9 nicm 269: fatalx("bad MSG_EXITED size");
270:
1.12 nicm 271: imsg_free(&imsg);
1.9 nicm 272: return (-1);
273: case MSG_SHUTDOWN:
1.12 nicm 274: if (datalen != 0)
1.9 nicm 275: fatalx("bad MSG_SHUTDOWN size");
276:
1.25 nicm 277: client_write_server(MSG_EXITING, NULL, 0);
278: client_exitmsg = "server exited";
1.9 nicm 279: break;
280: case MSG_SUSPEND:
1.12 nicm 281: if (datalen != 0)
1.9 nicm 282: fatalx("bad MSG_SUSPEND size");
283:
284: client_suspend();
1.21 nicm 285: break;
286: case MSG_LOCK:
287: if (datalen != sizeof lockdata)
288: fatalx("bad MSG_LOCK size");
289: memcpy(&lockdata, imsg.data, sizeof lockdata);
290:
291: lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
292: system(lockdata.cmd);
1.25 nicm 293: client_write_server(MSG_UNLOCK, NULL, 0);
1.9 nicm 294: break;
295: default:
296: fatalx("unexpected message");
297: }
1.12 nicm 298:
299: imsg_free(&imsg);
1.9 nicm 300: }
1.25 nicm 301: }
302:
303: void
304: client_suspend(void)
305: {
306: struct sigaction act;
307:
308: memset(&act, 0, sizeof act);
309: sigemptyset(&act.sa_mask);
310: act.sa_flags = SA_RESTART;
311:
312: act.sa_handler = SIG_DFL;
313: if (sigaction(SIGTSTP, &act, NULL) != 0)
314: fatal("sigaction failed");
315:
316: act.sa_handler = sighandler;
317: if (sigaction(SIGCONT, &act, NULL) != 0)
318: fatal("sigaction failed");
319:
320: kill(getpid(), SIGTSTP);
1.1 nicm 321: }