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