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