1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007 Nicholas Marriott <[email protected]> |
5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. |
9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ |
18 | |
19 | #include <sys/types.h> |
20 | #include <sys/socket.h> |
21 | #include <sys/un.h> |
22 | #include <sys/wait.h> |
23 | #include <sys/file.h> |
24 | |
25 | #include <errno.h> |
26 | #include <event.h> |
27 | #include <fcntl.h> |
28 | #include <signal.h> |
29 | #include <stdlib.h> |
30 | #include <string.h> |
31 | #include <unistd.h> |
32 | |
33 | #include "tmux.h" |
34 | |
35 | static struct tmuxproc *client_proc; |
36 | static struct tmuxpeer *client_peer; |
37 | static int client_flags; |
38 | static enum { |
39 | CLIENT_EXIT_NONE, |
40 | CLIENT_EXIT_DETACHED, |
41 | CLIENT_EXIT_DETACHED_HUP, |
42 | CLIENT_EXIT_LOST_TTY, |
43 | CLIENT_EXIT_TERMINATED, |
44 | CLIENT_EXIT_LOST_SERVER, |
45 | CLIENT_EXIT_EXITED, |
46 | CLIENT_EXIT_SERVER_EXITED, |
47 | } client_exitreason = CLIENT_EXIT_NONE; |
48 | static int client_exitflag; |
49 | static int client_exitval; |
50 | static enum msgtype client_exittype; |
51 | static const char *client_exitsession; |
52 | static const char *client_execshell; |
53 | static const char *client_execcmd; |
54 | static int client_attached; |
55 | static struct client_files client_files = RB_INITIALIZER(&client_files); |
56 | |
57 | static __dead void client_exec(const char *,const char *); |
58 | static int client_get_lock(char *); |
59 | static int client_connect(struct event_base *, const char *, int); |
60 | static void client_send_identify(const char *, const char *); |
61 | static void client_signal(int); |
62 | static void client_dispatch(struct imsg *, void *); |
63 | static void client_dispatch_attached(struct imsg *); |
64 | static void client_dispatch_wait(struct imsg *); |
65 | static const char *client_exit_message(void); |
66 | |
67 | /* |
68 | * Get server create lock. If already held then server start is happening in |
69 | * another client, so block until the lock is released and return -2 to |
70 | * retry. Return -1 on failure to continue and start the server anyway. |
71 | */ |
72 | static int |
73 | client_get_lock(char *lockfile) |
74 | { |
75 | int lockfd; |
76 | |
77 | log_debug("lock file is %s" , lockfile); |
78 | |
79 | if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { |
80 | log_debug("open failed: %s" , strerror(errno)); |
81 | return (-1); |
82 | } |
83 | |
84 | if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { |
85 | log_debug("flock failed: %s" , strerror(errno)); |
86 | if (errno != EAGAIN) |
87 | return (lockfd); |
88 | while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) |
89 | /* nothing */; |
90 | close(lockfd); |
91 | return (-2); |
92 | } |
93 | log_debug("flock succeeded" ); |
94 | |
95 | return (lockfd); |
96 | } |
97 | |
98 | /* Connect client to server. */ |
99 | static int |
100 | client_connect(struct event_base *base, const char *path, int start_server) |
101 | { |
102 | struct sockaddr_un sa; |
103 | size_t size; |
104 | int fd, lockfd = -1, locked = 0; |
105 | char *lockfile = NULL; |
106 | |
107 | memset(&sa, 0, sizeof sa); |
108 | sa.sun_family = AF_UNIX; |
109 | size = strlcpy(sa.sun_path, path, sizeof sa.sun_path); |
110 | if (size >= sizeof sa.sun_path) { |
111 | errno = ENAMETOOLONG; |
112 | return (-1); |
113 | } |
114 | log_debug("socket is %s" , path); |
115 | |
116 | retry: |
117 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) |
118 | return (-1); |
119 | |
120 | log_debug("trying connect" ); |
121 | if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { |
122 | log_debug("connect failed: %s" , strerror(errno)); |
123 | if (errno != ECONNREFUSED && errno != ENOENT) |
124 | goto failed; |
125 | if (!start_server) |
126 | goto failed; |
127 | close(fd); |
128 | |
129 | if (!locked) { |
130 | xasprintf(&lockfile, "%s.lock" , path); |
131 | if ((lockfd = client_get_lock(lockfile)) < 0) { |
132 | log_debug("didn't get lock (%d)" , lockfd); |
133 | |
134 | free(lockfile); |
135 | lockfile = NULL; |
136 | |
137 | if (lockfd == -2) |
138 | goto retry; |
139 | } |
140 | log_debug("got lock (%d)" , lockfd); |
141 | |
142 | /* |
143 | * Always retry at least once, even if we got the lock, |
144 | * because another client could have taken the lock, |
145 | * started the server and released the lock between our |
146 | * connect() and flock(). |
147 | */ |
148 | locked = 1; |
149 | goto retry; |
150 | } |
151 | |
152 | if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { |
153 | free(lockfile); |
154 | close(lockfd); |
155 | return (-1); |
156 | } |
157 | fd = server_start(client_proc, base, lockfd, lockfile); |
158 | } |
159 | |
160 | if (locked && lockfd >= 0) { |
161 | free(lockfile); |
162 | close(lockfd); |
163 | } |
164 | setblocking(fd, 0); |
165 | return (fd); |
166 | |
167 | failed: |
168 | if (locked) { |
169 | free(lockfile); |
170 | close(lockfd); |
171 | } |
172 | close(fd); |
173 | return (-1); |
174 | } |
175 | |
176 | /* Get exit string from reason number. */ |
177 | const char * |
178 | client_exit_message(void) |
179 | { |
180 | static char msg[256]; |
181 | |
182 | switch (client_exitreason) { |
183 | case CLIENT_EXIT_NONE: |
184 | break; |
185 | case CLIENT_EXIT_DETACHED: |
186 | if (client_exitsession != NULL) { |
187 | xsnprintf(msg, sizeof msg, "detached " |
188 | "(from session %s)" , client_exitsession); |
189 | return (msg); |
190 | } |
191 | return ("detached" ); |
192 | case CLIENT_EXIT_DETACHED_HUP: |
193 | if (client_exitsession != NULL) { |
194 | xsnprintf(msg, sizeof msg, "detached and SIGHUP " |
195 | "(from session %s)" , client_exitsession); |
196 | return (msg); |
197 | } |
198 | return ("detached and SIGHUP" ); |
199 | case CLIENT_EXIT_LOST_TTY: |
200 | return ("lost tty" ); |
201 | case CLIENT_EXIT_TERMINATED: |
202 | return ("terminated" ); |
203 | case CLIENT_EXIT_LOST_SERVER: |
204 | return ("server exited unexpectedly" ); |
205 | case CLIENT_EXIT_EXITED: |
206 | return ("exited" ); |
207 | case CLIENT_EXIT_SERVER_EXITED: |
208 | return ("server exited" ); |
209 | } |
210 | return ("unknown reason" ); |
211 | } |
212 | |
213 | /* Exit if all streams flushed. */ |
214 | static void |
215 | client_exit(void) |
216 | { |
217 | struct client_file *cf; |
218 | size_t left; |
219 | int waiting = 0; |
220 | |
221 | RB_FOREACH (cf, client_files, &client_files) { |
222 | if (cf->event == NULL) |
223 | continue; |
224 | left = EVBUFFER_LENGTH(cf->event->output); |
225 | if (left != 0) { |
226 | waiting++; |
227 | log_debug("file %u %zu bytes left" , cf->stream, left); |
228 | } |
229 | } |
230 | if (waiting == 0) |
231 | proc_exit(client_proc); |
232 | } |
233 | |
234 | /* Client main loop. */ |
235 | int |
236 | client_main(struct event_base *base, int argc, char **argv, int flags) |
237 | { |
238 | struct cmd_parse_result *pr; |
239 | struct cmd *cmd; |
240 | struct msg_command *data; |
241 | int cmdflags, fd, i; |
242 | const char *ttynam, *cwd; |
243 | pid_t ppid; |
244 | enum msgtype msg; |
245 | struct termios tio, saved_tio; |
246 | size_t size; |
247 | |
248 | /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ |
249 | signal(SIGCHLD, SIG_IGN); |
250 | |
251 | /* Save the flags. */ |
252 | client_flags = flags; |
253 | |
254 | /* Set up the initial command. */ |
255 | cmdflags = 0; |
256 | if (shell_command != NULL) { |
257 | msg = MSG_SHELL; |
258 | cmdflags = CMD_STARTSERVER; |
259 | } else if (argc == 0) { |
260 | msg = MSG_COMMAND; |
261 | cmdflags = CMD_STARTSERVER; |
262 | } else { |
263 | msg = MSG_COMMAND; |
264 | |
265 | /* |
266 | * It sucks parsing the command string twice (in client and |
267 | * later in server) but it is necessary to get the start server |
268 | * flag. |
269 | */ |
270 | pr = cmd_parse_from_arguments(argc, argv, NULL); |
271 | if (pr->status == CMD_PARSE_SUCCESS) { |
272 | TAILQ_FOREACH(cmd, &pr->cmdlist->list, qentry) { |
273 | if (cmd->entry->flags & CMD_STARTSERVER) |
274 | cmdflags |= CMD_STARTSERVER; |
275 | } |
276 | cmd_list_free(pr->cmdlist); |
277 | } else |
278 | free(pr->error); |
279 | } |
280 | |
281 | /* Create client process structure (starts logging). */ |
282 | client_proc = proc_start("client" ); |
283 | proc_set_signals(client_proc, client_signal); |
284 | |
285 | /* Initialize the client socket and start the server. */ |
286 | fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); |
287 | if (fd == -1) { |
288 | if (errno == ECONNREFUSED) { |
289 | fprintf(stderr, "no server running on %s\n" , |
290 | socket_path); |
291 | } else { |
292 | fprintf(stderr, "error connecting to %s (%s)\n" , |
293 | socket_path, strerror(errno)); |
294 | } |
295 | return (1); |
296 | } |
297 | client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); |
298 | |
299 | /* Save these before pledge(). */ |
300 | if ((cwd = find_cwd()) == NULL && (cwd = find_home()) == NULL) |
301 | cwd = "/" ; |
302 | if ((ttynam = ttyname(STDIN_FILENO)) == NULL) |
303 | ttynam = "" ; |
304 | |
305 | /* |
306 | * Drop privileges for client. "proc exec" is needed for -c and for |
307 | * locking (which uses system(3)). |
308 | * |
309 | * "tty" is needed to restore termios(4) and also for some reason -CC |
310 | * does not work properly without it (input is not recognised). |
311 | * |
312 | * "sendfd" is dropped later in client_dispatch_wait(). |
313 | */ |
314 | if (pledge( |
315 | "stdio rpath wpath cpath unix sendfd proc exec tty" , |
316 | NULL) != 0) |
317 | fatal("pledge failed" ); |
318 | |
319 | /* Free stuff that is not used in the client. */ |
320 | if (ptm_fd != -1) |
321 | close(ptm_fd); |
322 | options_free(global_options); |
323 | options_free(global_s_options); |
324 | options_free(global_w_options); |
325 | environ_free(global_environ); |
326 | |
327 | /* Set up control mode. */ |
328 | if (client_flags & CLIENT_CONTROLCONTROL) { |
329 | if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { |
330 | fprintf(stderr, "tcgetattr failed: %s\n" , |
331 | strerror(errno)); |
332 | return (1); |
333 | } |
334 | cfmakeraw(&tio); |
335 | tio.c_iflag = ICRNL|IXANY; |
336 | tio.c_oflag = OPOST|ONLCR; |
337 | #ifdef NOKERNINFO |
338 | tio.c_lflag = NOKERNINFO; |
339 | #endif |
340 | tio.c_cflag = CREAD|CS8|HUPCL; |
341 | tio.c_cc[VMIN] = 1; |
342 | tio.c_cc[VTIME] = 0; |
343 | cfsetispeed(&tio, cfgetispeed(&saved_tio)); |
344 | cfsetospeed(&tio, cfgetospeed(&saved_tio)); |
345 | tcsetattr(STDIN_FILENO, TCSANOW, &tio); |
346 | } |
347 | |
348 | /* Send identify messages. */ |
349 | client_send_identify(ttynam, cwd); |
350 | |
351 | /* Send first command. */ |
352 | if (msg == MSG_COMMAND) { |
353 | /* How big is the command? */ |
354 | size = 0; |
355 | for (i = 0; i < argc; i++) |
356 | size += strlen(argv[i]) + 1; |
357 | if (size > MAX_IMSGSIZE - (sizeof *data)) { |
358 | fprintf(stderr, "command too long\n" ); |
359 | return (1); |
360 | } |
361 | data = xmalloc((sizeof *data) + size); |
362 | |
363 | /* Prepare command for server. */ |
364 | data->argc = argc; |
365 | if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) { |
366 | fprintf(stderr, "command too long\n" ); |
367 | free(data); |
368 | return (1); |
369 | } |
370 | size += sizeof *data; |
371 | |
372 | /* Send the command. */ |
373 | if (proc_send(client_peer, msg, -1, data, size) != 0) { |
374 | fprintf(stderr, "failed to send command\n" ); |
375 | free(data); |
376 | return (1); |
377 | } |
378 | free(data); |
379 | } else if (msg == MSG_SHELL) |
380 | proc_send(client_peer, msg, -1, NULL, 0); |
381 | |
382 | /* Start main loop. */ |
383 | proc_loop(client_proc, NULL); |
384 | |
385 | /* Run command if user requested exec, instead of exiting. */ |
386 | if (client_exittype == MSG_EXEC) { |
387 | if (client_flags & CLIENT_CONTROLCONTROL) |
388 | tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); |
389 | client_exec(client_execshell, client_execcmd); |
390 | } |
391 | |
392 | /* Print the exit message, if any, and exit. */ |
393 | if (client_attached) { |
394 | if (client_exitreason != CLIENT_EXIT_NONE) |
395 | printf("[%s]\n" , client_exit_message()); |
396 | |
397 | ppid = getppid(); |
398 | if (client_exittype == MSG_DETACHKILL && ppid > 1) |
399 | kill(ppid, SIGHUP); |
400 | } else if (client_flags & CLIENT_CONTROLCONTROL) { |
401 | if (client_exitreason != CLIENT_EXIT_NONE) |
402 | printf("%%exit %s\n" , client_exit_message()); |
403 | else |
404 | printf("%%exit\n" ); |
405 | printf("\033\\" ); |
406 | tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); |
407 | } else if (client_exitreason != CLIENT_EXIT_NONE) |
408 | fprintf(stderr, "%s\n" , client_exit_message()); |
409 | setblocking(STDIN_FILENO, 1); |
410 | return (client_exitval); |
411 | } |
412 | |
413 | /* Send identify messages to server. */ |
414 | static void |
415 | client_send_identify(const char *ttynam, const char *cwd) |
416 | { |
417 | const char *s; |
418 | char **ss; |
419 | size_t sslen; |
420 | int fd, flags = client_flags; |
421 | pid_t pid; |
422 | |
423 | proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); |
424 | |
425 | if ((s = getenv("TERM" )) == NULL) |
426 | s = "" ; |
427 | proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); |
428 | |
429 | proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, |
430 | strlen(ttynam) + 1); |
431 | proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); |
432 | |
433 | if ((fd = dup(STDIN_FILENO)) == -1) |
434 | fatal("dup failed" ); |
435 | proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); |
436 | |
437 | pid = getpid(); |
438 | proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); |
439 | |
440 | for (ss = environ; *ss != NULL; ss++) { |
441 | sslen = strlen(*ss) + 1; |
442 | if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) |
443 | continue; |
444 | proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); |
445 | } |
446 | |
447 | proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); |
448 | } |
449 | |
450 | /* File write error callback. */ |
451 | static void |
452 | client_write_error_callback(__unused struct bufferevent *bev, |
453 | __unused short what, void *arg) |
454 | { |
455 | struct client_file *cf = arg; |
456 | |
457 | log_debug("write error file %d" , cf->stream); |
458 | |
459 | bufferevent_free(cf->event); |
460 | cf->event = NULL; |
461 | |
462 | close(cf->fd); |
463 | cf->fd = -1; |
464 | |
465 | if (client_exitflag) |
466 | client_exit(); |
467 | } |
468 | |
469 | /* File write callback. */ |
470 | static void |
471 | client_write_callback(__unused struct bufferevent *bev, void *arg) |
472 | { |
473 | struct client_file *cf = arg; |
474 | |
475 | if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { |
476 | bufferevent_free(cf->event); |
477 | close(cf->fd); |
478 | RB_REMOVE(client_files, &client_files, cf); |
479 | file_free(cf); |
480 | } |
481 | |
482 | if (client_exitflag) |
483 | client_exit(); |
484 | } |
485 | |
486 | /* Open write file. */ |
487 | static void |
488 | client_write_open(void *data, size_t datalen) |
489 | { |
490 | struct msg_write_open *msg = data; |
491 | const char *path; |
492 | struct msg_write_ready reply; |
493 | struct client_file find, *cf; |
494 | const int flags = O_NONBLOCK|O_WRONLY|O_CREAT; |
495 | int error = 0; |
496 | |
497 | if (datalen < sizeof *msg) |
498 | fatalx("bad MSG_WRITE_OPEN size" ); |
499 | if (datalen == sizeof *msg) |
500 | path = "-" ; |
501 | else |
502 | path = (const char *)(msg + 1); |
503 | log_debug("open write file %d %s" , msg->stream, path); |
504 | |
505 | find.stream = msg->stream; |
506 | if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { |
507 | cf = file_create(NULL, msg->stream, NULL, NULL); |
508 | RB_INSERT(client_files, &client_files, cf); |
509 | } else { |
510 | error = EBADF; |
511 | goto reply; |
512 | } |
513 | if (cf->closed) { |
514 | error = EBADF; |
515 | goto reply; |
516 | } |
517 | |
518 | cf->fd = -1; |
519 | if (msg->fd == -1) |
520 | cf->fd = open(path, msg->flags|flags, 0644); |
521 | else { |
522 | if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) |
523 | errno = EBADF; |
524 | else { |
525 | cf->fd = dup(msg->fd); |
526 | if (client_flags & CLIENT_CONTROL) |
527 | close(msg->fd); /* can only be used once */ |
528 | } |
529 | } |
530 | if (cf->fd == -1) { |
531 | error = errno; |
532 | goto reply; |
533 | } |
534 | |
535 | cf->event = bufferevent_new(cf->fd, NULL, client_write_callback, |
536 | client_write_error_callback, cf); |
537 | bufferevent_enable(cf->event, EV_WRITE); |
538 | goto reply; |
539 | |
540 | reply: |
541 | reply.stream = msg->stream; |
542 | reply.error = error; |
543 | proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply); |
544 | } |
545 | |
546 | /* Write to client file. */ |
547 | static void |
548 | client_write_data(void *data, size_t datalen) |
549 | { |
550 | struct msg_write_data *msg = data; |
551 | struct client_file find, *cf; |
552 | size_t size = datalen - sizeof *msg; |
553 | |
554 | if (datalen < sizeof *msg) |
555 | fatalx("bad MSG_WRITE size" ); |
556 | find.stream = msg->stream; |
557 | if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) |
558 | fatalx("unknown stream number" ); |
559 | log_debug("write %zu to file %d" , size, cf->stream); |
560 | |
561 | if (cf->event != NULL) |
562 | bufferevent_write(cf->event, msg + 1, size); |
563 | } |
564 | |
565 | /* Close client file. */ |
566 | static void |
567 | client_write_close(void *data, size_t datalen) |
568 | { |
569 | struct msg_write_close *msg = data; |
570 | struct client_file find, *cf; |
571 | |
572 | if (datalen != sizeof *msg) |
573 | fatalx("bad MSG_WRITE_CLOSE size" ); |
574 | find.stream = msg->stream; |
575 | if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) |
576 | fatalx("unknown stream number" ); |
577 | log_debug("close file %d" , cf->stream); |
578 | |
579 | if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { |
580 | if (cf->event != NULL) |
581 | bufferevent_free(cf->event); |
582 | if (cf->fd != -1) |
583 | close(cf->fd); |
584 | RB_REMOVE(client_files, &client_files, cf); |
585 | file_free(cf); |
586 | } |
587 | } |
588 | |
589 | /* File read callback. */ |
590 | static void |
591 | client_read_callback(__unused struct bufferevent *bev, void *arg) |
592 | { |
593 | struct client_file *cf = arg; |
594 | void *bdata; |
595 | size_t bsize; |
596 | struct msg_read_data *msg; |
597 | size_t msglen; |
598 | |
599 | msg = xmalloc(sizeof *msg); |
600 | for (;;) { |
601 | bdata = EVBUFFER_DATA(cf->event->input); |
602 | bsize = EVBUFFER_LENGTH(cf->event->input); |
603 | |
604 | if (bsize == 0) |
605 | break; |
606 | if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) |
607 | bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; |
608 | log_debug("read %zu from file %d" , bsize, cf->stream); |
609 | |
610 | msglen = (sizeof *msg) + bsize; |
611 | msg = xrealloc(msg, msglen); |
612 | msg->stream = cf->stream; |
613 | memcpy(msg + 1, bdata, bsize); |
614 | proc_send(client_peer, MSG_READ, -1, msg, msglen); |
615 | |
616 | evbuffer_drain(cf->event->input, bsize); |
617 | } |
618 | free(msg); |
619 | } |
620 | |
621 | /* File read error callback. */ |
622 | static void |
623 | client_read_error_callback(__unused struct bufferevent *bev, |
624 | __unused short what, void *arg) |
625 | { |
626 | struct client_file *cf = arg; |
627 | struct msg_read_done msg; |
628 | |
629 | log_debug("read error file %d" , cf->stream); |
630 | |
631 | msg.stream = cf->stream; |
632 | msg.error = 0; |
633 | proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg); |
634 | |
635 | bufferevent_free(cf->event); |
636 | close(cf->fd); |
637 | RB_REMOVE(client_files, &client_files, cf); |
638 | file_free(cf); |
639 | } |
640 | |
641 | /* Open read file. */ |
642 | static void |
643 | client_read_open(void *data, size_t datalen) |
644 | { |
645 | struct msg_read_open *msg = data; |
646 | const char *path; |
647 | struct msg_read_done reply; |
648 | struct client_file find, *cf; |
649 | const int flags = O_NONBLOCK|O_RDONLY; |
650 | int error = 0; |
651 | |
652 | if (datalen < sizeof *msg) |
653 | fatalx("bad MSG_READ_OPEN size" ); |
654 | if (datalen == sizeof *msg) |
655 | path = "-" ; |
656 | else |
657 | path = (const char *)(msg + 1); |
658 | log_debug("open read file %d %s" , msg->stream, path); |
659 | |
660 | find.stream = msg->stream; |
661 | if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { |
662 | cf = file_create(NULL, msg->stream, NULL, NULL); |
663 | RB_INSERT(client_files, &client_files, cf); |
664 | } else { |
665 | error = EBADF; |
666 | goto reply; |
667 | } |
668 | if (cf->closed) { |
669 | error = EBADF; |
670 | goto reply; |
671 | } |
672 | |
673 | cf->fd = -1; |
674 | if (msg->fd == -1) |
675 | cf->fd = open(path, flags); |
676 | else { |
677 | if (msg->fd != STDIN_FILENO) |
678 | errno = EBADF; |
679 | else { |
680 | cf->fd = dup(msg->fd); |
681 | close(msg->fd); /* can only be used once */ |
682 | } |
683 | } |
684 | if (cf->fd == -1) { |
685 | error = errno; |
686 | goto reply; |
687 | } |
688 | |
689 | cf->event = bufferevent_new(cf->fd, client_read_callback, NULL, |
690 | client_read_error_callback, cf); |
691 | bufferevent_enable(cf->event, EV_READ); |
692 | return; |
693 | |
694 | reply: |
695 | reply.stream = msg->stream; |
696 | reply.error = error; |
697 | proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply); |
698 | } |
699 | |
700 | /* Run command in shell; used for -c. */ |
701 | static __dead void |
702 | client_exec(const char *shell, const char *shellcmd) |
703 | { |
704 | const char *name, *ptr; |
705 | char *argv0; |
706 | |
707 | log_debug("shell %s, command %s" , shell, shellcmd); |
708 | |
709 | ptr = strrchr(shell, '/'); |
710 | if (ptr != NULL && *(ptr + 1) != '\0') |
711 | name = ptr + 1; |
712 | else |
713 | name = shell; |
714 | if (client_flags & CLIENT_LOGIN) |
715 | xasprintf(&argv0, "-%s" , name); |
716 | else |
717 | xasprintf(&argv0, "%s" , name); |
718 | setenv("SHELL" , shell, 1); |
719 | |
720 | proc_clear_signals(client_proc, 1); |
721 | |
722 | setblocking(STDIN_FILENO, 1); |
723 | setblocking(STDOUT_FILENO, 1); |
724 | setblocking(STDERR_FILENO, 1); |
725 | closefrom(STDERR_FILENO + 1); |
726 | |
727 | execl(shell, argv0, "-c" , shellcmd, (char *) NULL); |
728 | fatal("execl failed" ); |
729 | } |
730 | |
731 | /* Callback to handle signals in the client. */ |
732 | static void |
733 | client_signal(int sig) |
734 | { |
735 | struct sigaction sigact; |
736 | int status; |
737 | |
738 | if (sig == SIGCHLD) |
739 | waitpid(WAIT_ANY, &status, WNOHANG); |
740 | else if (!client_attached) { |
741 | if (sig == SIGTERM) |
742 | proc_exit(client_proc); |
743 | } else { |
744 | switch (sig) { |
745 | case SIGHUP: |
746 | client_exitreason = CLIENT_EXIT_LOST_TTY; |
747 | client_exitval = 1; |
748 | proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
749 | break; |
750 | case SIGTERM: |
751 | client_exitreason = CLIENT_EXIT_TERMINATED; |
752 | client_exitval = 1; |
753 | proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
754 | break; |
755 | case SIGWINCH: |
756 | proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); |
757 | break; |
758 | case SIGCONT: |
759 | memset(&sigact, 0, sizeof sigact); |
760 | sigemptyset(&sigact.sa_mask); |
761 | sigact.sa_flags = SA_RESTART; |
762 | sigact.sa_handler = SIG_IGN; |
763 | if (sigaction(SIGTSTP, &sigact, NULL) != 0) |
764 | fatal("sigaction failed" ); |
765 | proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); |
766 | break; |
767 | } |
768 | } |
769 | } |
770 | |
771 | /* Callback for client read events. */ |
772 | static void |
773 | client_dispatch(struct imsg *imsg, __unused void *arg) |
774 | { |
775 | if (imsg == NULL) { |
776 | client_exitreason = CLIENT_EXIT_LOST_SERVER; |
777 | client_exitval = 1; |
778 | proc_exit(client_proc); |
779 | return; |
780 | } |
781 | |
782 | if (client_attached) |
783 | client_dispatch_attached(imsg); |
784 | else |
785 | client_dispatch_wait(imsg); |
786 | } |
787 | |
788 | /* Dispatch imsgs when in wait state (before MSG_READY). */ |
789 | static void |
790 | client_dispatch_wait(struct imsg *imsg) |
791 | { |
792 | char *data; |
793 | ssize_t datalen; |
794 | int retval; |
795 | static int pledge_applied; |
796 | |
797 | /* |
798 | * "sendfd" is no longer required once all of the identify messages |
799 | * have been sent. We know the server won't send us anything until that |
800 | * point (because we don't ask it to), so we can drop "sendfd" once we |
801 | * get the first message from the server. |
802 | */ |
803 | if (!pledge_applied) { |
804 | if (pledge( |
805 | "stdio rpath wpath cpath unix proc exec tty" , |
806 | NULL) != 0) |
807 | fatal("pledge failed" ); |
808 | pledge_applied = 1; |
809 | } |
810 | |
811 | data = imsg->data; |
812 | datalen = imsg->hdr.len - IMSG_HEADER_SIZE; |
813 | |
814 | switch (imsg->hdr.type) { |
815 | case MSG_EXIT: |
816 | case MSG_SHUTDOWN: |
817 | if (datalen != sizeof retval && datalen != 0) |
818 | fatalx("bad MSG_EXIT size" ); |
819 | if (datalen == sizeof retval) { |
820 | memcpy(&retval, data, sizeof retval); |
821 | client_exitval = retval; |
822 | } |
823 | client_exitflag = 1; |
824 | client_exit(); |
825 | break; |
826 | case MSG_READY: |
827 | if (datalen != 0) |
828 | fatalx("bad MSG_READY size" ); |
829 | |
830 | client_attached = 1; |
831 | proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); |
832 | break; |
833 | case MSG_VERSION: |
834 | if (datalen != 0) |
835 | fatalx("bad MSG_VERSION size" ); |
836 | |
837 | fprintf(stderr, "protocol version mismatch " |
838 | "(client %d, server %u)\n" , PROTOCOL_VERSION, |
839 | imsg->hdr.peerid & 0xff); |
840 | client_exitval = 1; |
841 | proc_exit(client_proc); |
842 | break; |
843 | case MSG_SHELL: |
844 | if (datalen == 0 || data[datalen - 1] != '\0') |
845 | fatalx("bad MSG_SHELL string" ); |
846 | |
847 | client_exec(data, shell_command); |
848 | /* NOTREACHED */ |
849 | case MSG_DETACH: |
850 | case MSG_DETACHKILL: |
851 | proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
852 | break; |
853 | case MSG_EXITED: |
854 | proc_exit(client_proc); |
855 | break; |
856 | case MSG_READ_OPEN: |
857 | client_read_open(data, datalen); |
858 | break; |
859 | case MSG_WRITE_OPEN: |
860 | client_write_open(data, datalen); |
861 | break; |
862 | case MSG_WRITE: |
863 | client_write_data(data, datalen); |
864 | break; |
865 | case MSG_WRITE_CLOSE: |
866 | client_write_close(data, datalen); |
867 | break; |
868 | case MSG_OLDSTDERR: |
869 | case MSG_OLDSTDIN: |
870 | case MSG_OLDSTDOUT: |
871 | fprintf(stderr, "server version is too old for client\n" ); |
872 | proc_exit(client_proc); |
873 | break; |
874 | } |
875 | } |
876 | |
877 | /* Dispatch imsgs in attached state (after MSG_READY). */ |
878 | static void |
879 | client_dispatch_attached(struct imsg *imsg) |
880 | { |
881 | struct sigaction sigact; |
882 | char *data; |
883 | ssize_t datalen; |
884 | |
885 | data = imsg->data; |
886 | datalen = imsg->hdr.len - IMSG_HEADER_SIZE; |
887 | |
888 | switch (imsg->hdr.type) { |
889 | case MSG_DETACH: |
890 | case MSG_DETACHKILL: |
891 | if (datalen == 0 || data[datalen - 1] != '\0') |
892 | fatalx("bad MSG_DETACH string" ); |
893 | |
894 | client_exitsession = xstrdup(data); |
895 | client_exittype = imsg->hdr.type; |
896 | if (imsg->hdr.type == MSG_DETACHKILL) |
897 | client_exitreason = CLIENT_EXIT_DETACHED_HUP; |
898 | else |
899 | client_exitreason = CLIENT_EXIT_DETACHED; |
900 | proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
901 | break; |
902 | case MSG_EXEC: |
903 | if (datalen == 0 || data[datalen - 1] != '\0' || |
904 | strlen(data) + 1 == (size_t)datalen) |
905 | fatalx("bad MSG_EXEC string" ); |
906 | client_execcmd = xstrdup(data); |
907 | client_execshell = xstrdup(data + strlen(data) + 1); |
908 | |
909 | client_exittype = imsg->hdr.type; |
910 | proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
911 | break; |
912 | case MSG_EXIT: |
913 | if (datalen != 0 && datalen != sizeof (int)) |
914 | fatalx("bad MSG_EXIT size" ); |
915 | |
916 | proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
917 | client_exitreason = CLIENT_EXIT_EXITED; |
918 | break; |
919 | case MSG_EXITED: |
920 | if (datalen != 0) |
921 | fatalx("bad MSG_EXITED size" ); |
922 | |
923 | proc_exit(client_proc); |
924 | break; |
925 | case MSG_SHUTDOWN: |
926 | if (datalen != 0) |
927 | fatalx("bad MSG_SHUTDOWN size" ); |
928 | |
929 | proc_send(client_peer, MSG_EXITING, -1, NULL, 0); |
930 | client_exitreason = CLIENT_EXIT_SERVER_EXITED; |
931 | client_exitval = 1; |
932 | break; |
933 | case MSG_SUSPEND: |
934 | if (datalen != 0) |
935 | fatalx("bad MSG_SUSPEND size" ); |
936 | |
937 | memset(&sigact, 0, sizeof sigact); |
938 | sigemptyset(&sigact.sa_mask); |
939 | sigact.sa_flags = SA_RESTART; |
940 | sigact.sa_handler = SIG_DFL; |
941 | if (sigaction(SIGTSTP, &sigact, NULL) != 0) |
942 | fatal("sigaction failed" ); |
943 | kill(getpid(), SIGTSTP); |
944 | break; |
945 | case MSG_LOCK: |
946 | if (datalen == 0 || data[datalen - 1] != '\0') |
947 | fatalx("bad MSG_LOCK string" ); |
948 | |
949 | system(data); |
950 | proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); |
951 | break; |
952 | } |
953 | } |
954 | |