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