Add XAUTHORITY to update-environment, requested by Andreas Kloeckner.
[tmux-openbsd.git] / client.c
blob35a2e4e802fabaa2b230e3f1fdf4cb42220c317b
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
22 #include <sys/un.h>
23 #include <sys/wait.h>
25 #include <errno.h>
26 #include <event.h>
27 #include <fcntl.h>
28 #include <pwd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
33 #include "tmux.h"
35 struct imsgbuf client_ibuf;
36 struct event client_event;
37 const char *client_exitmsg;
38 int client_exitval;
39 int client_attached;
41 int client_connect(char *, int);
42 void client_send_identify(int);
43 void client_send_environ(void);
44 void client_write_server(enum msgtype, void *, size_t);
45 void client_update_event(void);
46 void client_signal(int, short, void *);
47 void client_callback(int, short, void *);
48 int client_dispatch_attached(void);
49 int client_dispatch_wait(void *);
51 /* Connect client to server. */
52 int
53 client_connect(char *path, int start_server)
55 struct sockaddr_un sa;
56 size_t size;
57 int fd, mode;
59 memset(&sa, 0, sizeof sa);
60 sa.sun_family = AF_UNIX;
61 size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
62 if (size >= sizeof sa.sun_path) {
63 errno = ENAMETOOLONG;
64 return (-1);
67 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
68 fatal("socket failed");
70 if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
71 if (!start_server)
72 goto failed;
73 switch (errno) {
74 case ECONNREFUSED:
75 if (unlink(path) != 0)
76 goto failed;
77 /* FALLTHROUGH */
78 case ENOENT:
79 if ((fd = server_start()) == -1)
80 goto failed;
81 break;
82 default:
83 goto failed;
87 if ((mode = fcntl(fd, F_GETFL)) == -1)
88 fatal("fcntl failed");
89 if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
90 fatal("fcntl failed");
91 return (fd);
93 failed:
94 close(fd);
95 return (-1);
98 /* Client main loop. */
99 int
100 client_main(int argc, char **argv, int flags)
102 struct cmd *cmd;
103 struct cmd_list *cmdlist;
104 struct msg_command_data cmddata;
105 int cmdflags, fd;
106 enum msgtype msg;
107 char *cause;
109 /* Set up the initial command. */
110 cmdflags = 0;
111 if (shell_cmd != NULL) {
112 msg = MSG_SHELL;
113 cmdflags = CMD_STARTSERVER;
114 } else if (argc == 0) {
115 msg = MSG_COMMAND;
116 cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
117 } else {
118 msg = MSG_COMMAND;
121 * It sucks parsing the command string twice (in client and
122 * later in server) but it is necessary to get the start server
123 * flag.
125 if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
126 log_warnx("%s", cause);
127 return (1);
129 cmdflags &= ~CMD_STARTSERVER;
130 TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
131 if (cmd->entry->flags & CMD_STARTSERVER)
132 cmdflags |= CMD_STARTSERVER;
133 if (cmd->entry->flags & CMD_SENDENVIRON)
134 cmdflags |= CMD_SENDENVIRON;
135 if (cmd->entry->flags & CMD_CANTNEST)
136 cmdflags |= CMD_CANTNEST;
138 cmd_list_free(cmdlist);
142 * Check if this could be a nested session, if the command can't nest:
143 * if the socket path matches $TMUX, this is probably the same server.
145 if (shell_cmd == NULL && environ_path != NULL &&
146 cmdflags & CMD_CANTNEST && strcmp(socket_path, environ_path) == 0) {
147 log_warnx("sessions should be nested with care. "
148 "unset $TMUX to force.");
149 return (1);
152 /* Initialise the client socket and start the server. */
153 fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER);
154 if (fd == -1) {
155 log_warn("failed to connect to server");
156 return (1);
159 /* Set process title, log and signals now this is the client. */
160 setproctitle("client (%s)", socket_path);
161 logfile("client");
163 /* Create imsg. */
164 imsg_init(&client_ibuf, fd);
165 event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
167 /* Establish signal handlers. */
168 set_signals(client_signal);
170 /* Send initial environment. */
171 if (cmdflags & CMD_SENDENVIRON)
172 client_send_environ();
173 client_send_identify(flags);
175 /* Send first command. */
176 if (msg == MSG_COMMAND) {
177 /* Fill in command line arguments. */
178 cmddata.pid = environ_pid;
179 cmddata.idx = environ_idx;
181 /* Prepare command for server. */
182 cmddata.argc = argc;
183 if (cmd_pack_argv(
184 argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
185 log_warnx("command too long");
186 return (1);
189 client_write_server(msg, &cmddata, sizeof cmddata);
190 } else if (msg == MSG_SHELL)
191 client_write_server(msg, NULL, 0);
193 /* Set the event and dispatch. */
194 client_update_event();
195 event_dispatch();
197 /* Print the exit message, if any, and exit. */
198 if (client_attached && client_exitmsg != NULL && !login_shell)
199 printf("[%s]\n", client_exitmsg);
200 return (client_exitval);
203 /* Send identify message to server with the file descriptors. */
204 void
205 client_send_identify(int flags)
207 struct msg_identify_data data;
208 char *term;
209 int fd;
211 data.flags = flags;
213 if (getcwd(data.cwd, sizeof data.cwd) == NULL)
214 *data.cwd = '\0';
216 term = getenv("TERM");
217 if (term == NULL ||
218 strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
219 *data.term = '\0';
221 if ((fd = dup(STDIN_FILENO)) == -1)
222 fatal("dup failed");
223 imsg_compose(&client_ibuf,
224 MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
226 if ((fd = dup(STDOUT_FILENO)) == -1)
227 fatal("dup failed");
228 imsg_compose(&client_ibuf,
229 MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
231 if ((fd = dup(STDERR_FILENO)) == -1)
232 fatal("dup failed");
233 imsg_compose(&client_ibuf,
234 MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
237 /* Forward entire environment to server. */
238 void
239 client_send_environ(void)
241 struct msg_environ_data data;
242 char **var;
244 for (var = environ; *var != NULL; var++) {
245 if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
246 continue;
247 client_write_server(MSG_ENVIRON, &data, sizeof data);
251 /* Write a message to the server without a file descriptor. */
252 void
253 client_write_server(enum msgtype type, void *buf, size_t len)
255 imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
258 /* Update client event based on whether it needs to read or read and write. */
259 void
260 client_update_event(void)
262 short events;
264 event_del(&client_event);
265 events = EV_READ;
266 if (client_ibuf.w.queued > 0)
267 events |= EV_WRITE;
268 event_set(
269 &client_event, client_ibuf.fd, events, client_callback, shell_cmd);
270 event_add(&client_event, NULL);
273 /* Callback to handle signals in the client. */
274 /* ARGSUSED */
275 void
276 client_signal(int sig, unused short events, unused void *data)
278 struct sigaction sigact;
279 int status;
281 if (!client_attached) {
282 switch (sig) {
283 case SIGCHLD:
284 waitpid(WAIT_ANY, &status, WNOHANG);
285 break;
286 case SIGTERM:
287 event_loopexit(NULL);
288 break;
290 } else {
291 switch (sig) {
292 case SIGHUP:
293 client_exitmsg = "lost tty";
294 client_exitval = 1;
295 client_write_server(MSG_EXITING, NULL, 0);
296 break;
297 case SIGTERM:
298 client_exitmsg = "terminated";
299 client_exitval = 1;
300 client_write_server(MSG_EXITING, NULL, 0);
301 break;
302 case SIGWINCH:
303 client_write_server(MSG_RESIZE, NULL, 0);
304 break;
305 case SIGCONT:
306 memset(&sigact, 0, sizeof sigact);
307 sigemptyset(&sigact.sa_mask);
308 sigact.sa_flags = SA_RESTART;
309 sigact.sa_handler = SIG_IGN;
310 if (sigaction(SIGTSTP, &sigact, NULL) != 0)
311 fatal("sigaction failed");
312 client_write_server(MSG_WAKEUP, NULL, 0);
313 break;
317 client_update_event();
320 /* Callback for client imsg read events. */
321 /* ARGSUSED */
322 void
323 client_callback(unused int fd, short events, void *data)
325 ssize_t n;
326 int retval;
328 if (events & EV_READ) {
329 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
330 goto lost_server;
331 if (client_attached)
332 retval = client_dispatch_attached();
333 else
334 retval = client_dispatch_wait(data);
335 if (retval != 0) {
336 event_loopexit(NULL);
337 return;
341 if (events & EV_WRITE) {
342 if (msgbuf_write(&client_ibuf.w) < 0)
343 goto lost_server;
346 client_update_event();
347 return;
349 lost_server:
350 client_exitmsg = "lost server";
351 client_exitval = 1;
352 event_loopexit(NULL);
355 /* Dispatch imsgs when in wait state (before MSG_READY). */
357 client_dispatch_wait(void *data)
359 struct imsg imsg;
360 ssize_t n, datalen;
361 struct msg_shell_data shelldata;
362 struct msg_exit_data exitdata;
363 const char *shellcmd = data;
365 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
366 fatalx("imsg_read failed");
368 for (;;) {
369 if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
370 fatalx("imsg_get failed");
371 if (n == 0)
372 return (0);
373 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
375 switch (imsg.hdr.type) {
376 case MSG_EXIT:
377 case MSG_SHUTDOWN:
378 if (datalen != sizeof exitdata) {
379 if (datalen != 0)
380 fatalx("bad MSG_EXIT size");
381 } else {
382 memcpy(&exitdata, imsg.data, sizeof exitdata);
383 client_exitval = exitdata.retcode;
385 imsg_free(&imsg);
386 return (-1);
387 case MSG_READY:
388 if (datalen != 0)
389 fatalx("bad MSG_READY size");
391 client_attached = 1;
392 break;
393 case MSG_VERSION:
394 if (datalen != 0)
395 fatalx("bad MSG_VERSION size");
397 log_warnx("protocol version mismatch (client %u, "
398 "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
399 client_exitval = 1;
401 imsg_free(&imsg);
402 return (-1);
403 case MSG_SHELL:
404 if (datalen != sizeof shelldata)
405 fatalx("bad MSG_SHELL size");
406 memcpy(&shelldata, imsg.data, sizeof shelldata);
407 shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
409 clear_signals(0);
411 shell_exec(shelldata.shell, shellcmd);
412 /* NOTREACHED */
413 default:
414 fatalx("unexpected message");
417 imsg_free(&imsg);
421 /* Dispatch imsgs in attached state (after MSG_READY). */
422 /* ARGSUSED */
424 client_dispatch_attached(void)
426 struct imsg imsg;
427 struct msg_lock_data lockdata;
428 struct sigaction sigact;
429 ssize_t n, datalen;
431 for (;;) {
432 if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
433 fatalx("imsg_get failed");
434 if (n == 0)
435 return (0);
436 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
438 log_debug("client got %d", imsg.hdr.type);
439 switch (imsg.hdr.type) {
440 case MSG_DETACH:
441 if (datalen != 0)
442 fatalx("bad MSG_DETACH size");
444 client_write_server(MSG_EXITING, NULL, 0);
445 client_exitmsg = "detached";
446 break;
447 case MSG_EXIT:
448 if (datalen != 0 &&
449 datalen != sizeof (struct msg_exit_data))
450 fatalx("bad MSG_EXIT size");
452 client_write_server(MSG_EXITING, NULL, 0);
453 client_exitmsg = "exited";
454 break;
455 case MSG_EXITED:
456 if (datalen != 0)
457 fatalx("bad MSG_EXITED size");
459 imsg_free(&imsg);
460 return (-1);
461 case MSG_SHUTDOWN:
462 if (datalen != 0)
463 fatalx("bad MSG_SHUTDOWN size");
465 client_write_server(MSG_EXITING, NULL, 0);
466 client_exitmsg = "server exited";
467 client_exitval = 1;
468 break;
469 case MSG_SUSPEND:
470 if (datalen != 0)
471 fatalx("bad MSG_SUSPEND size");
473 memset(&sigact, 0, sizeof sigact);
474 sigemptyset(&sigact.sa_mask);
475 sigact.sa_flags = SA_RESTART;
476 sigact.sa_handler = SIG_DFL;
477 if (sigaction(SIGTSTP, &sigact, NULL) != 0)
478 fatal("sigaction failed");
479 kill(getpid(), SIGTSTP);
480 break;
481 case MSG_LOCK:
482 if (datalen != sizeof lockdata)
483 fatalx("bad MSG_LOCK size");
484 memcpy(&lockdata, imsg.data, sizeof lockdata);
486 lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
487 system(lockdata.cmd);
488 client_write_server(MSG_UNLOCK, NULL, 0);
489 break;
490 default:
491 fatalx("unexpected message");
494 imsg_free(&imsg);