1/* $Id: tmux.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 <errno.h> 23#include <event.h> 24#include <fcntl.h> 25#include <pwd.h> 26#include <stdlib.h> 27#include <string.h> 28#include <unistd.h> 29 30#include "tmux.h" 31 32#if defined(DEBUG) && defined(__OpenBSD__) 33extern char *malloc_options; 34#endif 35 36struct options global_options; /* server options */ 37struct options global_s_options; /* session options */ 38struct options global_w_options; /* window options */ 39struct environ global_environ; 40 41struct event_base *ev_base; 42 43char *cfg_file; 44char *shell_cmd; 45int debug_level; 46time_t start_time; 47char socket_path[MAXPATHLEN]; 48int login_shell; 49char *environ_path; 50pid_t environ_pid = -1; 51int environ_idx = -1; 52 53__dead void usage(void); 54void parseenvironment(void); 55char *makesocketpath(const char *); 56 57#ifndef HAVE___PROGNAME 58char *__progname = (char *) "tmux"; 59#endif 60 61__dead void 62usage(void) 63{ 64 fprintf(stderr, 65 "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L socket-name]\n" 66 " [-S socket-path] [command [flags]]\n", 67 __progname); 68 exit(1); 69} 70 71void 72logfile(const char *name) 73{ 74 char *path; 75 76 log_close(); 77 if (debug_level > 0) { 78 xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); 79 log_open_file(debug_level, path); 80 xfree(path); 81 } 82} 83 84const char * 85getshell(void) 86{ 87 struct passwd *pw; 88 const char *shell; 89 90 shell = getenv("SHELL"); 91 if (checkshell(shell)) 92 return (shell); 93 94 pw = getpwuid(getuid()); 95 if (pw != NULL && checkshell(pw->pw_shell)) 96 return (pw->pw_shell); 97 98 return (_PATH_BSHELL); 99} 100 101int 102checkshell(const char *shell) 103{ 104 if (shell == NULL || *shell == '\0' || areshell(shell)) 105 return (0); 106 if (access(shell, X_OK) != 0) 107 return (0); 108 return (1); 109} 110 111int 112areshell(const char *shell) 113{ 114 const char *progname, *ptr; 115 116 if ((ptr = strrchr(shell, '/')) != NULL) 117 ptr++; 118 else 119 ptr = shell; 120 progname = __progname; 121 if (*progname == '-') 122 progname++; 123 if (strcmp(ptr, progname) == 0) 124 return (1); 125 return (0); 126} 127 128void 129parseenvironment(void) 130{ 131 char *env, path[256]; 132 long pid; 133 int idx; 134 135 if ((env = getenv("TMUX")) == NULL) 136 return; 137 138 if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &idx) != 3) 139 return; 140 environ_path = xstrdup(path); 141 environ_pid = pid; 142 environ_idx = idx; 143} 144 145char * 146makesocketpath(const char *label) 147{ 148 char base[MAXPATHLEN], *path, *s; 149 struct stat sb; 150 u_int uid; 151 152 uid = getuid(); 153 if ((s = getenv("TMPDIR")) == NULL || *s == '\0') 154 xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); 155 else 156 xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 157 158 if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 159 return (NULL); 160 161 if (lstat(base, &sb) != 0) 162 return (NULL); 163 if (!S_ISDIR(sb.st_mode)) { 164 errno = ENOTDIR; 165 return (NULL); 166 } 167 if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { 168 errno = EACCES; 169 return (NULL); 170 } 171 172 xasprintf(&path, "%s/%s", base, label); 173 return (path); 174} 175 176void 177setblocking(int fd, int state) 178{ 179 int mode; 180 181 if ((mode = fcntl(fd, F_GETFL)) != -1) { 182 if (!state) 183 mode |= O_NONBLOCK; 184 else 185 mode &= ~O_NONBLOCK; 186 fcntl(fd, F_SETFL, mode); 187 } 188} 189 190__dead void 191shell_exec(const char *shell, const char *shellcmd) 192{ 193 const char *shellname, *ptr; 194 char *argv0; 195 196 ptr = strrchr(shell, '/'); 197 if (ptr != NULL && *(ptr + 1) != '\0') 198 shellname = ptr + 1; 199 else 200 shellname = shell; 201 if (login_shell) 202 xasprintf(&argv0, "-%s", shellname); 203 else 204 xasprintf(&argv0, "%s", shellname); 205 setenv("SHELL", shell, 1); 206 207 setblocking(STDIN_FILENO, 1); 208 setblocking(STDOUT_FILENO, 1); 209 setblocking(STDERR_FILENO, 1); 210 closefrom(STDERR_FILENO + 1); 211 212 execl(shell, argv0, "-c", shellcmd, (char *) NULL); 213 fatal("execl failed"); 214} 215 216int 217main(int argc, char **argv) 218{ 219 struct passwd *pw; 220 struct keylist *keylist; 221 char *s, *path, *label, *home, **var; 222 int opt, flags, quiet, keys; 223 224#if defined(DEBUG) && defined(__OpenBSD__) 225 malloc_options = (char *) "AFGJPX"; 226#endif 227 228 quiet = flags = 0; 229 label = path = NULL; 230 login_shell = (**argv == '-'); 231 while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUvV")) != -1) { 232 switch (opt) { 233 case '2': 234 flags |= IDENTIFY_256COLOURS; 235 flags &= ~IDENTIFY_88COLOURS; 236 break; 237 case '8': 238 flags |= IDENTIFY_88COLOURS; 239 flags &= ~IDENTIFY_256COLOURS; 240 break; 241 case 'c': 242 if (shell_cmd != NULL) 243 xfree(shell_cmd); 244 shell_cmd = xstrdup(optarg); 245 break; 246 case 'V': 247 printf("%s %s\n", __progname, VERSION); 248 exit(0); 249 case 'f': 250 if (cfg_file != NULL) 251 xfree(cfg_file); 252 cfg_file = xstrdup(optarg); 253 break; 254 case 'l': 255 login_shell = 1; 256 break; 257 case 'L': 258 if (label != NULL) 259 xfree(label); 260 label = xstrdup(optarg); 261 break; 262 case 'q': 263 quiet = 1; 264 break; 265 case 'S': 266 if (path != NULL) 267 xfree(path); 268 path = xstrdup(optarg); 269 break; 270 case 'u': 271 flags |= IDENTIFY_UTF8; 272 break; 273 case 'v': 274 debug_level++; 275 break; 276 default: 277 usage(); 278 } 279 } 280 argc -= optind; 281 argv += optind; 282 283 if (shell_cmd != NULL && argc != 0) 284 usage(); 285 286 log_open_tty(debug_level); 287 288 if (!(flags & IDENTIFY_UTF8)) { 289 /* 290 * If the user has set whichever of LC_ALL, LC_CTYPE or LANG 291 * exist (in that order) to contain UTF-8, it is a safe 292 * assumption that either they are using a UTF-8 terminal, or 293 * if not they know that output from UTF-8-capable programs may 294 * be wrong. 295 */ 296 if ((s = getenv("LC_ALL")) == NULL) { 297 if ((s = getenv("LC_CTYPE")) == NULL) 298 s = getenv("LANG"); 299 } 300 if (s != NULL && (strcasestr(s, "UTF-8") != NULL || 301 strcasestr(s, "UTF8") != NULL)) 302 flags |= IDENTIFY_UTF8; 303 } 304 305 environ_init(&global_environ); 306 for (var = environ; *var != NULL; var++) 307 environ_put(&global_environ, *var); 308 309 options_init(&global_options, NULL); 310 options_table_populate_tree(server_options_table, &global_options); 311 options_set_number(&global_options, "quiet", quiet); 312 313 options_init(&global_s_options, NULL); 314 options_table_populate_tree(session_options_table, &global_s_options); 315 options_set_string(&global_s_options, "default-shell", "%s", getshell()); 316 317 options_init(&global_w_options, NULL); 318 options_table_populate_tree(window_options_table, &global_w_options); 319 320 /* Set the prefix option (its a list, so not in the table). */ 321 keylist = xmalloc(sizeof *keylist); 322 ARRAY_INIT(keylist); 323 ARRAY_ADD(keylist, '\002'); 324 options_set_data(&global_s_options, "prefix", keylist, xfree); 325 326 /* Enable UTF-8 if the first client is on UTF-8 terminal. */ 327 if (flags & IDENTIFY_UTF8) { 328 options_set_number(&global_s_options, "status-utf8", 1); 329 options_set_number(&global_s_options, "mouse-utf8", 1); 330 options_set_number(&global_w_options, "utf8", 1); 331 } 332 333 /* Override keys to vi if VISUAL or EDITOR are set. */ 334 if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 335 if (strrchr(s, '/') != NULL) 336 s = strrchr(s, '/') + 1; 337 if (strstr(s, "vi") != NULL) 338 keys = MODEKEY_VI; 339 else 340 keys = MODEKEY_EMACS; 341 options_set_number(&global_s_options, "status-keys", keys); 342 options_set_number(&global_w_options, "mode-keys", keys); 343 } 344 345 /* Locate the configuration file. */ 346 if (cfg_file == NULL) { 347 home = getenv("HOME"); 348 if (home == NULL || *home == '\0') { 349 pw = getpwuid(getuid()); 350 if (pw != NULL) 351 home = pw->pw_dir; 352 } 353 xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); 354 if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { 355 xfree(cfg_file); 356 cfg_file = NULL; 357 } 358 } 359 360 /* 361 * Figure out the socket path. If specified on the command-line with -S 362 * or -L, use it, otherwise try $TMUX or assume -L default. 363 */ 364 parseenvironment(); 365 if (path == NULL) { 366 /* If no -L, use the environment. */ 367 if (label == NULL) { 368 if (environ_path != NULL) 369 path = xstrdup(environ_path); 370 else 371 label = xstrdup("default"); 372 } 373 374 /* -L or default set. */ 375 if (label != NULL) { 376 if ((path = makesocketpath(label)) == NULL) { 377 log_warn("can't create socket"); 378 exit(1); 379 } 380 } 381 } 382 if (label != NULL) 383 xfree(label); 384 if (realpath(path, socket_path) == NULL) 385 strlcpy(socket_path, path, sizeof socket_path); 386 xfree(path); 387 388#ifdef HAVE_SETPROCTITLE 389 /* Set process title. */ 390 setproctitle("%s (%s)", __progname, socket_path); 391#endif 392 393 /* Pass control to the client. */ 394 ev_base = osdep_event_init(); 395 exit(client_main(argc, argv, flags)); 396} 397