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