xref: /minix3/external/bsd/tmux/dist/client.c (revision 27852ebe53d5bf221cf5058cb7e858fa8fa8895e)
1*0a6a1f1dSLionel Sambuc /* Id */
2eda6f593SDavid van Moolenbroek 
3eda6f593SDavid van Moolenbroek /*
4eda6f593SDavid van Moolenbroek  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5eda6f593SDavid van Moolenbroek  *
6eda6f593SDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software for any
7eda6f593SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
8eda6f593SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
9eda6f593SDavid van Moolenbroek  *
10eda6f593SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eda6f593SDavid van Moolenbroek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eda6f593SDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eda6f593SDavid van Moolenbroek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eda6f593SDavid van Moolenbroek  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15eda6f593SDavid van Moolenbroek  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16eda6f593SDavid van Moolenbroek  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eda6f593SDavid van Moolenbroek  */
18eda6f593SDavid van Moolenbroek 
19eda6f593SDavid van Moolenbroek #include <sys/types.h>
20*0a6a1f1dSLionel Sambuc #include <sys/file.h>
21eda6f593SDavid van Moolenbroek #include <sys/socket.h>
22eda6f593SDavid van Moolenbroek #include <sys/stat.h>
23eda6f593SDavid van Moolenbroek #include <sys/un.h>
24eda6f593SDavid van Moolenbroek #include <sys/wait.h>
25eda6f593SDavid van Moolenbroek 
26eda6f593SDavid van Moolenbroek #include <errno.h>
27eda6f593SDavid van Moolenbroek #include <event.h>
28eda6f593SDavid van Moolenbroek #include <fcntl.h>
29eda6f593SDavid van Moolenbroek #include <pwd.h>
30eda6f593SDavid van Moolenbroek #include <stdlib.h>
31eda6f593SDavid van Moolenbroek #include <string.h>
32eda6f593SDavid van Moolenbroek #include <unistd.h>
33eda6f593SDavid van Moolenbroek 
34eda6f593SDavid van Moolenbroek #include "tmux.h"
35eda6f593SDavid van Moolenbroek 
36eda6f593SDavid van Moolenbroek struct imsgbuf	client_ibuf;
37eda6f593SDavid van Moolenbroek struct event	client_event;
38*0a6a1f1dSLionel Sambuc struct event	client_stdin;
39*0a6a1f1dSLionel Sambuc enum {
40*0a6a1f1dSLionel Sambuc 	CLIENT_EXIT_NONE,
41*0a6a1f1dSLionel Sambuc 	CLIENT_EXIT_DETACHED,
42*0a6a1f1dSLionel Sambuc 	CLIENT_EXIT_DETACHED_HUP,
43*0a6a1f1dSLionel Sambuc 	CLIENT_EXIT_LOST_TTY,
44*0a6a1f1dSLionel Sambuc 	CLIENT_EXIT_TERMINATED,
45*0a6a1f1dSLionel Sambuc 	CLIENT_EXIT_LOST_SERVER,
46*0a6a1f1dSLionel Sambuc 	CLIENT_EXIT_EXITED,
47*0a6a1f1dSLionel Sambuc 	CLIENT_EXIT_SERVER_EXITED,
48*0a6a1f1dSLionel Sambuc } client_exitreason = CLIENT_EXIT_NONE;
49eda6f593SDavid van Moolenbroek int		client_exitval;
50eda6f593SDavid van Moolenbroek enum msgtype	client_exittype;
51*0a6a1f1dSLionel Sambuc const char     *client_exitsession;
52eda6f593SDavid van Moolenbroek int		client_attached;
53eda6f593SDavid van Moolenbroek 
54*0a6a1f1dSLionel Sambuc int		client_get_lock(char *);
55eda6f593SDavid van Moolenbroek int		client_connect(char *, int);
56eda6f593SDavid van Moolenbroek void		client_send_identify(int);
57*0a6a1f1dSLionel Sambuc int		client_write_one(enum msgtype, int, const void *, size_t);
58*0a6a1f1dSLionel Sambuc int		client_write_server(enum msgtype, const void *, size_t);
59eda6f593SDavid van Moolenbroek void		client_update_event(void);
60eda6f593SDavid van Moolenbroek void		client_signal(int, short, void *);
61*0a6a1f1dSLionel Sambuc void		client_stdin_callback(int, short, void *);
62*0a6a1f1dSLionel Sambuc void		client_write(int, const char *, size_t);
63eda6f593SDavid van Moolenbroek void		client_callback(int, short, void *);
64eda6f593SDavid van Moolenbroek int		client_dispatch_attached(void);
65eda6f593SDavid van Moolenbroek int		client_dispatch_wait(void *);
66*0a6a1f1dSLionel Sambuc const char     *client_exit_message(void);
67*0a6a1f1dSLionel Sambuc 
68*0a6a1f1dSLionel Sambuc /*
69*0a6a1f1dSLionel Sambuc  * Get server create lock. If already held then server start is happening in
70*0a6a1f1dSLionel Sambuc  * another client, so block until the lock is released and return -1 to
71*0a6a1f1dSLionel Sambuc  * retry. Ignore other errors - just continue and start the server without the
72*0a6a1f1dSLionel Sambuc  * lock.
73*0a6a1f1dSLionel Sambuc  */
74*0a6a1f1dSLionel Sambuc int
client_get_lock(char * lockfile)75*0a6a1f1dSLionel Sambuc client_get_lock(char *lockfile)
76*0a6a1f1dSLionel Sambuc {
77*0a6a1f1dSLionel Sambuc 	int lockfd;
78*0a6a1f1dSLionel Sambuc 
79*0a6a1f1dSLionel Sambuc 	if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1)
80*0a6a1f1dSLionel Sambuc 		fatal("open failed");
81*0a6a1f1dSLionel Sambuc 
82*0a6a1f1dSLionel Sambuc 	if (lockf(lockfd, F_TLOCK, 0) == -1 && errno == EAGAIN) {
83*0a6a1f1dSLionel Sambuc 		while (lockf(lockfd, F_LOCK, 0) == -1 && errno == EINTR)
84*0a6a1f1dSLionel Sambuc 			/* nothing */;
85*0a6a1f1dSLionel Sambuc 		close(lockfd);
86*0a6a1f1dSLionel Sambuc 		return (-1);
87*0a6a1f1dSLionel Sambuc 	}
88*0a6a1f1dSLionel Sambuc 
89*0a6a1f1dSLionel Sambuc 	return (lockfd);
90*0a6a1f1dSLionel Sambuc }
91eda6f593SDavid van Moolenbroek 
92eda6f593SDavid van Moolenbroek /* Connect client to server. */
93eda6f593SDavid van Moolenbroek int
client_connect(char * path,int start_server)94eda6f593SDavid van Moolenbroek client_connect(char *path, int start_server)
95eda6f593SDavid van Moolenbroek {
96eda6f593SDavid van Moolenbroek 	struct sockaddr_un	sa;
97eda6f593SDavid van Moolenbroek 	size_t			size;
98*0a6a1f1dSLionel Sambuc 	int			fd, lockfd;
99*0a6a1f1dSLionel Sambuc 	char		       *lockfile;
100eda6f593SDavid van Moolenbroek 
101eda6f593SDavid van Moolenbroek 	memset(&sa, 0, sizeof sa);
102eda6f593SDavid van Moolenbroek 	sa.sun_family = AF_UNIX;
103eda6f593SDavid van Moolenbroek 	size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
104eda6f593SDavid van Moolenbroek 	if (size >= sizeof sa.sun_path) {
105eda6f593SDavid van Moolenbroek 		errno = ENAMETOOLONG;
106eda6f593SDavid van Moolenbroek 		return (-1);
107eda6f593SDavid van Moolenbroek 	}
108eda6f593SDavid van Moolenbroek 
109*0a6a1f1dSLionel Sambuc retry:
110eda6f593SDavid van Moolenbroek 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
111eda6f593SDavid van Moolenbroek 		fatal("socket failed");
112eda6f593SDavid van Moolenbroek 
113eda6f593SDavid van Moolenbroek 	if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
114*0a6a1f1dSLionel Sambuc 		if (errno != ECONNREFUSED && errno != ENOENT)
115*0a6a1f1dSLionel Sambuc 			goto failed;
116eda6f593SDavid van Moolenbroek 		if (!start_server)
117eda6f593SDavid van Moolenbroek 			goto failed;
118*0a6a1f1dSLionel Sambuc 		close(fd);
119*0a6a1f1dSLionel Sambuc 
120*0a6a1f1dSLionel Sambuc 		xasprintf(&lockfile, "%s.lock", path);
121*0a6a1f1dSLionel Sambuc 		if ((lockfd = client_get_lock(lockfile)) == -1) {
122*0a6a1f1dSLionel Sambuc 			free(lockfile);
123*0a6a1f1dSLionel Sambuc 			goto retry;
124eda6f593SDavid van Moolenbroek 		}
125*0a6a1f1dSLionel Sambuc 		if (unlink(path) != 0 && errno != ENOENT) {
126*0a6a1f1dSLionel Sambuc 			free(lockfile);
127*0a6a1f1dSLionel Sambuc 			close(lockfd);
128*0a6a1f1dSLionel Sambuc 			return (-1);
129*0a6a1f1dSLionel Sambuc 		}
130*0a6a1f1dSLionel Sambuc 		fd = server_start(lockfd, lockfile);
131*0a6a1f1dSLionel Sambuc 		free(lockfile);
132*0a6a1f1dSLionel Sambuc 		close(lockfd);
133eda6f593SDavid van Moolenbroek 	}
134eda6f593SDavid van Moolenbroek 
135eda6f593SDavid van Moolenbroek 	setblocking(fd, 0);
136eda6f593SDavid van Moolenbroek 	return (fd);
137eda6f593SDavid van Moolenbroek 
138eda6f593SDavid van Moolenbroek failed:
139eda6f593SDavid van Moolenbroek 	close(fd);
140eda6f593SDavid van Moolenbroek 	return (-1);
141eda6f593SDavid van Moolenbroek }
142eda6f593SDavid van Moolenbroek 
143*0a6a1f1dSLionel Sambuc /* Get exit string from reason number. */
144*0a6a1f1dSLionel Sambuc const char *
client_exit_message(void)145*0a6a1f1dSLionel Sambuc client_exit_message(void)
146*0a6a1f1dSLionel Sambuc {
147*0a6a1f1dSLionel Sambuc 	static char msg[256];
148*0a6a1f1dSLionel Sambuc 
149*0a6a1f1dSLionel Sambuc 	switch (client_exitreason) {
150*0a6a1f1dSLionel Sambuc 	case CLIENT_EXIT_NONE:
151*0a6a1f1dSLionel Sambuc 		break;
152*0a6a1f1dSLionel Sambuc 	case CLIENT_EXIT_DETACHED:
153*0a6a1f1dSLionel Sambuc 		if (client_exitsession != NULL) {
154*0a6a1f1dSLionel Sambuc 			xsnprintf(msg, sizeof msg, "detached "
155*0a6a1f1dSLionel Sambuc 			    "(from session %s)", client_exitsession);
156*0a6a1f1dSLionel Sambuc 			return (msg);
157*0a6a1f1dSLionel Sambuc 		}
158*0a6a1f1dSLionel Sambuc 		return ("detached");
159*0a6a1f1dSLionel Sambuc 	case CLIENT_EXIT_DETACHED_HUP:
160*0a6a1f1dSLionel Sambuc 		if (client_exitsession != NULL) {
161*0a6a1f1dSLionel Sambuc 			xsnprintf(msg, sizeof msg, "detached and SIGHUP "
162*0a6a1f1dSLionel Sambuc 			    "(from session %s)", client_exitsession);
163*0a6a1f1dSLionel Sambuc 			return (msg);
164*0a6a1f1dSLionel Sambuc 		}
165*0a6a1f1dSLionel Sambuc 		return ("detached and SIGHUP");
166*0a6a1f1dSLionel Sambuc 	case CLIENT_EXIT_LOST_TTY:
167*0a6a1f1dSLionel Sambuc 		return ("lost tty");
168*0a6a1f1dSLionel Sambuc 	case CLIENT_EXIT_TERMINATED:
169*0a6a1f1dSLionel Sambuc 		return ("terminated");
170*0a6a1f1dSLionel Sambuc 	case CLIENT_EXIT_LOST_SERVER:
171*0a6a1f1dSLionel Sambuc 		return ("lost server");
172*0a6a1f1dSLionel Sambuc 	case CLIENT_EXIT_EXITED:
173*0a6a1f1dSLionel Sambuc 		return ("exited");
174*0a6a1f1dSLionel Sambuc 	case CLIENT_EXIT_SERVER_EXITED:
175*0a6a1f1dSLionel Sambuc 		return ("server exited");
176*0a6a1f1dSLionel Sambuc 	}
177*0a6a1f1dSLionel Sambuc 	return ("unknown reason");
178*0a6a1f1dSLionel Sambuc }
179*0a6a1f1dSLionel Sambuc 
180eda6f593SDavid van Moolenbroek /* Client main loop. */
181eda6f593SDavid van Moolenbroek int
client_main(int argc,char ** argv,int flags)182eda6f593SDavid van Moolenbroek client_main(int argc, char **argv, int flags)
183eda6f593SDavid van Moolenbroek {
184eda6f593SDavid van Moolenbroek 	struct cmd		*cmd;
185eda6f593SDavid van Moolenbroek 	struct cmd_list		*cmdlist;
186*0a6a1f1dSLionel Sambuc 	struct msg_command_data	*data;
187*0a6a1f1dSLionel Sambuc 	int			 cmdflags, fd, i;
188eda6f593SDavid van Moolenbroek 	pid_t			 ppid;
189eda6f593SDavid van Moolenbroek 	enum msgtype		 msg;
190eda6f593SDavid van Moolenbroek 	char			*cause;
191*0a6a1f1dSLionel Sambuc 	struct termios		 tio, saved_tio;
192*0a6a1f1dSLionel Sambuc 	size_t			 size;
193eda6f593SDavid van Moolenbroek 
194eda6f593SDavid van Moolenbroek 	/* Set up the initial command. */
195eda6f593SDavid van Moolenbroek 	cmdflags = 0;
196eda6f593SDavid van Moolenbroek 	if (shell_cmd != NULL) {
197eda6f593SDavid van Moolenbroek 		msg = MSG_SHELL;
198eda6f593SDavid van Moolenbroek 		cmdflags = CMD_STARTSERVER;
199eda6f593SDavid van Moolenbroek 	} else if (argc == 0) {
200eda6f593SDavid van Moolenbroek 		msg = MSG_COMMAND;
201*0a6a1f1dSLionel Sambuc 		cmdflags = CMD_STARTSERVER|CMD_CANTNEST;
202eda6f593SDavid van Moolenbroek 	} else {
203eda6f593SDavid van Moolenbroek 		msg = MSG_COMMAND;
204eda6f593SDavid van Moolenbroek 
205eda6f593SDavid van Moolenbroek 		/*
206eda6f593SDavid van Moolenbroek 		 * It sucks parsing the command string twice (in client and
207eda6f593SDavid van Moolenbroek 		 * later in server) but it is necessary to get the start server
208eda6f593SDavid van Moolenbroek 		 * flag.
209eda6f593SDavid van Moolenbroek 		 */
210*0a6a1f1dSLionel Sambuc 		cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause);
211*0a6a1f1dSLionel Sambuc 		if (cmdlist == NULL) {
212*0a6a1f1dSLionel Sambuc 			fprintf(stderr, "%s\n", cause);
213eda6f593SDavid van Moolenbroek 			return (1);
214eda6f593SDavid van Moolenbroek 		}
215eda6f593SDavid van Moolenbroek 		cmdflags &= ~CMD_STARTSERVER;
216eda6f593SDavid van Moolenbroek 		TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
217eda6f593SDavid van Moolenbroek 			if (cmd->entry->flags & CMD_STARTSERVER)
218eda6f593SDavid van Moolenbroek 				cmdflags |= CMD_STARTSERVER;
219eda6f593SDavid van Moolenbroek 			if (cmd->entry->flags & CMD_CANTNEST)
220eda6f593SDavid van Moolenbroek 				cmdflags |= CMD_CANTNEST;
221eda6f593SDavid van Moolenbroek 		}
222eda6f593SDavid van Moolenbroek 		cmd_list_free(cmdlist);
223eda6f593SDavid van Moolenbroek 	}
224eda6f593SDavid van Moolenbroek 
225eda6f593SDavid van Moolenbroek 	/*
226eda6f593SDavid van Moolenbroek 	 * Check if this could be a nested session, if the command can't nest:
227eda6f593SDavid van Moolenbroek 	 * if the socket path matches $TMUX, this is probably the same server.
228eda6f593SDavid van Moolenbroek 	 */
229eda6f593SDavid van Moolenbroek 	if (shell_cmd == NULL && environ_path != NULL &&
230*0a6a1f1dSLionel Sambuc 	    (cmdflags & CMD_CANTNEST) &&
231*0a6a1f1dSLionel Sambuc 	    strcmp(socket_path, environ_path) == 0) {
232*0a6a1f1dSLionel Sambuc 		fprintf(stderr, "sessions should be nested with care, "
233*0a6a1f1dSLionel Sambuc 		    "unset $TMUX to force\n");
234eda6f593SDavid van Moolenbroek 		return (1);
235eda6f593SDavid van Moolenbroek 	}
236eda6f593SDavid van Moolenbroek 
237eda6f593SDavid van Moolenbroek 	/* Initialise the client socket and start the server. */
238eda6f593SDavid van Moolenbroek 	fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER);
239eda6f593SDavid van Moolenbroek 	if (fd == -1) {
240*0a6a1f1dSLionel Sambuc 		fprintf(stderr, "failed to connect to server: %s\n",
241*0a6a1f1dSLionel Sambuc 		    strerror(errno));
242eda6f593SDavid van Moolenbroek 		return (1);
243eda6f593SDavid van Moolenbroek 	}
244eda6f593SDavid van Moolenbroek 
245eda6f593SDavid van Moolenbroek 	/* Set process title, log and signals now this is the client. */
246eda6f593SDavid van Moolenbroek #ifdef HAVE_SETPROCTITLE
247eda6f593SDavid van Moolenbroek 	setproctitle("client (%s)", socket_path);
248eda6f593SDavid van Moolenbroek #endif
249eda6f593SDavid van Moolenbroek 	logfile("client");
250eda6f593SDavid van Moolenbroek 
251eda6f593SDavid van Moolenbroek 	/* Create imsg. */
252eda6f593SDavid van Moolenbroek 	imsg_init(&client_ibuf, fd);
253eda6f593SDavid van Moolenbroek 	event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
254eda6f593SDavid van Moolenbroek 
255*0a6a1f1dSLionel Sambuc 	/* Create stdin handler. */
256*0a6a1f1dSLionel Sambuc 	setblocking(STDIN_FILENO, 0);
257*0a6a1f1dSLionel Sambuc 	event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
258*0a6a1f1dSLionel Sambuc 	    client_stdin_callback, NULL);
259*0a6a1f1dSLionel Sambuc 	if (flags & CLIENT_CONTROLCONTROL) {
260*0a6a1f1dSLionel Sambuc 		if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
261*0a6a1f1dSLionel Sambuc 			fprintf(stderr, "tcgetattr failed: %s\n",
262*0a6a1f1dSLionel Sambuc 			    strerror(errno));
263*0a6a1f1dSLionel Sambuc 			return (1);
264*0a6a1f1dSLionel Sambuc 		}
265*0a6a1f1dSLionel Sambuc 		cfmakeraw(&tio);
266*0a6a1f1dSLionel Sambuc 		tio.c_iflag = ICRNL|IXANY;
267*0a6a1f1dSLionel Sambuc 		tio.c_oflag = OPOST|ONLCR;
268*0a6a1f1dSLionel Sambuc #ifdef NOKERNINFO
269*0a6a1f1dSLionel Sambuc 		tio.c_lflag = NOKERNINFO;
270*0a6a1f1dSLionel Sambuc #endif
271*0a6a1f1dSLionel Sambuc 		tio.c_cflag = CREAD|CS8|HUPCL;
272*0a6a1f1dSLionel Sambuc 		tio.c_cc[VMIN] = 1;
273*0a6a1f1dSLionel Sambuc 		tio.c_cc[VTIME] = 0;
274*0a6a1f1dSLionel Sambuc 		cfsetispeed(&tio, cfgetispeed(&saved_tio));
275*0a6a1f1dSLionel Sambuc 		cfsetospeed(&tio, cfgetospeed(&saved_tio));
276*0a6a1f1dSLionel Sambuc 		tcsetattr(STDIN_FILENO, TCSANOW, &tio);
277*0a6a1f1dSLionel Sambuc 	}
278*0a6a1f1dSLionel Sambuc 
279eda6f593SDavid van Moolenbroek 	/* Establish signal handlers. */
280eda6f593SDavid van Moolenbroek 	set_signals(client_signal);
281eda6f593SDavid van Moolenbroek 
282*0a6a1f1dSLionel Sambuc 	/* Send identify messages. */
283eda6f593SDavid van Moolenbroek 	client_send_identify(flags);
284eda6f593SDavid van Moolenbroek 
285eda6f593SDavid van Moolenbroek 	/* Send first command. */
286eda6f593SDavid van Moolenbroek 	if (msg == MSG_COMMAND) {
287*0a6a1f1dSLionel Sambuc 		/* How big is the command? */
288*0a6a1f1dSLionel Sambuc 		size = 0;
289*0a6a1f1dSLionel Sambuc 		for (i = 0; i < argc; i++)
290*0a6a1f1dSLionel Sambuc 			size += strlen(argv[i]) + 1;
291*0a6a1f1dSLionel Sambuc 		data = xmalloc((sizeof *data) + size);
292eda6f593SDavid van Moolenbroek 
293eda6f593SDavid van Moolenbroek 		/* Prepare command for server. */
294*0a6a1f1dSLionel Sambuc 		data->argc = argc;
295*0a6a1f1dSLionel Sambuc 		if (cmd_pack_argv(argc, argv, (char*)(data + 1), size) != 0) {
296*0a6a1f1dSLionel Sambuc 			fprintf(stderr, "command too long\n");
297*0a6a1f1dSLionel Sambuc 			free(data);
298eda6f593SDavid van Moolenbroek 			return (1);
299eda6f593SDavid van Moolenbroek 		}
300*0a6a1f1dSLionel Sambuc 		size += sizeof *data;
301eda6f593SDavid van Moolenbroek 
302*0a6a1f1dSLionel Sambuc 		/* Send the command. */
303*0a6a1f1dSLionel Sambuc 		if (client_write_server(msg, data, size) != 0) {
304*0a6a1f1dSLionel Sambuc 			fprintf(stderr, "failed to send command\n");
305*0a6a1f1dSLionel Sambuc 			free(data);
306*0a6a1f1dSLionel Sambuc 			return (1);
307*0a6a1f1dSLionel Sambuc 		}
308*0a6a1f1dSLionel Sambuc 		free(data);
309eda6f593SDavid van Moolenbroek 	} else if (msg == MSG_SHELL)
310eda6f593SDavid van Moolenbroek 		client_write_server(msg, NULL, 0);
311eda6f593SDavid van Moolenbroek 
312eda6f593SDavid van Moolenbroek 	/* Set the event and dispatch. */
313eda6f593SDavid van Moolenbroek 	client_update_event();
314eda6f593SDavid van Moolenbroek 	event_dispatch();
315eda6f593SDavid van Moolenbroek 
316eda6f593SDavid van Moolenbroek 	/* Print the exit message, if any, and exit. */
317eda6f593SDavid van Moolenbroek 	if (client_attached) {
318*0a6a1f1dSLionel Sambuc 		if (client_exitreason != CLIENT_EXIT_NONE && !login_shell)
319*0a6a1f1dSLionel Sambuc 			printf("[%s]\n", client_exit_message());
320eda6f593SDavid van Moolenbroek 
321eda6f593SDavid van Moolenbroek 		ppid = getppid();
322eda6f593SDavid van Moolenbroek 		if (client_exittype == MSG_DETACHKILL && ppid > 1)
323eda6f593SDavid van Moolenbroek 			kill(ppid, SIGHUP);
324*0a6a1f1dSLionel Sambuc 	} else if (flags & CLIENT_CONTROLCONTROL) {
325*0a6a1f1dSLionel Sambuc 		if (client_exitreason != CLIENT_EXIT_NONE)
326*0a6a1f1dSLionel Sambuc 			printf("%%exit %s\n", client_exit_message());
327*0a6a1f1dSLionel Sambuc 		else
328*0a6a1f1dSLionel Sambuc 			printf("%%exit\n");
329*0a6a1f1dSLionel Sambuc 		printf("\033\\");
330*0a6a1f1dSLionel Sambuc 		tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
331eda6f593SDavid van Moolenbroek 	}
332*0a6a1f1dSLionel Sambuc 	setblocking(STDIN_FILENO, 1);
333eda6f593SDavid van Moolenbroek 	return (client_exitval);
334eda6f593SDavid van Moolenbroek }
335eda6f593SDavid van Moolenbroek 
336*0a6a1f1dSLionel Sambuc /* Send identify messages to server. */
337eda6f593SDavid van Moolenbroek void
client_send_identify(int flags)338eda6f593SDavid van Moolenbroek client_send_identify(int flags)
339eda6f593SDavid van Moolenbroek {
340*0a6a1f1dSLionel Sambuc 	const char	*s;
341*0a6a1f1dSLionel Sambuc 	char		**ss;
342eda6f593SDavid van Moolenbroek 	int		 fd;
343eda6f593SDavid van Moolenbroek 
344*0a6a1f1dSLionel Sambuc 	client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
345eda6f593SDavid van Moolenbroek 
346*0a6a1f1dSLionel Sambuc 	if ((s = getenv("TERM")) == NULL)
347*0a6a1f1dSLionel Sambuc 		s = "";
348*0a6a1f1dSLionel Sambuc 	client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1);
349eda6f593SDavid van Moolenbroek 
350*0a6a1f1dSLionel Sambuc 	if ((s = ttyname(STDIN_FILENO)) == NULL)
351*0a6a1f1dSLionel Sambuc 		s = "";
352*0a6a1f1dSLionel Sambuc 	client_write_one(MSG_IDENTIFY_TTYNAME, -1, s, strlen(s) + 1);
353*0a6a1f1dSLionel Sambuc 
354*0a6a1f1dSLionel Sambuc 	if ((fd = open(".", O_RDONLY)) == -1)
355*0a6a1f1dSLionel Sambuc 		fd = open("/", O_RDONLY);
356*0a6a1f1dSLionel Sambuc 	client_write_one(MSG_IDENTIFY_CWD, fd, NULL, 0);
357eda6f593SDavid van Moolenbroek 
358eda6f593SDavid van Moolenbroek 	if ((fd = dup(STDIN_FILENO)) == -1)
359eda6f593SDavid van Moolenbroek 		fatal("dup failed");
360*0a6a1f1dSLionel Sambuc 	client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0);
361eda6f593SDavid van Moolenbroek 
362*0a6a1f1dSLionel Sambuc 	for (ss = environ; *ss != NULL; ss++)
363*0a6a1f1dSLionel Sambuc 		client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1);
364eda6f593SDavid van Moolenbroek 
365*0a6a1f1dSLionel Sambuc 	client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0);
366*0a6a1f1dSLionel Sambuc 
367*0a6a1f1dSLionel Sambuc 	client_update_event();
368eda6f593SDavid van Moolenbroek }
369eda6f593SDavid van Moolenbroek 
370*0a6a1f1dSLionel Sambuc /* Helper to send one message. */
371*0a6a1f1dSLionel Sambuc int
client_write_one(enum msgtype type,int fd,const void * buf,size_t len)372*0a6a1f1dSLionel Sambuc client_write_one(enum msgtype type, int fd, const void *buf, size_t len)
373eda6f593SDavid van Moolenbroek {
374*0a6a1f1dSLionel Sambuc 	int	retval;
375eda6f593SDavid van Moolenbroek 
376*0a6a1f1dSLionel Sambuc 	retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd,
377*0a6a1f1dSLionel Sambuc 	    __UNCONST(buf), len);
378*0a6a1f1dSLionel Sambuc 	if (retval != 1)
379*0a6a1f1dSLionel Sambuc 		return (-1);
380*0a6a1f1dSLionel Sambuc 	return (0);
381eda6f593SDavid van Moolenbroek }
382eda6f593SDavid van Moolenbroek 
383eda6f593SDavid van Moolenbroek /* Write a message to the server without a file descriptor. */
384*0a6a1f1dSLionel Sambuc int
client_write_server(enum msgtype type,const void * buf,size_t len)385*0a6a1f1dSLionel Sambuc client_write_server(enum msgtype type, const void *buf, size_t len)
386eda6f593SDavid van Moolenbroek {
387*0a6a1f1dSLionel Sambuc 	int	retval;
388*0a6a1f1dSLionel Sambuc 
389*0a6a1f1dSLionel Sambuc 	retval = client_write_one(type, -1, buf, len);
390*0a6a1f1dSLionel Sambuc 	if (retval == 0)
391*0a6a1f1dSLionel Sambuc 		client_update_event();
392*0a6a1f1dSLionel Sambuc 	return (retval);
393eda6f593SDavid van Moolenbroek }
394eda6f593SDavid van Moolenbroek 
395eda6f593SDavid van Moolenbroek /* Update client event based on whether it needs to read or read and write. */
396eda6f593SDavid van Moolenbroek void
client_update_event(void)397eda6f593SDavid van Moolenbroek client_update_event(void)
398eda6f593SDavid van Moolenbroek {
399eda6f593SDavid van Moolenbroek 	short	events;
400eda6f593SDavid van Moolenbroek 
401eda6f593SDavid van Moolenbroek 	event_del(&client_event);
402eda6f593SDavid van Moolenbroek 	events = EV_READ;
403eda6f593SDavid van Moolenbroek 	if (client_ibuf.w.queued > 0)
404eda6f593SDavid van Moolenbroek 		events |= EV_WRITE;
405eda6f593SDavid van Moolenbroek 	event_set(
406eda6f593SDavid van Moolenbroek 	    &client_event, client_ibuf.fd, events, client_callback, shell_cmd);
407eda6f593SDavid van Moolenbroek 	event_add(&client_event, NULL);
408eda6f593SDavid van Moolenbroek }
409eda6f593SDavid van Moolenbroek 
410eda6f593SDavid van Moolenbroek /* Callback to handle signals in the client. */
411eda6f593SDavid van Moolenbroek void
client_signal(int sig,unused short events,unused void * data)412eda6f593SDavid van Moolenbroek client_signal(int sig, unused short events, unused void *data)
413eda6f593SDavid van Moolenbroek {
414eda6f593SDavid van Moolenbroek 	struct sigaction sigact;
415eda6f593SDavid van Moolenbroek 	int		 status;
416eda6f593SDavid van Moolenbroek 
417eda6f593SDavid van Moolenbroek 	if (!client_attached) {
418eda6f593SDavid van Moolenbroek 		switch (sig) {
419eda6f593SDavid van Moolenbroek 		case SIGCHLD:
420eda6f593SDavid van Moolenbroek 			waitpid(WAIT_ANY, &status, WNOHANG);
421eda6f593SDavid van Moolenbroek 			break;
422eda6f593SDavid van Moolenbroek 		case SIGTERM:
423eda6f593SDavid van Moolenbroek 			event_loopexit(NULL);
424eda6f593SDavid van Moolenbroek 			break;
425eda6f593SDavid van Moolenbroek 		}
426eda6f593SDavid van Moolenbroek 	} else {
427eda6f593SDavid van Moolenbroek 		switch (sig) {
428eda6f593SDavid van Moolenbroek 		case SIGHUP:
429*0a6a1f1dSLionel Sambuc 			client_exitreason = CLIENT_EXIT_LOST_TTY;
430eda6f593SDavid van Moolenbroek 			client_exitval = 1;
431eda6f593SDavid van Moolenbroek 			client_write_server(MSG_EXITING, NULL, 0);
432eda6f593SDavid van Moolenbroek 			break;
433eda6f593SDavid van Moolenbroek 		case SIGTERM:
434*0a6a1f1dSLionel Sambuc 			client_exitreason = CLIENT_EXIT_TERMINATED;
435eda6f593SDavid van Moolenbroek 			client_exitval = 1;
436eda6f593SDavid van Moolenbroek 			client_write_server(MSG_EXITING, NULL, 0);
437eda6f593SDavid van Moolenbroek 			break;
438eda6f593SDavid van Moolenbroek 		case SIGWINCH:
439eda6f593SDavid van Moolenbroek 			client_write_server(MSG_RESIZE, NULL, 0);
440eda6f593SDavid van Moolenbroek 			break;
441eda6f593SDavid van Moolenbroek 		case SIGCONT:
442eda6f593SDavid van Moolenbroek 			memset(&sigact, 0, sizeof sigact);
443eda6f593SDavid van Moolenbroek 			sigemptyset(&sigact.sa_mask);
444eda6f593SDavid van Moolenbroek 			sigact.sa_flags = SA_RESTART;
445eda6f593SDavid van Moolenbroek 			sigact.sa_handler = SIG_IGN;
446eda6f593SDavid van Moolenbroek 			if (sigaction(SIGTSTP, &sigact, NULL) != 0)
447eda6f593SDavid van Moolenbroek 				fatal("sigaction failed");
448eda6f593SDavid van Moolenbroek 			client_write_server(MSG_WAKEUP, NULL, 0);
449eda6f593SDavid van Moolenbroek 			break;
450eda6f593SDavid van Moolenbroek 		}
451eda6f593SDavid van Moolenbroek 	}
452eda6f593SDavid van Moolenbroek 
453eda6f593SDavid van Moolenbroek 	client_update_event();
454eda6f593SDavid van Moolenbroek }
455eda6f593SDavid van Moolenbroek 
456eda6f593SDavid van Moolenbroek /* Callback for client imsg read events. */
457eda6f593SDavid van Moolenbroek void
client_callback(unused int fd,short events,void * data)458eda6f593SDavid van Moolenbroek client_callback(unused int fd, short events, void *data)
459eda6f593SDavid van Moolenbroek {
460eda6f593SDavid van Moolenbroek 	ssize_t	n;
461eda6f593SDavid van Moolenbroek 	int	retval;
462eda6f593SDavid van Moolenbroek 
463eda6f593SDavid van Moolenbroek 	if (events & EV_READ) {
464eda6f593SDavid van Moolenbroek 		if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
465eda6f593SDavid van Moolenbroek 			goto lost_server;
466eda6f593SDavid van Moolenbroek 		if (client_attached)
467eda6f593SDavid van Moolenbroek 			retval = client_dispatch_attached();
468eda6f593SDavid van Moolenbroek 		else
469eda6f593SDavid van Moolenbroek 			retval = client_dispatch_wait(data);
470eda6f593SDavid van Moolenbroek 		if (retval != 0) {
471eda6f593SDavid van Moolenbroek 			event_loopexit(NULL);
472eda6f593SDavid van Moolenbroek 			return;
473eda6f593SDavid van Moolenbroek 		}
474eda6f593SDavid van Moolenbroek 	}
475eda6f593SDavid van Moolenbroek 
476eda6f593SDavid van Moolenbroek 	if (events & EV_WRITE) {
477*0a6a1f1dSLionel Sambuc 		if (msgbuf_write(&client_ibuf.w) < 0 && errno != EAGAIN)
478eda6f593SDavid van Moolenbroek 			goto lost_server;
479eda6f593SDavid van Moolenbroek 	}
480eda6f593SDavid van Moolenbroek 
481eda6f593SDavid van Moolenbroek 	client_update_event();
482eda6f593SDavid van Moolenbroek 	return;
483eda6f593SDavid van Moolenbroek 
484eda6f593SDavid van Moolenbroek lost_server:
485*0a6a1f1dSLionel Sambuc 	client_exitreason = CLIENT_EXIT_LOST_SERVER;
486eda6f593SDavid van Moolenbroek 	client_exitval = 1;
487eda6f593SDavid van Moolenbroek 	event_loopexit(NULL);
488eda6f593SDavid van Moolenbroek }
489eda6f593SDavid van Moolenbroek 
490*0a6a1f1dSLionel Sambuc /* Callback for client stdin read events. */
491*0a6a1f1dSLionel Sambuc void
client_stdin_callback(unused int fd,unused short events,unused void * data1)492*0a6a1f1dSLionel Sambuc client_stdin_callback(unused int fd, unused short events, unused void *data1)
493*0a6a1f1dSLionel Sambuc {
494*0a6a1f1dSLionel Sambuc 	struct msg_stdin_data	data;
495*0a6a1f1dSLionel Sambuc 
496*0a6a1f1dSLionel Sambuc 	data.size = read(STDIN_FILENO, data.data, sizeof data.data);
497*0a6a1f1dSLionel Sambuc 	if (data.size < 0 && (errno == EINTR || errno == EAGAIN))
498*0a6a1f1dSLionel Sambuc 		return;
499*0a6a1f1dSLionel Sambuc 
500*0a6a1f1dSLionel Sambuc 	client_write_server(MSG_STDIN, &data, sizeof data);
501*0a6a1f1dSLionel Sambuc 	if (data.size <= 0)
502*0a6a1f1dSLionel Sambuc 		event_del(&client_stdin);
503*0a6a1f1dSLionel Sambuc 	client_update_event();
504*0a6a1f1dSLionel Sambuc }
505*0a6a1f1dSLionel Sambuc 
506*0a6a1f1dSLionel Sambuc /* Force write to file descriptor. */
507*0a6a1f1dSLionel Sambuc void
client_write(int fd,const char * data,size_t size)508*0a6a1f1dSLionel Sambuc client_write(int fd, const char *data, size_t size)
509*0a6a1f1dSLionel Sambuc {
510*0a6a1f1dSLionel Sambuc 	ssize_t	used;
511*0a6a1f1dSLionel Sambuc 
512*0a6a1f1dSLionel Sambuc 	while (size != 0) {
513*0a6a1f1dSLionel Sambuc 		used = write(fd, data, size);
514*0a6a1f1dSLionel Sambuc 		if (used == -1) {
515*0a6a1f1dSLionel Sambuc 			if (errno == EINTR || errno == EAGAIN)
516*0a6a1f1dSLionel Sambuc 				continue;
517*0a6a1f1dSLionel Sambuc 			break;
518*0a6a1f1dSLionel Sambuc 		}
519*0a6a1f1dSLionel Sambuc 		data += used;
520*0a6a1f1dSLionel Sambuc 		size -= used;
521*0a6a1f1dSLionel Sambuc 	}
522*0a6a1f1dSLionel Sambuc }
523*0a6a1f1dSLionel Sambuc 
524eda6f593SDavid van Moolenbroek /* Dispatch imsgs when in wait state (before MSG_READY). */
525eda6f593SDavid van Moolenbroek int
client_dispatch_wait(void * data0)526*0a6a1f1dSLionel Sambuc client_dispatch_wait(void *data0)
527eda6f593SDavid van Moolenbroek {
528eda6f593SDavid van Moolenbroek 	struct imsg		 imsg;
529*0a6a1f1dSLionel Sambuc 	char			*data;
530eda6f593SDavid van Moolenbroek 	ssize_t			 n, datalen;
531*0a6a1f1dSLionel Sambuc 	struct msg_stdout_data	 stdoutdata;
532*0a6a1f1dSLionel Sambuc 	struct msg_stderr_data	 stderrdata;
533*0a6a1f1dSLionel Sambuc 	int			 retval;
534eda6f593SDavid van Moolenbroek 
535eda6f593SDavid van Moolenbroek 	for (;;) {
536eda6f593SDavid van Moolenbroek 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
537eda6f593SDavid van Moolenbroek 			fatalx("imsg_get failed");
538eda6f593SDavid van Moolenbroek 		if (n == 0)
539eda6f593SDavid van Moolenbroek 			return (0);
540*0a6a1f1dSLionel Sambuc 
541*0a6a1f1dSLionel Sambuc 		data = imsg.data;
542eda6f593SDavid van Moolenbroek 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
543eda6f593SDavid van Moolenbroek 
544*0a6a1f1dSLionel Sambuc 		log_debug("got %d from server", imsg.hdr.type);
545eda6f593SDavid van Moolenbroek 		switch (imsg.hdr.type) {
546eda6f593SDavid van Moolenbroek 		case MSG_EXIT:
547eda6f593SDavid van Moolenbroek 		case MSG_SHUTDOWN:
548*0a6a1f1dSLionel Sambuc 			if (datalen != sizeof retval && datalen != 0)
549eda6f593SDavid van Moolenbroek 				fatalx("bad MSG_EXIT size");
550*0a6a1f1dSLionel Sambuc 			if (datalen == sizeof retval) {
551*0a6a1f1dSLionel Sambuc 				memcpy(&retval, data, sizeof retval);
552*0a6a1f1dSLionel Sambuc 				client_exitval = retval;
553eda6f593SDavid van Moolenbroek 			}
554eda6f593SDavid van Moolenbroek 			imsg_free(&imsg);
555eda6f593SDavid van Moolenbroek 			return (-1);
556eda6f593SDavid van Moolenbroek 		case MSG_READY:
557eda6f593SDavid van Moolenbroek 			if (datalen != 0)
558eda6f593SDavid van Moolenbroek 				fatalx("bad MSG_READY size");
559eda6f593SDavid van Moolenbroek 
560*0a6a1f1dSLionel Sambuc 			event_del(&client_stdin);
561eda6f593SDavid van Moolenbroek 			client_attached = 1;
562*0a6a1f1dSLionel Sambuc 			client_write_server(MSG_RESIZE, NULL, 0);
563*0a6a1f1dSLionel Sambuc 			break;
564*0a6a1f1dSLionel Sambuc 		case MSG_STDIN:
565*0a6a1f1dSLionel Sambuc 			if (datalen != 0)
566*0a6a1f1dSLionel Sambuc 				fatalx("bad MSG_STDIN size");
567*0a6a1f1dSLionel Sambuc 
568*0a6a1f1dSLionel Sambuc 			event_add(&client_stdin, NULL);
569*0a6a1f1dSLionel Sambuc 			break;
570*0a6a1f1dSLionel Sambuc 		case MSG_STDOUT:
571*0a6a1f1dSLionel Sambuc 			if (datalen != sizeof stdoutdata)
572*0a6a1f1dSLionel Sambuc 				fatalx("bad MSG_STDOUT size");
573*0a6a1f1dSLionel Sambuc 			memcpy(&stdoutdata, data, sizeof stdoutdata);
574*0a6a1f1dSLionel Sambuc 
575*0a6a1f1dSLionel Sambuc 			client_write(STDOUT_FILENO, stdoutdata.data,
576*0a6a1f1dSLionel Sambuc 			    stdoutdata.size);
577*0a6a1f1dSLionel Sambuc 			break;
578*0a6a1f1dSLionel Sambuc 		case MSG_STDERR:
579*0a6a1f1dSLionel Sambuc 			if (datalen != sizeof stderrdata)
580*0a6a1f1dSLionel Sambuc 				fatalx("bad MSG_STDERR size");
581*0a6a1f1dSLionel Sambuc 			memcpy(&stderrdata, data, sizeof stderrdata);
582*0a6a1f1dSLionel Sambuc 
583*0a6a1f1dSLionel Sambuc 			client_write(STDERR_FILENO, stderrdata.data,
584*0a6a1f1dSLionel Sambuc 			    stderrdata.size);
585eda6f593SDavid van Moolenbroek 			break;
586eda6f593SDavid van Moolenbroek 		case MSG_VERSION:
587eda6f593SDavid van Moolenbroek 			if (datalen != 0)
588eda6f593SDavid van Moolenbroek 				fatalx("bad MSG_VERSION size");
589eda6f593SDavid van Moolenbroek 
590*0a6a1f1dSLionel Sambuc 			fprintf(stderr, "protocol version mismatch "
591*0a6a1f1dSLionel Sambuc 			    "(client %u, server %u)\n", PROTOCOL_VERSION,
592*0a6a1f1dSLionel Sambuc 			    imsg.hdr.peerid);
593eda6f593SDavid van Moolenbroek 			client_exitval = 1;
594eda6f593SDavid van Moolenbroek 
595eda6f593SDavid van Moolenbroek 			imsg_free(&imsg);
596eda6f593SDavid van Moolenbroek 			return (-1);
597eda6f593SDavid van Moolenbroek 		case MSG_SHELL:
598*0a6a1f1dSLionel Sambuc 			if (datalen == 0 || data[datalen - 1] != '\0')
599*0a6a1f1dSLionel Sambuc 				fatalx("bad MSG_SHELL string");
600eda6f593SDavid van Moolenbroek 
601eda6f593SDavid van Moolenbroek 			clear_signals(0);
602*0a6a1f1dSLionel Sambuc 			shell_exec(data, data0);
603eda6f593SDavid van Moolenbroek 			/* NOTREACHED */
604*0a6a1f1dSLionel Sambuc 		case MSG_DETACH:
605*0a6a1f1dSLionel Sambuc 		case MSG_DETACHKILL:
606*0a6a1f1dSLionel Sambuc 			client_write_server(MSG_EXITING, NULL, 0);
607*0a6a1f1dSLionel Sambuc 			break;
608*0a6a1f1dSLionel Sambuc 		case MSG_EXITED:
609*0a6a1f1dSLionel Sambuc 			imsg_free(&imsg);
610*0a6a1f1dSLionel Sambuc 			return (-1);
611eda6f593SDavid van Moolenbroek 		}
612eda6f593SDavid van Moolenbroek 
613eda6f593SDavid van Moolenbroek 		imsg_free(&imsg);
614eda6f593SDavid van Moolenbroek 	}
615eda6f593SDavid van Moolenbroek }
616eda6f593SDavid van Moolenbroek 
617eda6f593SDavid van Moolenbroek /* Dispatch imsgs in attached state (after MSG_READY). */
618eda6f593SDavid van Moolenbroek int
client_dispatch_attached(void)619eda6f593SDavid van Moolenbroek client_dispatch_attached(void)
620eda6f593SDavid van Moolenbroek {
621eda6f593SDavid van Moolenbroek 	struct imsg		 imsg;
622eda6f593SDavid van Moolenbroek 	struct sigaction	 sigact;
623*0a6a1f1dSLionel Sambuc 	char			*data;
624eda6f593SDavid van Moolenbroek 	ssize_t			 n, datalen;
625eda6f593SDavid van Moolenbroek 
626eda6f593SDavid van Moolenbroek 	for (;;) {
627eda6f593SDavid van Moolenbroek 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
628eda6f593SDavid van Moolenbroek 			fatalx("imsg_get failed");
629eda6f593SDavid van Moolenbroek 		if (n == 0)
630eda6f593SDavid van Moolenbroek 			return (0);
631*0a6a1f1dSLionel Sambuc 
632*0a6a1f1dSLionel Sambuc 		data = imsg.data;
633eda6f593SDavid van Moolenbroek 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
634eda6f593SDavid van Moolenbroek 
635*0a6a1f1dSLionel Sambuc 		log_debug("got %d from server", imsg.hdr.type);
636eda6f593SDavid van Moolenbroek 		switch (imsg.hdr.type) {
637eda6f593SDavid van Moolenbroek 		case MSG_DETACH:
638*0a6a1f1dSLionel Sambuc 		case MSG_DETACHKILL:
639*0a6a1f1dSLionel Sambuc 			if (datalen == 0 || data[datalen - 1] != '\0')
640*0a6a1f1dSLionel Sambuc 				fatalx("bad MSG_DETACH string");
641eda6f593SDavid van Moolenbroek 
642*0a6a1f1dSLionel Sambuc 			client_exitsession = xstrdup(data);
643eda6f593SDavid van Moolenbroek 			client_exittype = imsg.hdr.type;
644eda6f593SDavid van Moolenbroek 			if (imsg.hdr.type == MSG_DETACHKILL)
645*0a6a1f1dSLionel Sambuc 				client_exitreason = CLIENT_EXIT_DETACHED_HUP;
646eda6f593SDavid van Moolenbroek 			else
647*0a6a1f1dSLionel Sambuc 				client_exitreason = CLIENT_EXIT_DETACHED;
648eda6f593SDavid van Moolenbroek 			client_write_server(MSG_EXITING, NULL, 0);
649eda6f593SDavid van Moolenbroek 			break;
650eda6f593SDavid van Moolenbroek 		case MSG_EXIT:
651*0a6a1f1dSLionel Sambuc 			if (datalen != 0 && datalen != sizeof (int))
652eda6f593SDavid van Moolenbroek 				fatalx("bad MSG_EXIT size");
653eda6f593SDavid van Moolenbroek 
654eda6f593SDavid van Moolenbroek 			client_write_server(MSG_EXITING, NULL, 0);
655*0a6a1f1dSLionel Sambuc 			client_exitreason = CLIENT_EXIT_EXITED;
656eda6f593SDavid van Moolenbroek 			break;
657eda6f593SDavid van Moolenbroek 		case MSG_EXITED:
658eda6f593SDavid van Moolenbroek 			if (datalen != 0)
659eda6f593SDavid van Moolenbroek 				fatalx("bad MSG_EXITED size");
660eda6f593SDavid van Moolenbroek 
661eda6f593SDavid van Moolenbroek 			imsg_free(&imsg);
662eda6f593SDavid van Moolenbroek 			return (-1);
663eda6f593SDavid van Moolenbroek 		case MSG_SHUTDOWN:
664eda6f593SDavid van Moolenbroek 			if (datalen != 0)
665eda6f593SDavid van Moolenbroek 				fatalx("bad MSG_SHUTDOWN size");
666eda6f593SDavid van Moolenbroek 
667eda6f593SDavid van Moolenbroek 			client_write_server(MSG_EXITING, NULL, 0);
668*0a6a1f1dSLionel Sambuc 			client_exitreason = CLIENT_EXIT_SERVER_EXITED;
669eda6f593SDavid van Moolenbroek 			client_exitval = 1;
670eda6f593SDavid van Moolenbroek 			break;
671eda6f593SDavid van Moolenbroek 		case MSG_SUSPEND:
672eda6f593SDavid van Moolenbroek 			if (datalen != 0)
673eda6f593SDavid van Moolenbroek 				fatalx("bad MSG_SUSPEND size");
674eda6f593SDavid van Moolenbroek 
675eda6f593SDavid van Moolenbroek 			memset(&sigact, 0, sizeof sigact);
676eda6f593SDavid van Moolenbroek 			sigemptyset(&sigact.sa_mask);
677eda6f593SDavid van Moolenbroek 			sigact.sa_flags = SA_RESTART;
678eda6f593SDavid van Moolenbroek 			sigact.sa_handler = SIG_DFL;
679eda6f593SDavid van Moolenbroek 			if (sigaction(SIGTSTP, &sigact, NULL) != 0)
680eda6f593SDavid van Moolenbroek 				fatal("sigaction failed");
681eda6f593SDavid van Moolenbroek 			kill(getpid(), SIGTSTP);
682eda6f593SDavid van Moolenbroek 			break;
683eda6f593SDavid van Moolenbroek 		case MSG_LOCK:
684*0a6a1f1dSLionel Sambuc 			if (datalen == 0 || data[datalen - 1] != '\0')
685*0a6a1f1dSLionel Sambuc 				fatalx("bad MSG_LOCK string");
686eda6f593SDavid van Moolenbroek 
687*0a6a1f1dSLionel Sambuc 			system(data);
688eda6f593SDavid van Moolenbroek 			client_write_server(MSG_UNLOCK, NULL, 0);
689eda6f593SDavid van Moolenbroek 			break;
690eda6f593SDavid van Moolenbroek 		}
691eda6f593SDavid van Moolenbroek 
692eda6f593SDavid van Moolenbroek 		imsg_free(&imsg);
693eda6f593SDavid van Moolenbroek 	}
694eda6f593SDavid van Moolenbroek }
695