xref: /openbsd-src/usr.bin/tmux/client.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /* $OpenBSD: client.c,v 1.22 2009/09/23 12:03:30 nicm Exp $ */
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 
36 void	client_send_environ(struct client_ctx *);
37 
38 int
39 client_init(char *path, struct client_ctx *cctx, int cmdflags, int flags)
40 {
41 	struct sockaddr_un		sa;
42 	struct stat			sb;
43 	struct msg_identify_data	data;
44 	struct winsize			ws;
45 	size_t				size;
46 	int				fd, fd2, mode;
47 	char			       *term;
48 	char		 		rpathbuf[MAXPATHLEN];
49 
50 	if (realpath(path, rpathbuf) == NULL)
51 		strlcpy(rpathbuf, path, sizeof rpathbuf);
52 	setproctitle("client (%s)", rpathbuf);
53 
54 	if (lstat(path, &sb) != 0) {
55 		if (cmdflags & CMD_STARTSERVER && errno == ENOENT) {
56 			if ((fd = server_start(path)) == -1)
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 
75 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
76 		fatal("socket failed");
77 
78 	if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
79 		if (errno == ECONNREFUSED) {
80 			if (unlink(path) != 0 || !(cmdflags & CMD_STARTSERVER))
81 				goto not_found;
82 			if ((fd = server_start(path)) == -1)
83 				goto start_failed;
84 			goto server_started;
85 		}
86 		goto not_found;
87 	}
88 
89 server_started:
90 	if ((mode = fcntl(fd, F_GETFL)) == -1)
91 		fatal("fcntl failed");
92 	if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
93 		fatal("fcntl failed");
94 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
95 		fatal("fcntl failed");
96 	imsg_init(&cctx->ibuf, fd);
97 
98 	if (cmdflags & CMD_SENDENVIRON)
99 		client_send_environ(cctx);
100 	if (isatty(STDIN_FILENO)) {
101 		if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
102 			fatal("ioctl(TIOCGWINSZ)");
103 		data.flags = flags;
104 
105 		if (getcwd(data.cwd, sizeof data.cwd) == NULL)
106 			*data.cwd = '\0';
107 
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 
115 		if ((fd2 = dup(STDIN_FILENO)) == -1)
116 			fatal("dup failed");
117 		imsg_compose(&cctx->ibuf, MSG_IDENTIFY,
118 		    PROTOCOL_VERSION, -1, fd2, &data, sizeof data);
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 
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 
145 int
146 client_main(struct client_ctx *cctx)
147 {
148 	struct pollfd	 pfd;
149 	int		 n, nfds;
150 
151 	siginit();
152 
153 	logfile("client");
154 
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 
164 	for (;;) {
165 		if (sigterm)
166 			client_write_server(cctx, MSG_EXITING, NULL, 0);
167 		if (sigchld) {
168 			waitpid(WAIT_ANY, NULL, WNOHANG);
169 			sigchld = 0;
170 		}
171 		if (sigwinch) {
172 			client_write_server(cctx, MSG_RESIZE, NULL, 0);
173  			sigwinch = 0;
174 		}
175 		if (sigcont) {
176 			siginit();
177 			client_write_server(cctx, MSG_WAKEUP, NULL, 0);
178 			sigcont = 0;
179 		}
180 
181 		pfd.fd = cctx->ibuf.fd;
182 		pfd.events = POLLIN;
183 		if (cctx->ibuf.w.queued > 0)
184 			pfd.events |= POLLOUT;
185 
186 		if ((nfds = poll(&pfd, 1, INFTIM)) == -1) {
187 			if (errno == EAGAIN || errno == EINTR)
188 				continue;
189 			fatal("poll failed");
190 		}
191 		if (nfds == 0)
192 			continue;
193 
194 		if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))
195 			fatalx("socket error");
196 
197 		if (pfd.revents & POLLIN) {
198 			if ((n = imsg_read(&cctx->ibuf)) == -1 || n == 0) {
199 				cctx->exittype = CCTX_DIED;
200 				break;
201 			}
202 			if (client_msg_dispatch(cctx) != 0)
203 				break;
204 		}
205 
206 		if (pfd.revents & POLLOUT) {
207 			if (msgbuf_write(&cctx->ibuf.w) < 0) {
208 				cctx->exittype = CCTX_DIED;
209 				break;
210 			}
211 		}
212 	}
213 
214 out:
215  	if (sigterm) {
216  		printf("[terminated]\n");
217  		return (1);
218  	}
219 	switch (cctx->exittype) {
220 	case CCTX_DIED:
221 		printf("[lost server]\n");
222 		return (0);
223 	case CCTX_SHUTDOWN:
224 		printf("[server exited]\n");
225 		return (0);
226 	case CCTX_EXIT:
227 		if (cctx->errstr != NULL) {
228 			printf("[error: %s]\n", cctx->errstr);
229 			return (1);
230 		}
231 		printf("[exited]\n");
232 		return (0);
233 	case CCTX_DETACH:
234 		printf("[detached]\n");
235 		return (0);
236 	default:
237 		printf("[unknown error]\n");
238 		return (1);
239 	}
240 }
241 
242 int
243 client_msg_dispatch(struct client_ctx *cctx)
244 {
245 	struct imsg		 imsg;
246 	struct msg_print_data	 printdata;
247 	struct msg_lock_data	 lockdata;
248 	ssize_t			 n, datalen;
249 
250 	for (;;) {
251 		if ((n = imsg_get(&cctx->ibuf, &imsg)) == -1)
252 			fatalx("imsg_get failed");
253 		if (n == 0)
254 			return (0);
255 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
256 
257 		switch (imsg.hdr.type) {
258 		case MSG_DETACH:
259 			if (datalen != 0)
260 				fatalx("bad MSG_DETACH size");
261 
262 			client_write_server(cctx, MSG_EXITING, NULL, 0);
263 			cctx->exittype = CCTX_DETACH;
264 			break;
265 		case MSG_ERROR:
266 			if (datalen != sizeof printdata)
267 				fatalx("bad MSG_ERROR size");
268 			memcpy(&printdata, imsg.data, sizeof printdata);
269 
270 			printdata.msg[(sizeof printdata.msg) - 1] = '\0';
271 			/* Error string used after exit message from server. */
272 			cctx->errstr = xstrdup(printdata.msg);
273 			imsg_free(&imsg);
274 			return (-1);
275 		case MSG_EXIT:
276 			if (datalen != 0)
277 				fatalx("bad MSG_EXIT size");
278 
279 			client_write_server(cctx, MSG_EXITING, NULL, 0);
280 			cctx->exittype = CCTX_EXIT;
281 			break;
282 		case MSG_EXITED:
283 			if (datalen != 0)
284 				fatalx("bad MSG_EXITED size");
285 
286 			imsg_free(&imsg);
287 			return (-1);
288 		case MSG_SHUTDOWN:
289 			if (datalen != 0)
290 				fatalx("bad MSG_SHUTDOWN size");
291 
292 			client_write_server(cctx, MSG_EXITING, NULL, 0);
293 			cctx->exittype = CCTX_SHUTDOWN;
294 			break;
295 		case MSG_SUSPEND:
296 			if (datalen != 0)
297 				fatalx("bad MSG_SUSPEND size");
298 
299 			client_suspend();
300 			break;
301 		case MSG_LOCK:
302 			if (datalen != sizeof lockdata)
303 				fatalx("bad MSG_LOCK size");
304 			memcpy(&lockdata, imsg.data, sizeof lockdata);
305 
306 			lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
307 			system(lockdata.cmd);
308 			client_write_server(cctx, MSG_UNLOCK, NULL, 0);
309 			break;
310 		default:
311 			fatalx("unexpected message");
312 		}
313 
314 		imsg_free(&imsg);
315 	}
316 }
317