tmux.c revision 1.145
1121326Sharti/* $OpenBSD: tmux.c,v 1.145 2015/10/23 16:07:29 nicm Exp $ */ 2121326Sharti 3121326Sharti/* 4121326Sharti * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 5121326Sharti * 6121326Sharti * Permission to use, copy, modify, and distribute this software for any 7121326Sharti * purpose with or without fee is hereby granted, provided that the above 8121326Sharti * copyright notice and this permission notice appear in all copies. 9121326Sharti * 10121326Sharti * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11121326Sharti * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12121326Sharti * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13121326Sharti * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14121326Sharti * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15121326Sharti * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16121326Sharti * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17121326Sharti */ 18121326Sharti 19121326Sharti#include <sys/types.h> 20121326Sharti#include <sys/stat.h> 21121326Sharti 22121326Sharti#include <err.h> 23121326Sharti#include <errno.h> 24121326Sharti#include <event.h> 25121326Sharti#include <fcntl.h> 26121326Sharti#include <getopt.h> 27121326Sharti#include <locale.h> 28121326Sharti#include <paths.h> 29131826Sharti#include <pwd.h> 30121326Sharti#include <stdlib.h> 31121326Sharti#include <string.h> 32121326Sharti#include <unistd.h> 33121326Sharti 34121326Sharti#include "tmux.h" 35121326Sharti 36121326Sharti#ifdef DEBUG 37121326Shartiextern char *malloc_options; 38121326Sharti#endif 39121326Sharti 40121326Shartistruct options global_options; /* server options */ 41121326Shartistruct options global_s_options; /* session options */ 42121326Shartistruct options global_w_options; /* window options */ 43121326Shartistruct environ global_environ; 44121326Sharti 45121326Shartichar *shell_cmd; 46121326Shartiint debug_level; 47121326Shartitime_t start_time; 48121326Shartichar socket_path[PATH_MAX]; 49121326Sharti 50121326Sharti__dead void usage(void); 51121326Shartichar *makesocketpath(const char *); 52121326Sharti 53121326Sharti__dead void 54121326Shartiusage(void) 55121326Sharti{ 56121326Sharti fprintf(stderr, 57121326Sharti "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" 58121326Sharti " [-S socket-path] [command [flags]]\n", 59121326Sharti __progname); 60121326Sharti exit(1); 61121326Sharti} 62121326Sharti 63121326Shartivoid 64121326Shartilogfile(const char *name) 65121326Sharti{ 66121326Sharti char *path; 67121326Sharti 68121326Sharti if (debug_level > 0) { 69121326Sharti xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); 70121326Sharti log_open(path); 71121326Sharti free(path); 72121326Sharti } 73121326Sharti} 74 75const char * 76getshell(void) 77{ 78 struct passwd *pw; 79 const char *shell; 80 81 shell = getenv("SHELL"); 82 if (checkshell(shell)) 83 return (shell); 84 85 pw = getpwuid(getuid()); 86 if (pw != NULL && checkshell(pw->pw_shell)) 87 return (pw->pw_shell); 88 89 return (_PATH_BSHELL); 90} 91 92int 93checkshell(const char *shell) 94{ 95 if (shell == NULL || *shell == '\0' || *shell != '/') 96 return (0); 97 if (areshell(shell)) 98 return (0); 99 if (access(shell, X_OK) != 0) 100 return (0); 101 return (1); 102} 103 104int 105areshell(const char *shell) 106{ 107 const char *progname, *ptr; 108 109 if ((ptr = strrchr(shell, '/')) != NULL) 110 ptr++; 111 else 112 ptr = shell; 113 progname = __progname; 114 if (*progname == '-') 115 progname++; 116 if (strcmp(ptr, progname) == 0) 117 return (1); 118 return (0); 119} 120 121char * 122makesocketpath(const char *label) 123{ 124 char base[PATH_MAX], realbase[PATH_MAX], *path, *s; 125 struct stat sb; 126 u_int uid; 127 128 uid = getuid(); 129 if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') 130 xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 131 else if ((s = getenv("TMPDIR")) != NULL && *s != '\0') 132 xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 133 else 134 xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); 135 136 if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 137 return (NULL); 138 139 if (lstat(base, &sb) != 0) 140 return (NULL); 141 if (!S_ISDIR(sb.st_mode)) { 142 errno = ENOTDIR; 143 return (NULL); 144 } 145 if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { 146 errno = EACCES; 147 return (NULL); 148 } 149 150 if (realpath(base, realbase) == NULL) 151 strlcpy(realbase, base, sizeof realbase); 152 153 xasprintf(&path, "%s/%s", realbase, label); 154 return (path); 155} 156 157void 158setblocking(int fd, int state) 159{ 160 int mode; 161 162 if ((mode = fcntl(fd, F_GETFL)) != -1) { 163 if (!state) 164 mode |= O_NONBLOCK; 165 else 166 mode &= ~O_NONBLOCK; 167 fcntl(fd, F_SETFL, mode); 168 } 169} 170 171const char * 172find_home(void) 173{ 174 struct passwd *pw; 175 static const char *home; 176 177 if (home != NULL) 178 return (home); 179 180 home = getenv("HOME"); 181 if (home == NULL || *home == '\0') { 182 pw = getpwuid(getuid()); 183 if (pw != NULL) 184 home = pw->pw_dir; 185 else 186 home = NULL; 187 } 188 189 return (home); 190} 191 192int 193main(int argc, char **argv) 194{ 195 char *s, *path, *label, **var, tmp[PATH_MAX]; 196 int opt, flags, keys; 197 198#ifdef DEBUG 199 malloc_options = (char *) "AFGJPX"; 200#endif 201 202 setlocale(LC_TIME, ""); 203 tzset(); 204 205 if (**argv == '-') 206 flags = CLIENT_LOGIN; 207 else 208 flags = 0; 209 210 label = path = NULL; 211 while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { 212 switch (opt) { 213 case '2': 214 flags |= CLIENT_256COLOURS; 215 break; 216 case 'c': 217 free(shell_cmd); 218 shell_cmd = xstrdup(optarg); 219 break; 220 case 'C': 221 if (flags & CLIENT_CONTROL) 222 flags |= CLIENT_CONTROLCONTROL; 223 else 224 flags |= CLIENT_CONTROL; 225 break; 226 case 'f': 227 set_cfg_file(optarg); 228 break; 229 case 'l': 230 flags |= CLIENT_LOGIN; 231 break; 232 case 'L': 233 free(label); 234 label = xstrdup(optarg); 235 break; 236 case 'q': 237 break; 238 case 'S': 239 free(path); 240 path = xstrdup(optarg); 241 break; 242 case 'u': 243 flags |= CLIENT_UTF8; 244 break; 245 case 'v': 246 debug_level++; 247 break; 248 default: 249 usage(); 250 } 251 } 252 argc -= optind; 253 argv += optind; 254 255 if (shell_cmd != NULL && argc != 0) 256 usage(); 257 258 if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " 259 "proc exec tty ps", NULL) != 0) 260 err(1, "pledge"); 261 262 if (!(flags & CLIENT_UTF8)) { 263 /* 264 * If the user has set whichever of LC_ALL, LC_CTYPE or LANG 265 * exist (in that order) to contain UTF-8, it is a safe 266 * assumption that either they are using a UTF-8 terminal, or 267 * if not they know that output from UTF-8-capable programs may 268 * be wrong. 269 */ 270 if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { 271 if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') 272 s = getenv("LANG"); 273 } 274 if (s != NULL && (strcasestr(s, "UTF-8") != NULL || 275 strcasestr(s, "UTF8") != NULL)) 276 flags |= CLIENT_UTF8; 277 } 278 279 environ_init(&global_environ); 280 for (var = environ; *var != NULL; var++) 281 environ_put(&global_environ, *var); 282 if (getcwd(tmp, sizeof tmp) != NULL) 283 environ_set(&global_environ, "PWD", tmp); 284 285 options_init(&global_options, NULL); 286 options_table_populate_tree(server_options_table, &global_options); 287 288 options_init(&global_s_options, NULL); 289 options_table_populate_tree(session_options_table, &global_s_options); 290 options_set_string(&global_s_options, "default-shell", "%s", 291 getshell()); 292 293 options_init(&global_w_options, NULL); 294 options_table_populate_tree(window_options_table, &global_w_options); 295 296 /* Enable UTF-8 if the first client is on UTF-8 terminal. */ 297 if (flags & CLIENT_UTF8) { 298 options_set_number(&global_s_options, "status-utf8", 1); 299 options_set_number(&global_s_options, "mouse-utf8", 1); 300 options_set_number(&global_w_options, "utf8", 1); 301 } 302 303 /* Override keys to vi if VISUAL or EDITOR are set. */ 304 if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 305 if (strrchr(s, '/') != NULL) 306 s = strrchr(s, '/') + 1; 307 if (strstr(s, "vi") != NULL) 308 keys = MODEKEY_VI; 309 else 310 keys = MODEKEY_EMACS; 311 options_set_number(&global_s_options, "status-keys", keys); 312 options_set_number(&global_w_options, "mode-keys", keys); 313 } 314 315 /* 316 * Figure out the socket path. If specified on the command-line with -S 317 * or -L, use it, otherwise try $TMUX or assume -L default. 318 */ 319 if (path == NULL) { 320 /* If no -L, use the environment. */ 321 if (label == NULL) { 322 s = getenv("TMUX"); 323 if (s != NULL) { 324 path = xstrdup(s); 325 path[strcspn (path, ",")] = '\0'; 326 if (*path == '\0') { 327 free(path); 328 label = xstrdup("default"); 329 } 330 } else 331 label = xstrdup("default"); 332 } 333 334 /* -L or default set. */ 335 if (label != NULL) { 336 if ((path = makesocketpath(label)) == NULL) { 337 fprintf(stderr, "can't create socket: %s\n", 338 strerror(errno)); 339 exit(1); 340 } 341 } 342 } 343 free(label); 344 345 if (strlcpy(socket_path, path, sizeof socket_path) >= 346 sizeof socket_path) { 347 fprintf(stderr, "socket path too long: %s\n", path); 348 exit(1); 349 } 350 free(path); 351 352 /* Set process title. */ 353 setproctitle("%s (%s)", __progname, socket_path); 354 355 /* Pass control to the client. */ 356 exit(client_main(event_init(), argc, argv, flags)); 357} 358