xref: /netbsd-src/external/bsd/tmux/dist/client.c (revision 1b9578b8c2c1f848eeb16dabbfd7d1f0d9fdefbd)
1 /* $Id: client.c,v 1.1.1.1 2011/03/10 09:15:36 jmmv 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/socket.h>
21 #include <sys/stat.h>
22 #include <sys/un.h>
23 #include <sys/wait.h>
24 
25 #include <errno.h>
26 #include <event.h>
27 #include <fcntl.h>
28 #include <pwd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "tmux.h"
34 
35 struct imsgbuf	client_ibuf;
36 struct event	client_event;
37 const char     *client_exitmsg;
38 int		client_exitval;
39 int		client_attached;
40 
41 int		client_connect(char *, int);
42 void		client_send_identify(int);
43 void		client_send_environ(void);
44 void		client_write_server(enum msgtype, void *, size_t);
45 void		client_update_event(void);
46 void		client_signal(int, short, void *);
47 void		client_callback(int, short, void *);
48 int		client_dispatch_attached(void);
49 int		client_dispatch_wait(void *);
50 
51 /* Connect client to server. */
52 int
53 client_connect(char *path, int start_server)
54 {
55 	struct sockaddr_un	sa;
56 	size_t			size;
57 	int			fd, mode;
58 
59 	memset(&sa, 0, sizeof sa);
60 	sa.sun_family = AF_UNIX;
61 	size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
62 	if (size >= sizeof sa.sun_path) {
63 		errno = ENAMETOOLONG;
64 		return (-1);
65 	}
66 
67 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
68 		fatal("socket failed");
69 
70 	if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
71 		if (!start_server)
72 			goto failed;
73 		switch (errno) {
74 		case ECONNREFUSED:
75 			if (unlink(path) != 0)
76 				goto failed;
77 			/* FALLTHROUGH */
78 		case ENOENT:
79 			if ((fd = server_start()) == -1)
80 				goto failed;
81 			break;
82 		default:
83 			goto failed;
84 		}
85 	}
86 
87 	if ((mode = fcntl(fd, F_GETFL)) == -1)
88 		fatal("fcntl failed");
89 	if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
90 		fatal("fcntl failed");
91 	return (fd);
92 
93 failed:
94 	close(fd);
95 	return (-1);
96 }
97 
98 /* Client main loop. */
99 int
100 client_main(int argc, char **argv, int flags)
101 {
102 	struct cmd		*cmd;
103 	struct cmd_list		*cmdlist;
104 	struct msg_command_data	 cmddata;
105 	int			 cmdflags, fd;
106 	enum msgtype		 msg;
107 	char			*cause;
108 
109 	/* Set up the initial command. */
110 	cmdflags = 0;
111 	if (shell_cmd != NULL) {
112 		msg = MSG_SHELL;
113 		cmdflags = CMD_STARTSERVER;
114 	} else if (argc == 0) {
115 		msg = MSG_COMMAND;
116 		cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
117 	} else {
118 		msg = MSG_COMMAND;
119 
120 		/*
121 		 * It sucks parsing the command string twice (in client and
122 		 * later in server) but it is necessary to get the start server
123 		 * flag.
124 		 */
125 		if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
126 			log_warnx("%s", cause);
127 			return (1);
128 		}
129 		cmdflags &= ~CMD_STARTSERVER;
130 		TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
131 			if (cmd->entry->flags & CMD_STARTSERVER)
132 				cmdflags |= CMD_STARTSERVER;
133 			if (cmd->entry->flags & CMD_SENDENVIRON)
134 				cmdflags |= CMD_SENDENVIRON;
135 			if (cmd->entry->flags & CMD_CANTNEST)
136 				cmdflags |= CMD_CANTNEST;
137 		}
138 		cmd_list_free(cmdlist);
139 	}
140 
141 	/*
142 	 * Check if this could be a nested session, if the command can't nest:
143 	 * if the socket path matches $TMUX, this is probably the same server.
144 	 */
145 	if (shell_cmd == NULL && environ_path != NULL &&
146 	    cmdflags & CMD_CANTNEST && strcmp(socket_path, environ_path) == 0) {
147 		log_warnx("sessions should be nested with care. "
148 		    "unset $TMUX to force.");
149 		return (1);
150 	}
151 
152 	/* Initialise the client socket and start the server. */
153 	fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER);
154 	if (fd == -1) {
155 		log_warn("failed to connect to server");
156 		return (1);
157 	}
158 
159 	/* Set process title, log and signals now this is the client. */
160 #ifdef HAVE_SETPROCTITLE
161 	setproctitle("client (%s)", socket_path);
162 #endif
163 	logfile("client");
164 
165 	/* Create imsg. */
166 	imsg_init(&client_ibuf, fd);
167 	event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
168 
169 	/* Establish signal handlers. */
170 	set_signals(client_signal);
171 
172 	/* Send initial environment. */
173 	if (cmdflags & CMD_SENDENVIRON)
174 		client_send_environ();
175 	client_send_identify(flags);
176 
177 	/* Send first command. */
178 	if (msg == MSG_COMMAND) {
179 		/* Fill in command line arguments. */
180 		cmddata.pid = environ_pid;
181 		cmddata.idx = environ_idx;
182 
183 		/* Prepare command for server. */
184 		cmddata.argc = argc;
185 		if (cmd_pack_argv(
186 		    argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
187 			log_warnx("command too long");
188 			return (1);
189 		}
190 
191 		client_write_server(msg, &cmddata, sizeof cmddata);
192 	} else if (msg == MSG_SHELL)
193 		client_write_server(msg, NULL, 0);
194 
195 	/* Set the event and dispatch. */
196 	client_update_event();
197 	event_dispatch();
198 
199 	/* Print the exit message, if any, and exit. */
200 	if (client_attached && client_exitmsg != NULL && !login_shell)
201 		printf("[%s]\n", client_exitmsg);
202 	return (client_exitval);
203 }
204 
205 /* Send identify message to server with the file descriptors. */
206 void
207 client_send_identify(int flags)
208 {
209 	struct msg_identify_data	data;
210 	char			       *term;
211 	int				fd;
212 
213 	data.flags = flags;
214 
215 	if (getcwd(data.cwd, sizeof data.cwd) == NULL)
216 		*data.cwd = '\0';
217 
218 	term = getenv("TERM");
219 	if (term == NULL ||
220 	    strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
221 		*data.term = '\0';
222 
223 	if ((fd = dup(STDIN_FILENO)) == -1)
224 		fatal("dup failed");
225 	imsg_compose(&client_ibuf,
226 	    MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
227 
228 	if ((fd = dup(STDOUT_FILENO)) == -1)
229 		fatal("dup failed");
230 	imsg_compose(&client_ibuf,
231 	    MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
232 
233 	if ((fd = dup(STDERR_FILENO)) == -1)
234 		fatal("dup failed");
235 	imsg_compose(&client_ibuf,
236 	    MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
237 }
238 
239 /* Forward entire environment to server. */
240 void
241 client_send_environ(void)
242 {
243 	struct msg_environ_data	data;
244 	char		      **var;
245 
246 	for (var = environ; *var != NULL; var++) {
247 		if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
248 			continue;
249 		client_write_server(MSG_ENVIRON, &data, sizeof data);
250 	}
251 }
252 
253 /* Write a message to the server without a file descriptor. */
254 void
255 client_write_server(enum msgtype type, void *buf, size_t len)
256 {
257 	imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
258 }
259 
260 /* Update client event based on whether it needs to read or read and write. */
261 void
262 client_update_event(void)
263 {
264 	short	events;
265 
266 	event_del(&client_event);
267 	events = EV_READ;
268 	if (client_ibuf.w.queued > 0)
269 		events |= EV_WRITE;
270 	event_set(
271 	    &client_event, client_ibuf.fd, events, client_callback, shell_cmd);
272 	event_add(&client_event, NULL);
273 }
274 
275 /* Callback to handle signals in the client. */
276 /* ARGSUSED */
277 void
278 client_signal(int sig, unused short events, unused void *data)
279 {
280 	struct sigaction sigact;
281 	int		 status;
282 
283 	if (!client_attached) {
284 		switch (sig) {
285 		case SIGCHLD:
286 			waitpid(WAIT_ANY, &status, WNOHANG);
287 			break;
288 		case SIGTERM:
289 			event_loopexit(NULL);
290 			break;
291 		}
292 	} else {
293 		switch (sig) {
294 		case SIGHUP:
295 			client_exitmsg = "lost tty";
296 			client_exitval = 1;
297 			client_write_server(MSG_EXITING, NULL, 0);
298 			break;
299 		case SIGTERM:
300 			client_exitmsg = "terminated";
301 			client_exitval = 1;
302 			client_write_server(MSG_EXITING, NULL, 0);
303 			break;
304 		case SIGWINCH:
305 			client_write_server(MSG_RESIZE, NULL, 0);
306 			break;
307 		case SIGCONT:
308 			memset(&sigact, 0, sizeof sigact);
309 			sigemptyset(&sigact.sa_mask);
310 			sigact.sa_flags = SA_RESTART;
311 			sigact.sa_handler = SIG_IGN;
312 			if (sigaction(SIGTSTP, &sigact, NULL) != 0)
313 				fatal("sigaction failed");
314 			client_write_server(MSG_WAKEUP, NULL, 0);
315 			break;
316 		}
317 	}
318 
319 	client_update_event();
320 }
321 
322 /* Callback for client imsg read events. */
323 /* ARGSUSED */
324 void
325 client_callback(unused int fd, short events, void *data)
326 {
327 	ssize_t	n;
328 	int	retval;
329 
330 	if (events & EV_READ) {
331 		if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
332 			goto lost_server;
333 		if (client_attached)
334 			retval = client_dispatch_attached();
335 		else
336 			retval = client_dispatch_wait(data);
337 		if (retval != 0) {
338 			event_loopexit(NULL);
339 			return;
340 		}
341 	}
342 
343 	if (events & EV_WRITE) {
344 		if (msgbuf_write(&client_ibuf.w) < 0)
345 			goto lost_server;
346 	}
347 
348 	client_update_event();
349 	return;
350 
351 lost_server:
352 	client_exitmsg = "lost server";
353 	client_exitval = 1;
354 	event_loopexit(NULL);
355 }
356 
357 /* Dispatch imsgs when in wait state (before MSG_READY). */
358 int
359 client_dispatch_wait(void *data)
360 {
361 	struct imsg		imsg;
362 	ssize_t			n, datalen;
363 	struct msg_shell_data	shelldata;
364 	struct msg_exit_data	exitdata;
365 	const char             *shellcmd = data;
366 
367 	if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
368 		fatalx("imsg_read failed");
369 
370 	for (;;) {
371 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
372 			fatalx("imsg_get failed");
373 		if (n == 0)
374 			return (0);
375 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
376 
377 		switch (imsg.hdr.type) {
378 		case MSG_EXIT:
379 		case MSG_SHUTDOWN:
380 			if (datalen != sizeof exitdata) {
381 				if (datalen != 0)
382 					fatalx("bad MSG_EXIT size");
383 			} else {
384 				memcpy(&exitdata, imsg.data, sizeof exitdata);
385 				client_exitval = exitdata.retcode;
386 			}
387 			imsg_free(&imsg);
388 			return (-1);
389 		case MSG_READY:
390 			if (datalen != 0)
391 				fatalx("bad MSG_READY size");
392 
393 			client_attached = 1;
394 			break;
395 		case MSG_VERSION:
396 			if (datalen != 0)
397 				fatalx("bad MSG_VERSION size");
398 
399 			log_warnx("protocol version mismatch (client %u, "
400 			    "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
401 			client_exitval = 1;
402 
403 			imsg_free(&imsg);
404 			return (-1);
405 		case MSG_SHELL:
406 			if (datalen != sizeof shelldata)
407 				fatalx("bad MSG_SHELL size");
408 			memcpy(&shelldata, imsg.data, sizeof shelldata);
409 			shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
410 
411 			clear_signals(0);
412 
413 			shell_exec(shelldata.shell, shellcmd);
414 			/* NOTREACHED */
415 		default:
416 			fatalx("unexpected message");
417 		}
418 
419 		imsg_free(&imsg);
420 	}
421 }
422 
423 /* Dispatch imsgs in attached state (after MSG_READY). */
424 /* ARGSUSED */
425 int
426 client_dispatch_attached(void)
427 {
428 	struct imsg		imsg;
429 	struct msg_lock_data	lockdata;
430 	struct sigaction	sigact;
431 	ssize_t			n, datalen;
432 
433 	for (;;) {
434 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
435 			fatalx("imsg_get failed");
436 		if (n == 0)
437 			return (0);
438 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
439 
440 		log_debug("client got %d", imsg.hdr.type);
441 		switch (imsg.hdr.type) {
442 		case MSG_DETACH:
443 			if (datalen != 0)
444 				fatalx("bad MSG_DETACH size");
445 
446 			client_write_server(MSG_EXITING, NULL, 0);
447 			client_exitmsg = "detached";
448 			break;
449 		case MSG_EXIT:
450 			if (datalen != 0 &&
451 			    datalen != sizeof (struct msg_exit_data))
452 				fatalx("bad MSG_EXIT size");
453 
454 			client_write_server(MSG_EXITING, NULL, 0);
455 			client_exitmsg = "exited";
456 			break;
457 		case MSG_EXITED:
458 			if (datalen != 0)
459 				fatalx("bad MSG_EXITED size");
460 
461 			imsg_free(&imsg);
462 			return (-1);
463 		case MSG_SHUTDOWN:
464 			if (datalen != 0)
465 				fatalx("bad MSG_SHUTDOWN size");
466 
467 			client_write_server(MSG_EXITING, NULL, 0);
468 			client_exitmsg = "server exited";
469 			client_exitval = 1;
470 			break;
471 		case MSG_SUSPEND:
472 			if (datalen != 0)
473 				fatalx("bad MSG_SUSPEND size");
474 
475 			memset(&sigact, 0, sizeof sigact);
476 			sigemptyset(&sigact.sa_mask);
477 			sigact.sa_flags = SA_RESTART;
478 			sigact.sa_handler = SIG_DFL;
479 			if (sigaction(SIGTSTP, &sigact, NULL) != 0)
480 				fatal("sigaction failed");
481 			kill(getpid(), SIGTSTP);
482 			break;
483 		case MSG_LOCK:
484 			if (datalen != sizeof lockdata)
485 				fatalx("bad MSG_LOCK size");
486 			memcpy(&lockdata, imsg.data, sizeof lockdata);
487 
488 			lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
489 			system(lockdata.cmd);
490 			client_write_server(MSG_UNLOCK, NULL, 0);
491 			break;
492 		default:
493 			fatalx("unexpected message");
494 		}
495 
496 		imsg_free(&imsg);
497 	}
498 }
499