1 /* $Id: client.c,v 1.1.1.1 2011/03/10 09:15:36 jmmv Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 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/stat.h> 22 #include <sys/un.h> 23 #include <sys/wait.h> 24 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> 32 33 #include "tmux.h" 34 35 struct imsgbuf client_ibuf; 36 struct event client_event; 37 const char *client_exitmsg; 38 int client_exitval; 39 int client_attached; 40 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 *); 50 51 /* Connect client to server. */ 52 int 53 client_connect(char *path, int start_server) 54 { 55 struct sockaddr_un sa; 56 size_t size; 57 int fd, mode; 58 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); 65 } 66 67 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 68 fatal("socket failed"); 69 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; 84 } 85 } 86 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); 92 93 failed: 94 close(fd); 95 return (-1); 96 } 97 98 /* Client main loop. */ 99 int 100 client_main(int argc, char **argv, int flags) 101 { 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; 108 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; 119 120 /* 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. 124 */ 125 if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) { 126 log_warnx("%s", cause); 127 return (1); 128 } 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; 137 } 138 cmd_list_free(cmdlist); 139 } 140 141 /* 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. 144 */ 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); 150 } 151 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); 157 } 158 159 /* Set process title, log and signals now this is the client. */ 160 #ifdef HAVE_SETPROCTITLE 161 setproctitle("client (%s)", socket_path); 162 #endif 163 logfile("client"); 164 165 /* Create imsg. */ 166 imsg_init(&client_ibuf, fd); 167 event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); 168 169 /* Establish signal handlers. */ 170 set_signals(client_signal); 171 172 /* Send initial environment. */ 173 if (cmdflags & CMD_SENDENVIRON) 174 client_send_environ(); 175 client_send_identify(flags); 176 177 /* Send first command. */ 178 if (msg == MSG_COMMAND) { 179 /* Fill in command line arguments. */ 180 cmddata.pid = environ_pid; 181 cmddata.idx = environ_idx; 182 183 /* Prepare command for server. */ 184 cmddata.argc = argc; 185 if (cmd_pack_argv( 186 argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) { 187 log_warnx("command too long"); 188 return (1); 189 } 190 191 client_write_server(msg, &cmddata, sizeof cmddata); 192 } else if (msg == MSG_SHELL) 193 client_write_server(msg, NULL, 0); 194 195 /* Set the event and dispatch. */ 196 client_update_event(); 197 event_dispatch(); 198 199 /* Print the exit message, if any, and exit. */ 200 if (client_attached && client_exitmsg != NULL && !login_shell) 201 printf("[%s]\n", client_exitmsg); 202 return (client_exitval); 203 } 204 205 /* Send identify message to server with the file descriptors. */ 206 void 207 client_send_identify(int flags) 208 { 209 struct msg_identify_data data; 210 char *term; 211 int fd; 212 213 data.flags = flags; 214 215 if (getcwd(data.cwd, sizeof data.cwd) == NULL) 216 *data.cwd = '\0'; 217 218 term = getenv("TERM"); 219 if (term == NULL || 220 strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) 221 *data.term = '\0'; 222 223 if ((fd = dup(STDIN_FILENO)) == -1) 224 fatal("dup failed"); 225 imsg_compose(&client_ibuf, 226 MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); 227 228 if ((fd = dup(STDOUT_FILENO)) == -1) 229 fatal("dup failed"); 230 imsg_compose(&client_ibuf, 231 MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0); 232 233 if ((fd = dup(STDERR_FILENO)) == -1) 234 fatal("dup failed"); 235 imsg_compose(&client_ibuf, 236 MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0); 237 } 238 239 /* Forward entire environment to server. */ 240 void 241 client_send_environ(void) 242 { 243 struct msg_environ_data data; 244 char **var; 245 246 for (var = environ; *var != NULL; var++) { 247 if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var) 248 continue; 249 client_write_server(MSG_ENVIRON, &data, sizeof data); 250 } 251 } 252 253 /* Write a message to the server without a file descriptor. */ 254 void 255 client_write_server(enum msgtype type, void *buf, size_t len) 256 { 257 imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); 258 } 259 260 /* Update client event based on whether it needs to read or read and write. */ 261 void 262 client_update_event(void) 263 { 264 short events; 265 266 event_del(&client_event); 267 events = EV_READ; 268 if (client_ibuf.w.queued > 0) 269 events |= EV_WRITE; 270 event_set( 271 &client_event, client_ibuf.fd, events, client_callback, shell_cmd); 272 event_add(&client_event, NULL); 273 } 274 275 /* Callback to handle signals in the client. */ 276 /* ARGSUSED */ 277 void 278 client_signal(int sig, unused short events, unused void *data) 279 { 280 struct sigaction sigact; 281 int status; 282 283 if (!client_attached) { 284 switch (sig) { 285 case SIGCHLD: 286 waitpid(WAIT_ANY, &status, WNOHANG); 287 break; 288 case SIGTERM: 289 event_loopexit(NULL); 290 break; 291 } 292 } else { 293 switch (sig) { 294 case SIGHUP: 295 client_exitmsg = "lost tty"; 296 client_exitval = 1; 297 client_write_server(MSG_EXITING, NULL, 0); 298 break; 299 case SIGTERM: 300 client_exitmsg = "terminated"; 301 client_exitval = 1; 302 client_write_server(MSG_EXITING, NULL, 0); 303 break; 304 case SIGWINCH: 305 client_write_server(MSG_RESIZE, NULL, 0); 306 break; 307 case SIGCONT: 308 memset(&sigact, 0, sizeof sigact); 309 sigemptyset(&sigact.sa_mask); 310 sigact.sa_flags = SA_RESTART; 311 sigact.sa_handler = SIG_IGN; 312 if (sigaction(SIGTSTP, &sigact, NULL) != 0) 313 fatal("sigaction failed"); 314 client_write_server(MSG_WAKEUP, NULL, 0); 315 break; 316 } 317 } 318 319 client_update_event(); 320 } 321 322 /* Callback for client imsg read events. */ 323 /* ARGSUSED */ 324 void 325 client_callback(unused int fd, short events, void *data) 326 { 327 ssize_t n; 328 int retval; 329 330 if (events & EV_READ) { 331 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) 332 goto lost_server; 333 if (client_attached) 334 retval = client_dispatch_attached(); 335 else 336 retval = client_dispatch_wait(data); 337 if (retval != 0) { 338 event_loopexit(NULL); 339 return; 340 } 341 } 342 343 if (events & EV_WRITE) { 344 if (msgbuf_write(&client_ibuf.w) < 0) 345 goto lost_server; 346 } 347 348 client_update_event(); 349 return; 350 351 lost_server: 352 client_exitmsg = "lost server"; 353 client_exitval = 1; 354 event_loopexit(NULL); 355 } 356 357 /* Dispatch imsgs when in wait state (before MSG_READY). */ 358 int 359 client_dispatch_wait(void *data) 360 { 361 struct imsg imsg; 362 ssize_t n, datalen; 363 struct msg_shell_data shelldata; 364 struct msg_exit_data exitdata; 365 const char *shellcmd = data; 366 367 if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) 368 fatalx("imsg_read failed"); 369 370 for (;;) { 371 if ((n = imsg_get(&client_ibuf, &imsg)) == -1) 372 fatalx("imsg_get failed"); 373 if (n == 0) 374 return (0); 375 datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 376 377 switch (imsg.hdr.type) { 378 case MSG_EXIT: 379 case MSG_SHUTDOWN: 380 if (datalen != sizeof exitdata) { 381 if (datalen != 0) 382 fatalx("bad MSG_EXIT size"); 383 } else { 384 memcpy(&exitdata, imsg.data, sizeof exitdata); 385 client_exitval = exitdata.retcode; 386 } 387 imsg_free(&imsg); 388 return (-1); 389 case MSG_READY: 390 if (datalen != 0) 391 fatalx("bad MSG_READY size"); 392 393 client_attached = 1; 394 break; 395 case MSG_VERSION: 396 if (datalen != 0) 397 fatalx("bad MSG_VERSION size"); 398 399 log_warnx("protocol version mismatch (client %u, " 400 "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid); 401 client_exitval = 1; 402 403 imsg_free(&imsg); 404 return (-1); 405 case MSG_SHELL: 406 if (datalen != sizeof shelldata) 407 fatalx("bad MSG_SHELL size"); 408 memcpy(&shelldata, imsg.data, sizeof shelldata); 409 shelldata.shell[(sizeof shelldata.shell) - 1] = '\0'; 410 411 clear_signals(0); 412 413 shell_exec(shelldata.shell, shellcmd); 414 /* NOTREACHED */ 415 default: 416 fatalx("unexpected message"); 417 } 418 419 imsg_free(&imsg); 420 } 421 } 422 423 /* Dispatch imsgs in attached state (after MSG_READY). */ 424 /* ARGSUSED */ 425 int 426 client_dispatch_attached(void) 427 { 428 struct imsg imsg; 429 struct msg_lock_data lockdata; 430 struct sigaction sigact; 431 ssize_t n, datalen; 432 433 for (;;) { 434 if ((n = imsg_get(&client_ibuf, &imsg)) == -1) 435 fatalx("imsg_get failed"); 436 if (n == 0) 437 return (0); 438 datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 439 440 log_debug("client got %d", imsg.hdr.type); 441 switch (imsg.hdr.type) { 442 case MSG_DETACH: 443 if (datalen != 0) 444 fatalx("bad MSG_DETACH size"); 445 446 client_write_server(MSG_EXITING, NULL, 0); 447 client_exitmsg = "detached"; 448 break; 449 case MSG_EXIT: 450 if (datalen != 0 && 451 datalen != sizeof (struct msg_exit_data)) 452 fatalx("bad MSG_EXIT size"); 453 454 client_write_server(MSG_EXITING, NULL, 0); 455 client_exitmsg = "exited"; 456 break; 457 case MSG_EXITED: 458 if (datalen != 0) 459 fatalx("bad MSG_EXITED size"); 460 461 imsg_free(&imsg); 462 return (-1); 463 case MSG_SHUTDOWN: 464 if (datalen != 0) 465 fatalx("bad MSG_SHUTDOWN size"); 466 467 client_write_server(MSG_EXITING, NULL, 0); 468 client_exitmsg = "server exited"; 469 client_exitval = 1; 470 break; 471 case MSG_SUSPEND: 472 if (datalen != 0) 473 fatalx("bad MSG_SUSPEND size"); 474 475 memset(&sigact, 0, sizeof sigact); 476 sigemptyset(&sigact.sa_mask); 477 sigact.sa_flags = SA_RESTART; 478 sigact.sa_handler = SIG_DFL; 479 if (sigaction(SIGTSTP, &sigact, NULL) != 0) 480 fatal("sigaction failed"); 481 kill(getpid(), SIGTSTP); 482 break; 483 case MSG_LOCK: 484 if (datalen != sizeof lockdata) 485 fatalx("bad MSG_LOCK size"); 486 memcpy(&lockdata, imsg.data, sizeof lockdata); 487 488 lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0'; 489 system(lockdata.cmd); 490 client_write_server(MSG_UNLOCK, NULL, 0); 491 break; 492 default: 493 fatalx("unexpected message"); 494 } 495 496 imsg_free(&imsg); 497 } 498 } 499