abduco

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }