Size on split-window is -l not -s. Doh.
[tmux-openbsd.git] / client.c
blob1ff3e1f16090e2188569be30b1521e53d64120f6
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;
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 setblocking(fd, 0);
88 return (fd);
90 failed:
91 close(fd);
92 return (-1);
95 /* Client main loop. */
96 int
97 client_main(int argc, char **argv, int flags)
99 struct cmd *cmd;
100 struct cmd_list *cmdlist;
101 struct msg_command_data cmddata;
102 int cmdflags, fd;
103 enum msgtype msg;
104 char *cause;
106 /* Set up the initial command. */
107 cmdflags = 0;
108 if (shell_cmd != NULL) {
109 msg = MSG_SHELL;
110 cmdflags = CMD_STARTSERVER;
111 } else if (argc == 0) {
112 msg = MSG_COMMAND;
113 cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
114 } else {
115 msg = MSG_COMMAND;
118 * It sucks parsing the command string twice (in client and
119 * later in server) but it is necessary to get the start server
120 * flag.
122 if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
123 log_warnx("%s", cause);
124 return (1);
126 cmdflags &= ~CMD_STARTSERVER;
127 TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
128 if (cmd->entry->flags & CMD_STARTSERVER)
129 cmdflags |= CMD_STARTSERVER;
130 if (cmd->entry->flags & CMD_SENDENVIRON)
131 cmdflags |= CMD_SENDENVIRON;
132 if (cmd->entry->flags & CMD_CANTNEST)
133 cmdflags |= CMD_CANTNEST;
135 cmd_list_free(cmdlist);
139 * Check if this could be a nested session, if the command can't nest:
140 * if the socket path matches $TMUX, this is probably the same server.
142 if (shell_cmd == NULL && environ_path != NULL &&
143 cmdflags & CMD_CANTNEST && strcmp(socket_path, environ_path) == 0) {
144 log_warnx("sessions should be nested with care. "
145 "unset $TMUX to force.");
146 return (1);
149 /* Initialise the client socket and start the server. */
150 fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER);
151 if (fd == -1) {
152 log_warn("failed to connect to server");
153 return (1);
156 /* Set process title, log and signals now this is the client. */
157 setproctitle("client (%s)", socket_path);
158 logfile("client");
160 /* Create imsg. */
161 imsg_init(&client_ibuf, fd);
162 event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
164 /* Establish signal handlers. */
165 set_signals(client_signal);
167 /* Send initial environment. */
168 if (cmdflags & CMD_SENDENVIRON)
169 client_send_environ();
170 client_send_identify(flags);
172 /* Send first command. */
173 if (msg == MSG_COMMAND) {
174 /* Fill in command line arguments. */
175 cmddata.pid = environ_pid;
176 cmddata.idx = environ_idx;
178 /* Prepare command for server. */
179 cmddata.argc = argc;
180 if (cmd_pack_argv(
181 argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
182 log_warnx("command too long");
183 return (1);
186 client_write_server(msg, &cmddata, sizeof cmddata);
187 } else if (msg == MSG_SHELL)
188 client_write_server(msg, NULL, 0);
190 /* Set the event and dispatch. */
191 client_update_event();
192 event_dispatch();
194 /* Print the exit message, if any, and exit. */
195 if (client_attached && client_exitmsg != NULL && !login_shell)
196 printf("[%s]\n", client_exitmsg);
197 return (client_exitval);
200 /* Send identify message to server with the file descriptors. */
201 void
202 client_send_identify(int flags)
204 struct msg_identify_data data;
205 char *term;
206 int fd;
208 data.flags = flags;
210 if (getcwd(data.cwd, sizeof data.cwd) == NULL)
211 *data.cwd = '\0';
213 term = getenv("TERM");
214 if (term == NULL ||
215 strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
216 *data.term = '\0';
218 if ((fd = dup(STDIN_FILENO)) == -1)
219 fatal("dup failed");
220 imsg_compose(&client_ibuf,
221 MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
223 if ((fd = dup(STDOUT_FILENO)) == -1)
224 fatal("dup failed");
225 imsg_compose(&client_ibuf,
226 MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
228 if ((fd = dup(STDERR_FILENO)) == -1)
229 fatal("dup failed");
230 imsg_compose(&client_ibuf,
231 MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
234 /* Forward entire environment to server. */
235 void
236 client_send_environ(void)
238 struct msg_environ_data data;
239 char **var;
241 for (var = environ; *var != NULL; var++) {
242 if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
243 continue;
244 client_write_server(MSG_ENVIRON, &data, sizeof data);
248 /* Write a message to the server without a file descriptor. */
249 void
250 client_write_server(enum msgtype type, void *buf, size_t len)
252 imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
255 /* Update client event based on whether it needs to read or read and write. */
256 void
257 client_update_event(void)
259 short events;
261 event_del(&client_event);
262 events = EV_READ;
263 if (client_ibuf.w.queued > 0)
264 events |= EV_WRITE;
265 event_set(
266 &client_event, client_ibuf.fd, events, client_callback, shell_cmd);
267 event_add(&client_event, NULL);
270 /* Callback to handle signals in the client. */
271 /* ARGSUSED */
272 void
273 client_signal(int sig, unused short events, unused void *data)
275 struct sigaction sigact;
276 int status;
278 if (!client_attached) {
279 switch (sig) {
280 case SIGCHLD:
281 waitpid(WAIT_ANY, &status, WNOHANG);
282 break;
283 case SIGTERM:
284 event_loopexit(NULL);
285 break;
287 } else {
288 switch (sig) {
289 case SIGHUP:
290 client_exitmsg = "lost tty";
291 client_exitval = 1;
292 client_write_server(MSG_EXITING, NULL, 0);
293 break;
294 case SIGTERM:
295 client_exitmsg = "terminated";
296 client_exitval = 1;
297 client_write_server(MSG_EXITING, NULL, 0);
298 break;
299 case SIGWINCH:
300 client_write_server(MSG_RESIZE, NULL, 0);
301 break;
302 case SIGCONT:
303 memset(&sigact, 0, sizeof sigact);
304 sigemptyset(&sigact.sa_mask);
305 sigact.sa_flags = SA_RESTART;
306 sigact.sa_handler = SIG_IGN;
307 if (sigaction(SIGTSTP, &sigact, NULL) != 0)
308 fatal("sigaction failed");
309 client_write_server(MSG_WAKEUP, NULL, 0);
310 break;
314 client_update_event();
317 /* Callback for client imsg read events. */
318 /* ARGSUSED */
319 void
320 client_callback(unused int fd, short events, void *data)
322 ssize_t n;
323 int retval;
325 if (events & EV_READ) {
326 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
327 goto lost_server;
328 if (client_attached)
329 retval = client_dispatch_attached();
330 else
331 retval = client_dispatch_wait(data);
332 if (retval != 0) {
333 event_loopexit(NULL);
334 return;
338 if (events & EV_WRITE) {
339 if (msgbuf_write(&client_ibuf.w) < 0)
340 goto lost_server;
343 client_update_event();
344 return;
346 lost_server:
347 client_exitmsg = "lost server";
348 client_exitval = 1;
349 event_loopexit(NULL);
352 /* Dispatch imsgs when in wait state (before MSG_READY). */
354 client_dispatch_wait(void *data)
356 struct imsg imsg;
357 ssize_t n, datalen;
358 struct msg_shell_data shelldata;
359 struct msg_exit_data exitdata;
360 const char *shellcmd = data;
362 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
363 fatalx("imsg_read failed");
365 for (;;) {
366 if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
367 fatalx("imsg_get failed");
368 if (n == 0)
369 return (0);
370 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
372 switch (imsg.hdr.type) {
373 case MSG_EXIT:
374 case MSG_SHUTDOWN:
375 if (datalen != sizeof exitdata) {
376 if (datalen != 0)
377 fatalx("bad MSG_EXIT size");
378 } else {
379 memcpy(&exitdata, imsg.data, sizeof exitdata);
380 client_exitval = exitdata.retcode;
382 imsg_free(&imsg);
383 return (-1);
384 case MSG_READY:
385 if (datalen != 0)
386 fatalx("bad MSG_READY size");
388 client_attached = 1;
389 break;
390 case MSG_VERSION:
391 if (datalen != 0)
392 fatalx("bad MSG_VERSION size");
394 log_warnx("protocol version mismatch (client %u, "
395 "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
396 client_exitval = 1;
398 imsg_free(&imsg);
399 return (-1);
400 case MSG_SHELL:
401 if (datalen != sizeof shelldata)
402 fatalx("bad MSG_SHELL size");
403 memcpy(&shelldata, imsg.data, sizeof shelldata);
404 shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
406 clear_signals(0);
408 shell_exec(shelldata.shell, shellcmd);
409 /* NOTREACHED */
410 default:
411 fatalx("unexpected message");
414 imsg_free(&imsg);
418 /* Dispatch imsgs in attached state (after MSG_READY). */
419 /* ARGSUSED */
421 client_dispatch_attached(void)
423 struct imsg imsg;
424 struct msg_lock_data lockdata;
425 struct sigaction sigact;
426 ssize_t n, datalen;
428 for (;;) {
429 if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
430 fatalx("imsg_get failed");
431 if (n == 0)
432 return (0);
433 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
435 log_debug("client got %d", imsg.hdr.type);
436 switch (imsg.hdr.type) {
437 case MSG_DETACH:
438 if (datalen != 0)
439 fatalx("bad MSG_DETACH size");
441 client_write_server(MSG_EXITING, NULL, 0);
442 client_exitmsg = "detached";
443 break;
444 case MSG_EXIT:
445 if (datalen != 0 &&
446 datalen != sizeof (struct msg_exit_data))
447 fatalx("bad MSG_EXIT size");
449 client_write_server(MSG_EXITING, NULL, 0);
450 client_exitmsg = "exited";
451 break;
452 case MSG_EXITED:
453 if (datalen != 0)
454 fatalx("bad MSG_EXITED size");
456 imsg_free(&imsg);
457 return (-1);
458 case MSG_SHUTDOWN:
459 if (datalen != 0)
460 fatalx("bad MSG_SHUTDOWN size");
462 client_write_server(MSG_EXITING, NULL, 0);
463 client_exitmsg = "server exited";
464 client_exitval = 1;
465 break;
466 case MSG_SUSPEND:
467 if (datalen != 0)
468 fatalx("bad MSG_SUSPEND size");
470 memset(&sigact, 0, sizeof sigact);
471 sigemptyset(&sigact.sa_mask);
472 sigact.sa_flags = SA_RESTART;
473 sigact.sa_handler = SIG_DFL;
474 if (sigaction(SIGTSTP, &sigact, NULL) != 0)
475 fatal("sigaction failed");
476 kill(getpid(), SIGTSTP);
477 break;
478 case MSG_LOCK:
479 if (datalen != sizeof lockdata)
480 fatalx("bad MSG_LOCK size");
481 memcpy(&lockdata, imsg.data, sizeof lockdata);
483 lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
484 system(lockdata.cmd);
485 client_write_server(MSG_UNLOCK, NULL, 0);
486 break;
487 default:
488 fatalx("unexpected message");
491 imsg_free(&imsg);