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