1 /* $OpenBSD: tmux.c,v 1.162 2015/11/24 23:46:15 nicm 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/stat.h> 21 22 #include <err.h> 23 #include <errno.h> 24 #include <event.h> 25 #include <fcntl.h> 26 #include <getopt.h> 27 #include <locale.h> 28 #include <paths.h> 29 #include <pwd.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <time.h> 33 #include <unistd.h> 34 35 #include "tmux.h" 36 37 struct options *global_options; /* server options */ 38 struct options *global_s_options; /* session options */ 39 struct options *global_w_options; /* window options */ 40 struct environ *global_environ; 41 42 struct timeval start_time; 43 const char *socket_path; 44 45 __dead void usage(void); 46 static char *make_label(const char *); 47 48 __dead void 49 usage(void) 50 { 51 fprintf(stderr, 52 "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" 53 " [-S socket-path] [command [flags]]\n", 54 __progname); 55 exit(1); 56 } 57 58 const char * 59 getshell(void) 60 { 61 struct passwd *pw; 62 const char *shell; 63 64 shell = getenv("SHELL"); 65 if (checkshell(shell)) 66 return (shell); 67 68 pw = getpwuid(getuid()); 69 if (pw != NULL && checkshell(pw->pw_shell)) 70 return (pw->pw_shell); 71 72 return (_PATH_BSHELL); 73 } 74 75 int 76 checkshell(const char *shell) 77 { 78 if (shell == NULL || *shell == '\0' || *shell != '/') 79 return (0); 80 if (areshell(shell)) 81 return (0); 82 if (access(shell, X_OK) != 0) 83 return (0); 84 return (1); 85 } 86 87 int 88 areshell(const char *shell) 89 { 90 const char *progname, *ptr; 91 92 if ((ptr = strrchr(shell, '/')) != NULL) 93 ptr++; 94 else 95 ptr = shell; 96 progname = __progname; 97 if (*progname == '-') 98 progname++; 99 if (strcmp(ptr, progname) == 0) 100 return (1); 101 return (0); 102 } 103 104 static char * 105 make_label(const char *label) 106 { 107 char *base, resolved[PATH_MAX], *path, *s; 108 struct stat sb; 109 u_int uid; 110 int saved_errno; 111 112 if (label == NULL) 113 label = "default"; 114 115 uid = getuid(); 116 117 if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') 118 xasprintf(&base, "%s/tmux-%u", s, uid); 119 else 120 xasprintf(&base, "%s/tmux-%u", _PATH_TMP, uid); 121 122 if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 123 goto fail; 124 125 if (lstat(base, &sb) != 0) 126 goto fail; 127 if (!S_ISDIR(sb.st_mode)) { 128 errno = ENOTDIR; 129 goto fail; 130 } 131 if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { 132 errno = EACCES; 133 goto fail; 134 } 135 136 if (realpath(base, resolved) == NULL) 137 strlcpy(resolved, base, sizeof resolved); 138 xasprintf(&path, "%s/%s", resolved, label); 139 return (path); 140 141 fail: 142 saved_errno = errno; 143 free(base); 144 errno = saved_errno; 145 return (NULL); 146 } 147 148 void 149 setblocking(int fd, int state) 150 { 151 int mode; 152 153 if ((mode = fcntl(fd, F_GETFL)) != -1) { 154 if (!state) 155 mode |= O_NONBLOCK; 156 else 157 mode &= ~O_NONBLOCK; 158 fcntl(fd, F_SETFL, mode); 159 } 160 } 161 162 const char * 163 find_home(void) 164 { 165 struct passwd *pw; 166 static const char *home; 167 168 if (home != NULL) 169 return (home); 170 171 home = getenv("HOME"); 172 if (home == NULL || *home == '\0') { 173 pw = getpwuid(getuid()); 174 if (pw != NULL) 175 home = pw->pw_dir; 176 else 177 home = NULL; 178 } 179 180 return (home); 181 } 182 183 int 184 main(int argc, char **argv) 185 { 186 char *path, *label, **var, tmp[PATH_MAX], *shellcmd = NULL; 187 const char *s; 188 int opt, flags, keys; 189 190 setlocale(LC_TIME, ""); 191 tzset(); 192 193 if (**argv == '-') 194 flags = CLIENT_LOGIN; 195 else 196 flags = 0; 197 198 label = path = NULL; 199 while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { 200 switch (opt) { 201 case '2': 202 flags |= CLIENT_256COLOURS; 203 break; 204 case 'c': 205 free(shellcmd); 206 shellcmd = xstrdup(optarg); 207 break; 208 case 'C': 209 if (flags & CLIENT_CONTROL) 210 flags |= CLIENT_CONTROLCONTROL; 211 else 212 flags |= CLIENT_CONTROL; 213 break; 214 case 'f': 215 set_cfg_file(optarg); 216 break; 217 case 'l': 218 flags |= CLIENT_LOGIN; 219 break; 220 case 'L': 221 free(label); 222 label = xstrdup(optarg); 223 break; 224 case 'q': 225 break; 226 case 'S': 227 free(path); 228 path = xstrdup(optarg); 229 break; 230 case 'u': 231 flags |= CLIENT_UTF8; 232 break; 233 case 'v': 234 log_add_level(); 235 break; 236 default: 237 usage(); 238 } 239 } 240 argc -= optind; 241 argv += optind; 242 243 if (shellcmd != NULL && argc != 0) 244 usage(); 245 246 if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " 247 "recvfd proc exec tty ps", NULL) != 0) 248 err(1, "pledge"); 249 250 /* 251 * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. 252 * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain 253 * UTF-8, it is a safe assumption that either they are using a UTF-8 254 * terminal, or if not they know that output from UTF-8-capable 255 * programs may be wrong. 256 */ 257 if (getenv("TMUX") != NULL) 258 flags |= CLIENT_UTF8; 259 else { 260 s = getenv("LC_ALL"); 261 if (s == NULL || *s == '\0') 262 s = getenv("LC_CTYPE"); 263 if (s == NULL || *s == '\0') 264 s = getenv("LANG"); 265 if (s == NULL || *s == '\0') 266 s = ""; 267 if (strcasestr(s, "UTF-8") != NULL || 268 strcasestr(s, "UTF8") != NULL) 269 flags |= CLIENT_UTF8; 270 } 271 272 global_environ = environ_create(); 273 for (var = environ; *var != NULL; var++) 274 environ_put(global_environ, *var); 275 if (getcwd(tmp, sizeof tmp) != NULL) 276 environ_set(global_environ, "PWD", "%s", tmp); 277 278 global_options = options_create(NULL); 279 options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); 280 281 global_s_options = options_create(NULL); 282 options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); 283 options_set_string(global_s_options, "default-shell", "%s", getshell()); 284 285 global_w_options = options_create(NULL); 286 options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); 287 288 /* Override keys to vi if VISUAL or EDITOR are set. */ 289 if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 290 if (strrchr(s, '/') != NULL) 291 s = strrchr(s, '/') + 1; 292 if (strstr(s, "vi") != NULL) 293 keys = MODEKEY_VI; 294 else 295 keys = MODEKEY_EMACS; 296 options_set_number(global_s_options, "status-keys", keys); 297 options_set_number(global_w_options, "mode-keys", keys); 298 } 299 300 /* 301 * If socket is specified on the command-line with -S or -L, it is 302 * used. Otherwise, $TMUX is checked and if that fails "default" is 303 * used. 304 */ 305 if (path == NULL && label == NULL) { 306 s = getenv("TMUX"); 307 if (s != NULL && *s != '\0' && *s != ',') { 308 path = xstrdup(s); 309 path[strcspn (path, ",")] = '\0'; 310 } 311 } 312 if (path == NULL && (path = make_label(label)) == NULL) { 313 fprintf(stderr, "can't create socket: %s\n", strerror(errno)); 314 exit(1); 315 } 316 socket_path = path; 317 free(label); 318 319 /* Pass control to the client. */ 320 exit(client_main(event_init(), argc, argv, flags, shellcmd)); 321 } 322