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