GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/client.c Lines: 0 326 0.0 %
Date: 2016-12-06 Branches: 0 204 0.0 %

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