abduco.c (18589B)
1 /* 2 * Copyright (c) 2013-2018 Marc André Tanner <mat at brain-dump.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <inttypes.h> 19 #include <stdio.h> 20 #include <stdarg.h> 21 #include <stdlib.h> 22 #include <stdbool.h> 23 #include <stddef.h> 24 #include <signal.h> 25 #include <libgen.h> 26 #include <string.h> 27 #include <limits.h> 28 #include <dirent.h> 29 #include <termios.h> 30 #include <time.h> 31 #include <unistd.h> 32 #include <pwd.h> 33 #include <sys/select.h> 34 #include <sys/stat.h> 35 #include <sys/ioctl.h> 36 #include <sys/types.h> 37 #include <sys/wait.h> 38 #include <sys/socket.h> 39 #include <sys/un.h> 40 #if defined(__linux__) || defined(__CYGWIN__) 41 # include <pty.h> 42 #elif defined(__FreeBSD__) || defined(__DragonFly__) 43 # include <libutil.h> 44 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 45 # include <util.h> 46 #endif 47 48 #if defined CTRL && defined _AIX 49 #undef CTRL 50 #endif 51 #ifndef CTRL 52 #define CTRL(k) ((k) & 0x1F) 53 #endif 54 55 #include "config.h" 56 57 #if defined(_AIX) 58 # include "forkpty-aix.c" 59 #elif defined(__sun) 60 # include "forkpty-sunos.c" 61 #endif 62 63 #define countof(arr) (sizeof(arr) / sizeof((arr)[0])) 64 65 enum PacketType { 66 MSG_CONTENT, 67 MSG_REFUSED, 68 MSG_ATTACH, 69 MSG_DETACH, 70 MSG_RESIZE, 71 MSG_EXIT, 72 MSG_PID, 73 }; 74 75 typedef struct { 76 uint32_t type; 77 uint32_t len; 78 union { 79 char msg[4096 - 2*sizeof(uint32_t)]; 80 struct { 81 uint16_t rows; 82 uint16_t cols; 83 } ws; 84 uint32_t i; 85 uint64_t l; 86 } u; 87 } Packet; 88 89 typedef struct Client Client; 90 struct Client { 91 int socket; 92 enum { 93 STATE_REFUSED, 94 STATE_CONNECTED, 95 STATE_ATTACHED, 96 STATE_DETACHED, 97 STATE_DISCONNECTED, 98 } state; 99 bool need_resize; 100 enum { 101 CLIENT_READONLY = 1 << 0, 102 CLIENT_LOWPRIORITY = 1 << 1, 103 CLIENT_MAXPRIORITY = 1 << 2, 104 } flags; 105 Client *next; 106 }; 107 108 typedef struct { 109 Client *clients; 110 int socket; 111 Packet pty_output; 112 int pty; 113 int has_maxprio; 114 int exit_status; 115 struct termios term; 116 struct winsize winsize; 117 pid_t pid; 118 volatile sig_atomic_t running; 119 const char *name; 120 const char *session_name; 121 char host[255]; 122 bool read_pty; 123 } Server; 124 125 static Server server = { 126 .running = true, 127 .exit_status = -1, 128 .host = "@localhost", 129 .has_maxprio = 0, 130 }; 131 static Client client; 132 static struct termios orig_term, cur_term; 133 static bool has_term, alternate_buffer, quiet, passthrough; 134 135 static struct sockaddr_un sockaddr = { 136 .sun_family = AF_UNIX, 137 }; 138 139 static bool set_socket_name(struct sockaddr_un *sockaddr, const char *name); 140 static void die(const char *s); 141 static void info(const char *str, ...); 142 143 #include "debug.c" 144 145 static inline size_t packet_header_size() { 146 return offsetof(Packet, u); 147 } 148 149 static size_t packet_size(Packet *pkt) { 150 return packet_header_size() + pkt->len; 151 } 152 153 static ssize_t write_all(int fd, const char *buf, size_t len) { 154 debug("write_all(%d)\n", len); 155 ssize_t ret = len; 156 while (len > 0) { 157 ssize_t res = write(fd, buf, len); 158 if (res < 0) { 159 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) 160 continue; 161 return -1; 162 } 163 if (res == 0) 164 return ret - len; 165 buf += res; 166 len -= res; 167 } 168 return ret; 169 } 170 171 static ssize_t read_all(int fd, char *buf, size_t len) { 172 debug("read_all(%d)\n", len); 173 ssize_t ret = len; 174 while (len > 0) { 175 ssize_t res = read(fd, buf, len); 176 if (res < 0) { 177 if (errno == EWOULDBLOCK) 178 return ret - len; 179 if (errno == EAGAIN || errno == EINTR) 180 continue; 181 return -1; 182 } 183 if (res == 0) 184 return ret - len; 185 buf += res; 186 len -= res; 187 } 188 return ret; 189 } 190 191 static bool send_packet(int socket, Packet *pkt) { 192 size_t size = packet_size(pkt); 193 if (size > sizeof(*pkt)) 194 return false; 195 return write_all(socket, (char *)pkt, size) == size; 196 } 197 198 static bool recv_packet(int socket, Packet *pkt) { 199 ssize_t len = read_all(socket, (char*)pkt, packet_header_size()); 200 if (len <= 0 || len != packet_header_size()) 201 return false; 202 if (pkt->len > sizeof(pkt->u.msg)) { 203 pkt->len = 0; 204 return false; 205 } 206 if (pkt->len > 0) { 207 len = read_all(socket, pkt->u.msg, pkt->len); 208 if (len <= 0 || len != pkt->len) 209 return false; 210 } 211 return true; 212 } 213 214 #include "client.c" 215 #include "server.c" 216 217 static void info(const char *str, ...) { 218 va_list ap; 219 va_start(ap, str); 220 if (str && !quiet) { 221 fprintf(stderr, "%s: %s: ", server.name, server.session_name); 222 vfprintf(stderr, str, ap); 223 fprintf(stderr, "\r\n"); 224 fflush(stderr); 225 } 226 va_end(ap); 227 } 228 229 static void die(const char *s) { 230 perror(s); 231 exit(EXIT_FAILURE); 232 } 233 234 static void usage(void) { 235 fprintf(stderr, "usage: abduco [-A|-a|-c|-n] [-p] [-r] [-q] [-l|-m] " 236 "[-f] [-e detachkey] name command\n"); 237 exit(EXIT_FAILURE); 238 } 239 240 static bool xsnprintf(char *buf, size_t size, const char *fmt, ...) { 241 va_list ap; 242 if (size > INT_MAX) 243 return false; 244 va_start(ap, fmt); 245 int n = vsnprintf(buf, size, fmt, ap); 246 va_end(ap); 247 if (n == -1) 248 return false; 249 if (n >= size) { 250 errno = ENAMETOOLONG; 251 return false; 252 } 253 return true; 254 } 255 256 static int session_connect(const char *name) { 257 int fd; 258 struct stat sb; 259 if (!set_socket_name(&sockaddr, name) || (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 260 return -1; 261 socklen_t socklen = offsetof(struct sockaddr_un, sun_path) + strlen(sockaddr.sun_path) + 1; 262 if (connect(fd, (struct sockaddr*)&sockaddr, socklen) == -1) { 263 if (errno == ECONNREFUSED && stat(sockaddr.sun_path, &sb) == 0 && S_ISSOCK(sb.st_mode)) 264 unlink(sockaddr.sun_path); 265 close(fd); 266 return -1; 267 } 268 return fd; 269 } 270 271 static pid_t session_exists(const char *name) { 272 Packet pkt; 273 pid_t pid = 0; 274 if ((server.socket = session_connect(name)) == -1) 275 return pid; 276 if (client_recv_packet(&pkt) && pkt.type == MSG_PID) 277 pid = pkt.u.l; 278 close(server.socket); 279 return pid; 280 } 281 282 static bool session_alive(const char *name) { 283 struct stat sb; 284 return session_exists(name) && 285 stat(sockaddr.sun_path, &sb) == 0 && 286 S_ISSOCK(sb.st_mode) && (sb.st_mode & S_IXGRP) == 0; 287 } 288 289 static bool create_socket_dir(struct sockaddr_un *sockaddr) { 290 sockaddr->sun_path[0] = '\0'; 291 int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); 292 if (socketfd == -1) 293 return false; 294 295 const size_t maxlen = sizeof(sockaddr->sun_path); 296 uid_t uid = getuid(); 297 struct passwd *pw = getpwuid(uid); 298 299 for (unsigned int i = 0; i < countof(socket_dirs); i++) { 300 struct stat sb; 301 struct Dir *dir = &socket_dirs[i]; 302 bool ishome = false; 303 if (dir->env) { 304 dir->path = getenv(dir->env); 305 ishome = !strcmp(dir->env, "HOME"); 306 if (ishome && (!dir->path || !dir->path[0]) && pw) 307 dir->path = pw->pw_dir; 308 } 309 if (!dir->path || !dir->path[0]) 310 continue; 311 if (!xsnprintf(sockaddr->sun_path, maxlen, "%s/%s%s/", dir->path, ishome ? "." : "", server.name)) 312 continue; 313 mode_t mask = umask(0); 314 int r = mkdir(sockaddr->sun_path, dir->personal ? S_IRWXU : S_IRWXU|S_IRWXG|S_IRWXO|S_ISVTX); 315 umask(mask); 316 if (r != 0 && errno != EEXIST) 317 continue; 318 if (lstat(sockaddr->sun_path, &sb) != 0) 319 continue; 320 if (!S_ISDIR(sb.st_mode)) { 321 errno = ENOTDIR; 322 continue; 323 } 324 325 size_t dirlen = strlen(sockaddr->sun_path); 326 if (!dir->personal) { 327 /* create subdirectory only accessible to user */ 328 if (pw && !xsnprintf(sockaddr->sun_path+dirlen, maxlen-dirlen, "%s/", pw->pw_name)) 329 continue; 330 if (!pw && !xsnprintf(sockaddr->sun_path+dirlen, maxlen-dirlen, "%d/", uid)) 331 continue; 332 if (mkdir(sockaddr->sun_path, S_IRWXU) != 0 && errno != EEXIST) 333 continue; 334 if (lstat(sockaddr->sun_path, &sb) != 0) 335 continue; 336 if (!S_ISDIR(sb.st_mode)) { 337 errno = ENOTDIR; 338 continue; 339 } 340 dirlen = strlen(sockaddr->sun_path); 341 } 342 343 if (sb.st_uid != uid || sb.st_mode & (S_IRWXG|S_IRWXO)) { 344 errno = EACCES; 345 continue; 346 } 347 348 if (!xsnprintf(sockaddr->sun_path+dirlen, maxlen-dirlen, ".abduco-%d", getpid())) 349 continue; 350 351 socklen_t socklen = offsetof(struct sockaddr_un, sun_path) + strlen(sockaddr->sun_path) + 1; 352 if (bind(socketfd, (struct sockaddr*)sockaddr, socklen) == -1) 353 continue; 354 unlink(sockaddr->sun_path); 355 close(socketfd); 356 sockaddr->sun_path[dirlen] = '\0'; 357 return true; 358 } 359 360 close(socketfd); 361 return false; 362 } 363 364 static bool set_socket_name(struct sockaddr_un *sockaddr, const char *name) { 365 const size_t maxlen = sizeof(sockaddr->sun_path); 366 const char *session_name = NULL; 367 char buf[maxlen]; 368 369 if (name[0] == '/') { 370 if (strlen(name) >= maxlen) { 371 errno = ENAMETOOLONG; 372 return false; 373 } 374 strncpy(sockaddr->sun_path, name, maxlen); 375 } else if (name[0] == '.' && (name[1] == '.' || name[1] == '/')) { 376 char *cwd = getcwd(buf, sizeof buf); 377 if (!cwd) 378 return false; 379 if (!xsnprintf(sockaddr->sun_path, maxlen, "%s/%s", cwd, name)) 380 return false; 381 } else { 382 if (!create_socket_dir(sockaddr)) 383 return false; 384 if (strlen(sockaddr->sun_path) + strlen(name) + strlen(server.host) >= maxlen) { 385 errno = ENAMETOOLONG; 386 return false; 387 } 388 session_name = name; 389 strncat(sockaddr->sun_path, name, maxlen - strlen(sockaddr->sun_path) - 1); 390 strncat(sockaddr->sun_path, server.host, maxlen - strlen(sockaddr->sun_path) - 1); 391 } 392 393 if (!session_name) { 394 strncpy(buf, sockaddr->sun_path, sizeof buf); 395 session_name = basename(buf); 396 } 397 setenv("ABDUCO_SESSION", session_name, 1); 398 setenv("ABDUCO_SOCKET", sockaddr->sun_path, 1); 399 400 return true; 401 } 402 403 static bool create_session(const char *name, char * const argv[]) { 404 /* this uses the well known double fork strategy as described in section 1.7 of 405 * 406 * http://www.faqs.org/faqs/unix-faq/programmer/faq/ 407 * 408 * pipes are used for synchronization and error reporting i.e. the child sets 409 * the close on exec flag before calling execvp(3) the parent blocks on a read(2) 410 * in case of failure the error message is written to the pipe, success is 411 * indicated by EOF on the pipe. 412 */ 413 int client_pipe[2], server_pipe[2]; 414 pid_t pid; 415 char errormsg[255]; 416 struct sigaction sa; 417 418 if (session_exists(name)) { 419 errno = EADDRINUSE; 420 return false; 421 } 422 423 if (pipe(client_pipe) == -1) 424 return false; 425 if ((server.socket = server_create_socket(name)) == -1) 426 return false; 427 428 switch ((pid = fork())) { 429 case 0: /* child process */ 430 setsid(); 431 close(client_pipe[0]); 432 switch ((pid = fork())) { 433 case 0: /* child process */ 434 if (pipe(server_pipe) == -1) { 435 snprintf(errormsg, sizeof(errormsg), "server-pipe: %s\n", strerror(errno)); 436 write_all(client_pipe[1], errormsg, strlen(errormsg)); 437 close(client_pipe[1]); 438 _exit(EXIT_FAILURE); 439 } 440 sa.sa_flags = 0; 441 sigemptyset(&sa.sa_mask); 442 sa.sa_handler = server_pty_died_handler; 443 sigaction(SIGCHLD, &sa, NULL); 444 switch (server.pid = forkpty(&server.pty, NULL, has_term ? &server.term : NULL, &server.winsize)) { 445 case 0: /* child = user application process */ 446 close(server.socket); 447 close(server_pipe[0]); 448 if (fcntl(client_pipe[1], F_SETFD, FD_CLOEXEC) == 0 && 449 fcntl(server_pipe[1], F_SETFD, FD_CLOEXEC) == 0) 450 execvp(argv[0], argv); 451 snprintf(errormsg, sizeof(errormsg), "server-execvp: %s: %s\n", 452 argv[0], strerror(errno)); 453 write_all(client_pipe[1], errormsg, strlen(errormsg)); 454 write_all(server_pipe[1], errormsg, strlen(errormsg)); 455 close(client_pipe[1]); 456 close(server_pipe[1]); 457 _exit(EXIT_FAILURE); 458 break; 459 case -1: /* forkpty failed */ 460 snprintf(errormsg, sizeof(errormsg), "server-forkpty: %s\n", strerror(errno)); 461 write_all(client_pipe[1], errormsg, strlen(errormsg)); 462 close(client_pipe[1]); 463 close(server_pipe[0]); 464 close(server_pipe[1]); 465 _exit(EXIT_FAILURE); 466 break; 467 default: /* parent = server process */ 468 sa.sa_handler = server_sigterm_handler; 469 sigaction(SIGTERM, &sa, NULL); 470 sigaction(SIGINT, &sa, NULL); 471 sa.sa_handler = server_sigusr1_handler; 472 sigaction(SIGUSR1, &sa, NULL); 473 sa.sa_handler = SIG_IGN; 474 sigaction(SIGPIPE, &sa, NULL); 475 sigaction(SIGHUP, &sa, NULL); 476 chdir("/"); 477 #ifdef NDEBUG 478 int fd = open("/dev/null", O_RDWR); 479 if (fd != -1) { 480 dup2(fd, STDIN_FILENO); 481 dup2(fd, STDOUT_FILENO); 482 dup2(fd, STDERR_FILENO); 483 close(fd); 484 } 485 #endif /* NDEBUG */ 486 close(client_pipe[1]); 487 close(server_pipe[1]); 488 if (read_all(server_pipe[0], errormsg, sizeof(errormsg)) > 0) 489 _exit(EXIT_FAILURE); 490 close(server_pipe[0]); 491 server_mainloop(); 492 break; 493 } 494 break; 495 case -1: /* fork failed */ 496 snprintf(errormsg, sizeof(errormsg), "server-fork: %s\n", strerror(errno)); 497 write_all(client_pipe[1], errormsg, strlen(errormsg)); 498 close(client_pipe[1]); 499 _exit(EXIT_FAILURE); 500 break; 501 default: /* parent = intermediate process */ 502 close(client_pipe[1]); 503 _exit(EXIT_SUCCESS); 504 break; 505 } 506 break; 507 case -1: /* fork failed */ 508 close(client_pipe[0]); 509 close(client_pipe[1]); 510 return false; 511 default: /* parent = client process */ 512 close(client_pipe[1]); 513 int status; 514 wait(&status); /* wait for first fork */ 515 ssize_t len = read_all(client_pipe[0], errormsg, sizeof(errormsg)); 516 if (len > 0) { 517 write_all(STDERR_FILENO, errormsg, len); 518 unlink(sockaddr.sun_path); 519 exit(EXIT_FAILURE); 520 } 521 close(client_pipe[0]); 522 } 523 return true; 524 } 525 526 static bool attach_session(const char *name, const bool terminate) { 527 if (server.socket > 0) 528 close(server.socket); 529 if ((server.socket = session_connect(name)) == -1) 530 return false; 531 if (server_set_socket_non_blocking(server.socket) == -1) 532 return false; 533 534 struct sigaction sa; 535 sa.sa_flags = 0; 536 sigemptyset(&sa.sa_mask); 537 sa.sa_handler = client_sigwinch_handler; 538 sigaction(SIGWINCH, &sa, NULL); 539 sa.sa_handler = SIG_IGN; 540 sigaction(SIGPIPE, &sa, NULL); 541 542 client_setup_terminal(); 543 int status = client_mainloop(); 544 client_restore_terminal(); 545 if (status == -1) { 546 info("detached"); 547 } else if (status == 222) { 548 info("server has already a max priority client"); 549 } else if (status == -EIO) { 550 info("exited due to I/O errors"); 551 } else { 552 info("session terminated with exit status %d", status); 553 if (terminate) 554 exit(status); 555 } 556 557 return terminate; 558 } 559 560 static int session_filter(const struct dirent *d) { 561 return strstr(d->d_name, server.host) != NULL; 562 } 563 564 static int session_comparator(const struct dirent **a, const struct dirent **b) { 565 struct stat sa, sb; 566 if (stat((*a)->d_name, &sa) != 0) 567 return -1; 568 if (stat((*b)->d_name, &sb) != 0) 569 return 1; 570 return sa.st_atime < sb.st_atime ? -1 : 1; 571 } 572 573 static int list_session(void) { 574 if (!create_socket_dir(&sockaddr)) 575 return 1; 576 if (chdir(sockaddr.sun_path) == -1) 577 die("list-session"); 578 struct dirent **namelist; 579 int n = scandir(sockaddr.sun_path, &namelist, session_filter, session_comparator); 580 if (n < 0) 581 return 1; 582 printf("Active sessions (on host %s)\n", server.host+1); 583 while (n--) { 584 struct stat sb; char buf[255]; 585 if (stat(namelist[n]->d_name, &sb) == 0 && S_ISSOCK(sb.st_mode)) { 586 pid_t pid = 0; 587 strftime(buf, sizeof(buf), "%a%t %F %T", localtime(&sb.st_mtime)); 588 char status = ' '; 589 char *local = strstr(namelist[n]->d_name, server.host); 590 if (local) { 591 *local = '\0'; /* truncate hostname if we are local */ 592 if (!(pid = session_exists(namelist[n]->d_name))) 593 continue; 594 } 595 if (sb.st_mode & S_IXUSR) 596 status = '*'; 597 else if (sb.st_mode & S_IXGRP) 598 status = '+'; 599 printf("%c %s\t%jd\t%s\n", status, buf, (intmax_t)pid, namelist[n]->d_name); 600 } 601 free(namelist[n]); 602 } 603 free(namelist); 604 return 0; 605 } 606 607 int main(int argc, char *argv[]) { 608 int opt; 609 bool force = false; 610 char **cmd = NULL, action = '\0'; 611 612 char *default_cmd[4] = { "/bin/sh", "-c", getenv("ABDUCO_CMD"), NULL }; 613 if (!default_cmd[2]) { 614 default_cmd[0] = ABDUCO_CMD; 615 default_cmd[1] = NULL; 616 } 617 618 server.name = basename(argv[0]); 619 gethostname(server.host+1, sizeof(server.host) - 1); 620 621 while ((opt = getopt(argc, argv, "aAclmne:fpqrv")) != -1) { 622 switch (opt) { 623 case 'a': 624 case 'A': 625 case 'c': 626 case 'n': 627 action = opt; 628 break; 629 case 'e': 630 if (!optarg) 631 usage(); 632 if (optarg[0] == '^' && optarg[1]) 633 optarg[0] = CTRL(optarg[1]); 634 KEY_DETACH = optarg[0]; 635 break; 636 case 'f': 637 force = true; 638 break; 639 case 'p': 640 passthrough = true; 641 break; 642 case 'q': 643 quiet = true; 644 break; 645 case 'r': 646 client.flags |= CLIENT_READONLY; 647 break; 648 case 'l': 649 client.flags |= CLIENT_LOWPRIORITY; 650 break; 651 case 'm': 652 client.flags |= CLIENT_MAXPRIORITY; 653 break; 654 case 'v': 655 puts("abduco-"VERSION" © 2013-2018 Marc André Tanner"); 656 exit(EXIT_SUCCESS); 657 default: 658 usage(); 659 } 660 } 661 662 if ((client.flags & CLIENT_LOWPRIORITY) && (client.flags & CLIENT_MAXPRIORITY)) 663 usage(); 664 665 /* collect the session name if trailing args */ 666 if (optind < argc) 667 server.session_name = argv[optind]; 668 669 /* if yet more trailing arguments, they must be the command */ 670 if (optind + 1 < argc) 671 cmd = &argv[optind + 1]; 672 else 673 cmd = default_cmd; 674 675 if (server.session_name && !isatty(STDIN_FILENO)) 676 passthrough = true; 677 678 if (passthrough) { 679 if (!action) 680 action = 'a'; 681 quiet = true; 682 client.flags |= CLIENT_LOWPRIORITY; 683 } 684 685 if (!action && !server.session_name) 686 exit(list_session()); 687 if (!action || !server.session_name) 688 usage(); 689 690 if (!passthrough && tcgetattr(STDIN_FILENO, &orig_term) != -1) { 691 server.term = orig_term; 692 has_term = true; 693 } 694 695 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &server.winsize) == -1) { 696 server.winsize.ws_col = 80; 697 server.winsize.ws_row = 25; 698 } 699 700 server.read_pty = (action == 'n'); 701 702 redo: 703 switch (action) { 704 case 'n': 705 case 'c': 706 if (force) { 707 if (session_alive(server.session_name)) { 708 info("session exists and has not yet terminated"); 709 return 1; 710 } 711 if (session_exists(server.session_name)) 712 attach_session(server.session_name, false); 713 } 714 if (!create_session(server.session_name, cmd)) 715 die("create-session"); 716 if (action == 'n') 717 break; 718 case 'a': 719 if (!attach_session(server.session_name, true)) 720 die("attach-session"); 721 break; 722 case 'A': 723 if (session_alive(server.session_name)) { 724 if (!attach_session(server.session_name, true)) 725 die("attach-session"); 726 } else if (!attach_session(server.session_name, !force)) { 727 force = false; 728 action = 'c'; 729 goto redo; 730 } 731 break; 732 } 733 734 return 0; 735 }