1*41c58c32Snicm /* $OpenBSD: tmux.c,v 1.122 2013/10/05 10:40:49 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 4311827fbSnicm * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm #include <sys/stat.h> 21311827fbSnicm 22311827fbSnicm #include <errno.h> 237724d0b0Snicm #include <event.h> 243808d149Snicm #include <fcntl.h> 25622ef518Snicm #include <locale.h> 26311827fbSnicm #include <paths.h> 27311827fbSnicm #include <pwd.h> 28311827fbSnicm #include <stdlib.h> 29311827fbSnicm #include <string.h> 30311827fbSnicm #include <unistd.h> 31311827fbSnicm 32311827fbSnicm #include "tmux.h" 33311827fbSnicm 34311827fbSnicm #ifdef DEBUG 35bbc36c02Snicm extern char *malloc_options; 36311827fbSnicm #endif 37311827fbSnicm 38f79ec119Snicm struct options global_options; /* server options */ 39eaecedb2Snicm struct options global_s_options; /* session options */ 40eaecedb2Snicm struct options global_w_options; /* window options */ 416f7d62ebSnicm struct environ global_environ; 42311827fbSnicm 434291359cSnicm struct event_base *ev_base; 444291359cSnicm 4565439d22Snicm char *cfg_file; 4665439d22Snicm char *shell_cmd; 47311827fbSnicm int debug_level; 48311827fbSnicm time_t start_time; 4965439d22Snicm char socket_path[MAXPATHLEN]; 5065b1f011Snicm int login_shell; 5165439d22Snicm char *environ_path; 5207bd7befSnicm pid_t environ_pid = -1; 530a539d5dSnicm int environ_session_id = -1; 544ccba3b3Snicm 55311827fbSnicm __dead void usage(void); 5665439d22Snicm void parseenvironment(void); 5765439d22Snicm char *makesocketpath(const char *); 587724d0b0Snicm 59311827fbSnicm __dead void 60311827fbSnicm usage(void) 61311827fbSnicm { 626ca2ce32Ssobrado fprintf(stderr, 6304924df5Snicm "usage: %s [-28lquv] [-c shell-command] [-f file] [-L socket-name]\n" 6465b1f011Snicm " [-S socket-path] [command [flags]]\n", 65311827fbSnicm __progname); 66311827fbSnicm exit(1); 67311827fbSnicm } 68311827fbSnicm 69311827fbSnicm void 70311827fbSnicm logfile(const char *name) 71311827fbSnicm { 72311827fbSnicm char *path; 73311827fbSnicm 74311827fbSnicm if (debug_level > 0) { 753a4fea3bSnicm xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); 7636fa3172Snicm log_open(debug_level, path); 777d053cf9Snicm free(path); 78311827fbSnicm } 79311827fbSnicm } 80311827fbSnicm 813a5ec08bSnicm const char * 823a5ec08bSnicm getshell(void) 833a5ec08bSnicm { 843a5ec08bSnicm struct passwd *pw; 853a5ec08bSnicm const char *shell; 863a5ec08bSnicm 873a5ec08bSnicm shell = getenv("SHELL"); 883a5ec08bSnicm if (checkshell(shell)) 893a5ec08bSnicm return (shell); 903a5ec08bSnicm 913a5ec08bSnicm pw = getpwuid(getuid()); 923a5ec08bSnicm if (pw != NULL && checkshell(pw->pw_shell)) 933a5ec08bSnicm return (pw->pw_shell); 943a5ec08bSnicm 953a5ec08bSnicm return (_PATH_BSHELL); 963a5ec08bSnicm } 973a5ec08bSnicm 983a5ec08bSnicm int 993a5ec08bSnicm checkshell(const char *shell) 1003a5ec08bSnicm { 101a89a0ceeSnicm if (shell == NULL || *shell == '\0' || *shell != '/') 102a89a0ceeSnicm return (0); 103a89a0ceeSnicm if (areshell(shell)) 1043a5ec08bSnicm return (0); 1053a5ec08bSnicm if (access(shell, X_OK) != 0) 1063a5ec08bSnicm return (0); 1073a5ec08bSnicm return (1); 1083a5ec08bSnicm } 1093a5ec08bSnicm 1103a5ec08bSnicm int 1113a5ec08bSnicm areshell(const char *shell) 1123a5ec08bSnicm { 1133a5ec08bSnicm const char *progname, *ptr; 1143a5ec08bSnicm 1153a5ec08bSnicm if ((ptr = strrchr(shell, '/')) != NULL) 1163a5ec08bSnicm ptr++; 1173a5ec08bSnicm else 1183a5ec08bSnicm ptr = shell; 1193a5ec08bSnicm progname = __progname; 1203a5ec08bSnicm if (*progname == '-') 1213a5ec08bSnicm progname++; 1223a5ec08bSnicm if (strcmp(ptr, progname) == 0) 1233a5ec08bSnicm return (1); 1243a5ec08bSnicm return (0); 1253a5ec08bSnicm } 1263a5ec08bSnicm 12735a092d6Snicm const char* 12835a092d6Snicm get_full_path(const char *wd, const char *path) 12935a092d6Snicm { 130c4a631faSnicm int fd; 13135a092d6Snicm static char newpath[MAXPATHLEN]; 132*41c58c32Snicm const char *retval; 13335a092d6Snicm 134c4a631faSnicm fd = open(".", O_RDONLY); 135c4a631faSnicm if (fd == -1) 13635a092d6Snicm return (NULL); 137c4a631faSnicm 138*41c58c32Snicm retval = NULL; 139*41c58c32Snicm if (chdir(wd) == 0) { 140*41c58c32Snicm if (realpath(path, newpath) == 0) 141*41c58c32Snicm retval = newpath; 142*41c58c32Snicm } 143c4a631faSnicm 144c4a631faSnicm if (fchdir(fd) != 0) 145c4a631faSnicm chdir("/"); 146c4a631faSnicm close(fd); 147c4a631faSnicm 148*41c58c32Snicm return (retval); 14935a092d6Snicm } 15035a092d6Snicm 151a2f71d82Snicm void 15265439d22Snicm parseenvironment(void) 153a2f71d82Snicm { 15407bd7befSnicm char *env, path[256]; 15507bd7befSnicm long pid; 1560a539d5dSnicm int id; 157a2f71d82Snicm 158a2f71d82Snicm if ((env = getenv("TMUX")) == NULL) 159a2f71d82Snicm return; 160a2f71d82Snicm 1610a539d5dSnicm if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3) 162a2f71d82Snicm return; 16307bd7befSnicm environ_path = xstrdup(path); 16407bd7befSnicm environ_pid = pid; 1650a539d5dSnicm environ_session_id = id; 166a2f71d82Snicm } 167a2f71d82Snicm 168311827fbSnicm char * 16965439d22Snicm makesocketpath(const char *label) 170311827fbSnicm { 17135194355Snicm char base[MAXPATHLEN], realbase[MAXPATHLEN], *path, *s; 172311827fbSnicm struct stat sb; 173311827fbSnicm u_int uid; 174311827fbSnicm 175311827fbSnicm uid = getuid(); 17640b64c41Snicm if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') 17740b64c41Snicm xsnprintf(base, sizeof base, "%s/", s); 17840b64c41Snicm else if ((s = getenv("TMPDIR")) != NULL && *s != '\0') 179e3b883c9Snicm xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 18040b64c41Snicm else 18140b64c41Snicm xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); 182311827fbSnicm 183311827fbSnicm if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 184311827fbSnicm return (NULL); 185311827fbSnicm 186311827fbSnicm if (lstat(base, &sb) != 0) 187311827fbSnicm return (NULL); 188311827fbSnicm if (!S_ISDIR(sb.st_mode)) { 189311827fbSnicm errno = ENOTDIR; 190311827fbSnicm return (NULL); 191311827fbSnicm } 192311827fbSnicm if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { 193311827fbSnicm errno = EACCES; 194311827fbSnicm return (NULL); 195311827fbSnicm } 196311827fbSnicm 19735194355Snicm if (realpath(base, realbase) == NULL) 19835194355Snicm strlcpy(realbase, base, sizeof realbase); 19935194355Snicm 20035194355Snicm xasprintf(&path, "%s/%s", realbase, label); 201311827fbSnicm return (path); 202311827fbSnicm } 203311827fbSnicm 204a0ee4254Snicm void 205a0ee4254Snicm setblocking(int fd, int state) 206a0ee4254Snicm { 207a0ee4254Snicm int mode; 208a0ee4254Snicm 209a0ee4254Snicm if ((mode = fcntl(fd, F_GETFL)) != -1) { 210a0ee4254Snicm if (!state) 211a0ee4254Snicm mode |= O_NONBLOCK; 212a0ee4254Snicm else 213a0ee4254Snicm mode &= ~O_NONBLOCK; 214a0ee4254Snicm fcntl(fd, F_SETFL, mode); 215a0ee4254Snicm } 216a0ee4254Snicm } 217a0ee4254Snicm 2187724d0b0Snicm __dead void 2197724d0b0Snicm shell_exec(const char *shell, const char *shellcmd) 2207724d0b0Snicm { 2217724d0b0Snicm const char *shellname, *ptr; 2227724d0b0Snicm char *argv0; 2237724d0b0Snicm 2247724d0b0Snicm ptr = strrchr(shell, '/'); 2257724d0b0Snicm if (ptr != NULL && *(ptr + 1) != '\0') 2267724d0b0Snicm shellname = ptr + 1; 2277724d0b0Snicm else 2287724d0b0Snicm shellname = shell; 2297724d0b0Snicm if (login_shell) 2307724d0b0Snicm xasprintf(&argv0, "-%s", shellname); 2317724d0b0Snicm else 2327724d0b0Snicm xasprintf(&argv0, "%s", shellname); 2337724d0b0Snicm setenv("SHELL", shell, 1); 2347724d0b0Snicm 235a0ee4254Snicm setblocking(STDIN_FILENO, 1); 236a0ee4254Snicm setblocking(STDOUT_FILENO, 1); 237a0ee4254Snicm setblocking(STDERR_FILENO, 1); 238498ee386Snicm closefrom(STDERR_FILENO + 1); 239498ee386Snicm 2407724d0b0Snicm execl(shell, argv0, "-c", shellcmd, (char *) NULL); 2417724d0b0Snicm fatal("execl failed"); 2427724d0b0Snicm } 2437724d0b0Snicm 244311827fbSnicm int 245311827fbSnicm main(int argc, char **argv) 246311827fbSnicm { 247311827fbSnicm struct passwd *pw; 24865439d22Snicm char *s, *path, *label, *home, **var; 249c9347692Snicm int opt, flags, quiet, keys; 250311827fbSnicm 251bbc36c02Snicm #ifdef DEBUG 252bbc36c02Snicm malloc_options = (char *) "AFGJPX"; 253bbc36c02Snicm #endif 254bbc36c02Snicm 255622ef518Snicm setlocale(LC_TIME, ""); 256622ef518Snicm 257c9347692Snicm quiet = flags = 0; 25865439d22Snicm label = path = NULL; 25965b1f011Snicm login_shell = (**argv == '-'); 2600e3eb495Snicm while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { 261311827fbSnicm switch (opt) { 262311827fbSnicm case '2': 263311827fbSnicm flags |= IDENTIFY_256COLOURS; 264311827fbSnicm break; 265ef8ef121Snicm case 'c': 2667d053cf9Snicm free(shell_cmd); 26765439d22Snicm shell_cmd = xstrdup(optarg); 268ef8ef121Snicm break; 26990896992Snicm case 'C': 27090896992Snicm if (flags & IDENTIFY_CONTROL) 27190896992Snicm flags |= IDENTIFY_TERMIOS; 27290896992Snicm else 27390896992Snicm flags |= IDENTIFY_CONTROL; 27490896992Snicm break; 275311827fbSnicm case 'f': 2767d053cf9Snicm free(cfg_file); 277311827fbSnicm cfg_file = xstrdup(optarg); 278311827fbSnicm break; 27965b1f011Snicm case 'l': 28065b1f011Snicm login_shell = 1; 28165b1f011Snicm break; 282311827fbSnicm case 'L': 2837d053cf9Snicm free(label); 284311827fbSnicm label = xstrdup(optarg); 285311827fbSnicm break; 286481abfa9Snicm case 'q': 287f79ec119Snicm quiet = 1; 288481abfa9Snicm break; 289311827fbSnicm case 'S': 2907d053cf9Snicm free(path); 291311827fbSnicm path = xstrdup(optarg); 292311827fbSnicm break; 293311827fbSnicm case 'u': 294311827fbSnicm flags |= IDENTIFY_UTF8; 295311827fbSnicm break; 296311827fbSnicm case 'v': 297311827fbSnicm debug_level++; 298311827fbSnicm break; 299311827fbSnicm default: 300311827fbSnicm usage(); 301311827fbSnicm } 302311827fbSnicm } 303311827fbSnicm argc -= optind; 304311827fbSnicm argv += optind; 305311827fbSnicm 30665439d22Snicm if (shell_cmd != NULL && argc != 0) 307ef8ef121Snicm usage(); 308ef8ef121Snicm 309461e0674Snicm if (!(flags & IDENTIFY_UTF8)) { 310461e0674Snicm /* 311461e0674Snicm * If the user has set whichever of LC_ALL, LC_CTYPE or LANG 312461e0674Snicm * exist (in that order) to contain UTF-8, it is a safe 313461e0674Snicm * assumption that either they are using a UTF-8 terminal, or 314461e0674Snicm * if not they know that output from UTF-8-capable programs may 315461e0674Snicm * be wrong. 316461e0674Snicm */ 31754810b57Snicm if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { 31854810b57Snicm if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') 319461e0674Snicm s = getenv("LANG"); 320461e0674Snicm } 321a7a86d62Snicm if (s != NULL && (strcasestr(s, "UTF-8") != NULL || 322a7a86d62Snicm strcasestr(s, "UTF8") != NULL)) 323461e0674Snicm flags |= IDENTIFY_UTF8; 324461e0674Snicm } 325461e0674Snicm 326bd0e97c4Snicm environ_init(&global_environ); 327bd0e97c4Snicm for (var = environ; *var != NULL; var++) 328bd0e97c4Snicm environ_put(&global_environ, *var); 329bd0e97c4Snicm 330f79ec119Snicm options_init(&global_options, NULL); 3313affa6cbSnicm options_table_populate_tree(server_options_table, &global_options); 3323affa6cbSnicm options_set_number(&global_options, "quiet", quiet); 333f79ec119Snicm 334eaecedb2Snicm options_init(&global_s_options, NULL); 3353affa6cbSnicm options_table_populate_tree(session_options_table, &global_s_options); 3363affa6cbSnicm options_set_string(&global_s_options, "default-shell", "%s", getshell()); 337311827fbSnicm 3383affa6cbSnicm options_init(&global_w_options, NULL); 3393affa6cbSnicm options_table_populate_tree(window_options_table, &global_w_options); 3403affa6cbSnicm 3413affa6cbSnicm /* Enable UTF-8 if the first client is on UTF-8 terminal. */ 342bd0e97c4Snicm if (flags & IDENTIFY_UTF8) { 3433affa6cbSnicm options_set_number(&global_s_options, "status-utf8", 1); 344699995ddSnicm options_set_number(&global_s_options, "mouse-utf8", 1); 3453affa6cbSnicm options_set_number(&global_w_options, "utf8", 1); 346bd0e97c4Snicm } 347bd0e97c4Snicm 3483affa6cbSnicm /* Override keys to vi if VISUAL or EDITOR are set. */ 349c9347692Snicm if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 350c9347692Snicm if (strrchr(s, '/') != NULL) 351c9347692Snicm s = strrchr(s, '/') + 1; 352c9347692Snicm if (strstr(s, "vi") != NULL) 353c9347692Snicm keys = MODEKEY_VI; 3543affa6cbSnicm else 3553affa6cbSnicm keys = MODEKEY_EMACS; 3563affa6cbSnicm options_set_number(&global_s_options, "status-keys", keys); 3573affa6cbSnicm options_set_number(&global_w_options, "mode-keys", keys); 358c9347692Snicm } 359c9347692Snicm 36065439d22Snicm /* Locate the configuration file. */ 361311827fbSnicm if (cfg_file == NULL) { 362311827fbSnicm home = getenv("HOME"); 363311827fbSnicm if (home == NULL || *home == '\0') { 364311827fbSnicm pw = getpwuid(getuid()); 365311827fbSnicm if (pw != NULL) 366311827fbSnicm home = pw->pw_dir; 367311827fbSnicm } 368ebf82b56Snicm xasprintf(&cfg_file, "%s/.tmux.conf", home); 3699a53e128Snicm if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { 3707d053cf9Snicm free(cfg_file); 371311827fbSnicm cfg_file = NULL; 372311827fbSnicm } 373311827fbSnicm } 374311827fbSnicm 3752fa50cfcSnicm /* 37665439d22Snicm * Figure out the socket path. If specified on the command-line with -S 37765439d22Snicm * or -L, use it, otherwise try $TMUX or assume -L default. 3782fa50cfcSnicm */ 37965439d22Snicm parseenvironment(); 3802fa50cfcSnicm if (path == NULL) { 38165439d22Snicm /* If no -L, use the environment. */ 3822fa50cfcSnicm if (label == NULL) { 38365439d22Snicm if (environ_path != NULL) 38465439d22Snicm path = xstrdup(environ_path); 38565439d22Snicm else 386311827fbSnicm label = xstrdup("default"); 3872fa50cfcSnicm } 3882fa50cfcSnicm 3892fa50cfcSnicm /* -L or default set. */ 3902fa50cfcSnicm if (label != NULL) { 39165439d22Snicm if ((path = makesocketpath(label)) == NULL) { 39236fa3172Snicm fprintf(stderr, "can't create socket\n"); 393311827fbSnicm exit(1); 394311827fbSnicm } 3952fa50cfcSnicm } 3962fa50cfcSnicm } 3977d053cf9Snicm free(label); 39865439d22Snicm strlcpy(socket_path, path, sizeof socket_path); 3997d053cf9Snicm free(path); 400311827fbSnicm 40165439d22Snicm /* Set process title. */ 40265439d22Snicm setproctitle("%s (%s)", __progname, socket_path); 403311827fbSnicm 40465439d22Snicm /* Pass control to the client. */ 40565439d22Snicm ev_base = event_init(); 40665439d22Snicm exit(client_main(argc, argv, flags)); 407fd234c13Snicm } 408