Annotation of src/usr.bin/tmux/tmux.c, Revision 1.1
1.1 ! nicm 1: /* $OpenBSD$ */
! 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 <paths.h>
! 24: #include <pwd.h>
! 25: #include <signal.h>
! 26: #include <stdlib.h>
! 27: #include <string.h>
! 28: #include <syslog.h>
! 29: #include <unistd.h>
! 30:
! 31: #include "tmux.h"
! 32:
! 33: #ifdef DEBUG
! 34: const char *malloc_options = "AFGJPX";
! 35: #endif
! 36:
! 37: volatile sig_atomic_t sigwinch;
! 38: volatile sig_atomic_t sigterm;
! 39: volatile sig_atomic_t sigcont;
! 40: volatile sig_atomic_t sigchld;
! 41: volatile sig_atomic_t sigusr1;
! 42: volatile sig_atomic_t sigusr2;
! 43:
! 44: char *cfg_file;
! 45: struct options global_options;
! 46: struct options global_window_options;
! 47:
! 48: int server_locked;
! 49: char *server_password;
! 50: time_t server_activity;
! 51:
! 52: int debug_level;
! 53: int be_quiet;
! 54: time_t start_time;
! 55: char *socket_path;
! 56:
! 57: __dead void usage(void);
! 58: char *makesockpath(const char *);
! 59:
! 60: __dead void
! 61: usage(void)
! 62: {
! 63: fprintf(stderr, "usage: %s [-28dqUuVv] [-f file] "
! 64: "[-L socket-name] [-S socket-path] [command [flags]]\n",
! 65: __progname);
! 66: exit(1);
! 67: }
! 68:
! 69: void
! 70: logfile(const char *name)
! 71: {
! 72: char *path;
! 73:
! 74: log_close();
! 75: if (debug_level > 0) {
! 76: xasprintf(
! 77: &path, "%s-%s-%ld.log", __progname, name, (long) getpid());
! 78: log_open_file(debug_level, path);
! 79: xfree(path);
! 80: }
! 81: }
! 82:
! 83: void
! 84: sighandler(int sig)
! 85: {
! 86: int saved_errno;
! 87:
! 88: saved_errno = errno;
! 89: switch (sig) {
! 90: case SIGWINCH:
! 91: sigwinch = 1;
! 92: break;
! 93: case SIGTERM:
! 94: sigterm = 1;
! 95: break;
! 96: case SIGCHLD:
! 97: sigchld = 1;
! 98: break;
! 99: case SIGCONT:
! 100: sigcont = 1;
! 101: break;
! 102: case SIGUSR1:
! 103: sigusr1 = 1;
! 104: break;
! 105: case SIGUSR2:
! 106: sigusr2 = 1;
! 107: break;
! 108: }
! 109: errno = saved_errno;
! 110: }
! 111:
! 112: void
! 113: siginit(void)
! 114: {
! 115: struct sigaction act;
! 116:
! 117: memset(&act, 0, sizeof act);
! 118: sigemptyset(&act.sa_mask);
! 119: act.sa_flags = SA_RESTART;
! 120:
! 121: act.sa_handler = SIG_IGN;
! 122: if (sigaction(SIGPIPE, &act, NULL) != 0)
! 123: fatal("sigaction failed");
! 124: if (sigaction(SIGINT, &act, NULL) != 0)
! 125: fatal("sigaction failed");
! 126: if (sigaction(SIGTSTP, &act, NULL) != 0)
! 127: fatal("sigaction failed");
! 128: if (sigaction(SIGQUIT, &act, NULL) != 0)
! 129: fatal("sigaction failed");
! 130:
! 131: act.sa_handler = sighandler;
! 132: if (sigaction(SIGWINCH, &act, NULL) != 0)
! 133: fatal("sigaction failed");
! 134: if (sigaction(SIGTERM, &act, NULL) != 0)
! 135: fatal("sigaction failed");
! 136: if (sigaction(SIGCHLD, &act, NULL) != 0)
! 137: fatal("sigaction failed");
! 138: if (sigaction(SIGUSR1, &act, NULL) != 0)
! 139: fatal("sigaction failed");
! 140: if (sigaction(SIGUSR2, &act, NULL) != 0)
! 141: fatal("sigaction failed");
! 142: }
! 143:
! 144: void
! 145: sigreset(void)
! 146: {
! 147: struct sigaction act;
! 148:
! 149: memset(&act, 0, sizeof act);
! 150: sigemptyset(&act.sa_mask);
! 151:
! 152: act.sa_handler = SIG_DFL;
! 153: if (sigaction(SIGPIPE, &act, NULL) != 0)
! 154: fatal("sigaction failed");
! 155: if (sigaction(SIGUSR1, &act, NULL) != 0)
! 156: fatal("sigaction failed");
! 157: if (sigaction(SIGUSR2, &act, NULL) != 0)
! 158: fatal("sigaction failed");
! 159: if (sigaction(SIGINT, &act, NULL) != 0)
! 160: fatal("sigaction failed");
! 161: if (sigaction(SIGTSTP, &act, NULL) != 0)
! 162: fatal("sigaction failed");
! 163: if (sigaction(SIGQUIT, &act, NULL) != 0)
! 164: fatal("sigaction failed");
! 165: if (sigaction(SIGWINCH, &act, NULL) != 0)
! 166: fatal("sigaction failed");
! 167: if (sigaction(SIGTERM, &act, NULL) != 0)
! 168: fatal("sigaction failed");
! 169: if (sigaction(SIGCHLD, &act, NULL) != 0)
! 170: fatal("sigaction failed");
! 171: }
! 172:
! 173: char *
! 174: makesockpath(const char *label)
! 175: {
! 176: char base[MAXPATHLEN], *path;
! 177: struct stat sb;
! 178: u_int uid;
! 179:
! 180: uid = getuid();
! 181: xsnprintf(base, MAXPATHLEN, "%s/%s-%d", _PATH_TMP, __progname, uid);
! 182:
! 183: if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
! 184: return (NULL);
! 185:
! 186: if (lstat(base, &sb) != 0)
! 187: return (NULL);
! 188: if (!S_ISDIR(sb.st_mode)) {
! 189: errno = ENOTDIR;
! 190: return (NULL);
! 191: }
! 192: if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) {
! 193: errno = EACCES;
! 194: return (NULL);
! 195: }
! 196:
! 197: xasprintf(&path, "%s/%s", base, label);
! 198: return (path);
! 199: }
! 200:
! 201: int
! 202: main(int argc, char **argv)
! 203: {
! 204: struct client_ctx cctx;
! 205: struct msg_command_data cmddata;
! 206: struct buffer *b;
! 207: struct cmd_list *cmdlist;
! 208: struct cmd *cmd;
! 209: struct pollfd pfd;
! 210: struct hdr hdr;
! 211: const char *shell;
! 212: struct passwd *pw;
! 213: char *s, *path, *label, *cause, *home, *pass = NULL;
! 214: char cwd[MAXPATHLEN];
! 215: int retcode, opt, flags, unlock, start_server;
! 216:
! 217: unlock = flags = 0;
! 218: label = path = NULL;
! 219: while ((opt = getopt(argc, argv, "28df:L:qS:uUv")) != -1) {
! 220: switch (opt) {
! 221: case '2':
! 222: flags |= IDENTIFY_256COLOURS;
! 223: flags &= ~IDENTIFY_88COLOURS;
! 224: break;
! 225: case '8':
! 226: flags |= IDENTIFY_88COLOURS;
! 227: flags &= ~IDENTIFY_256COLOURS;
! 228: break;
! 229: case 'f':
! 230: cfg_file = xstrdup(optarg);
! 231: break;
! 232: case 'L':
! 233: if (path != NULL) {
! 234: log_warnx("-L and -S cannot be used together");
! 235: exit(1);
! 236: }
! 237: if (label != NULL)
! 238: xfree(label);
! 239: label = xstrdup(optarg);
! 240: break;
! 241: case 'S':
! 242: if (label != NULL) {
! 243: log_warnx("-L and -S cannot be used together");
! 244: exit(1);
! 245: }
! 246: if (path != NULL)
! 247: xfree(path);
! 248: path = xstrdup(optarg);
! 249: break;
! 250: case 'q':
! 251: be_quiet = 1;
! 252: break;
! 253: case 'u':
! 254: flags |= IDENTIFY_UTF8;
! 255: break;
! 256: case 'U':
! 257: unlock = 1;
! 258: break;
! 259: case 'd':
! 260: flags |= IDENTIFY_HASDEFAULTS;
! 261: break;
! 262: case 'v':
! 263: debug_level++;
! 264: break;
! 265: default:
! 266: usage();
! 267: }
! 268: }
! 269: argc -= optind;
! 270: argv += optind;
! 271:
! 272: log_open_tty(debug_level);
! 273: siginit();
! 274:
! 275: options_init(&global_options, NULL);
! 276: options_set_number(&global_options, "bell-action", BELL_ANY);
! 277: options_set_number(&global_options, "buffer-limit", 9);
! 278: options_set_number(&global_options, "display-time", 750);
! 279: options_set_number(&global_options, "history-limit", 2000);
! 280: options_set_number(&global_options, "lock-after-time", 0);
! 281: options_set_number(&global_options, "message-attr", GRID_ATTR_REVERSE);
! 282: options_set_number(&global_options, "message-bg", 3);
! 283: options_set_number(&global_options, "message-fg", 0);
! 284: options_set_number(&global_options, "prefix", '\002');
! 285: options_set_number(&global_options, "repeat-time", 500);
! 286: options_set_number(&global_options, "set-remain-on-exit", 0);
! 287: options_set_number(&global_options, "set-titles", 1);
! 288: options_set_number(&global_options, "status", 1);
! 289: options_set_number(&global_options, "status-attr", GRID_ATTR_REVERSE);
! 290: options_set_number(&global_options, "status-bg", 2);
! 291: options_set_number(&global_options, "status-fg", 0);
! 292: options_set_number(&global_options, "status-interval", 15);
! 293: options_set_number(&global_options, "status-keys", MODEKEY_EMACS);
! 294: options_set_number(&global_options, "status-left-length", 10);
! 295: options_set_number(&global_options, "status-right-length", 40);
! 296: options_set_string(&global_options, "status-left", "[#S]");
! 297: options_set_string(
! 298: &global_options, "status-right", "\"#24T\" %%H:%%M %%d-%%b-%%y");
! 299:
! 300: options_init(&global_window_options, NULL);
! 301: options_set_number(&global_window_options, "aggressive-resize", 0);
! 302: options_set_number(&global_window_options, "automatic-rename", 1);
! 303: options_set_number(&global_window_options, "clock-mode-colour", 4);
! 304: options_set_number(&global_window_options, "clock-mode-style", 1);
! 305: options_set_number(&global_window_options, "force-height", 0);
! 306: options_set_number(&global_window_options, "force-width", 0);
! 307: options_set_number(
! 308: &global_window_options, "mode-attr", GRID_ATTR_REVERSE);
! 309: options_set_number(&global_window_options, "main-pane-width", 81);
! 310: options_set_number(&global_window_options, "main-pane-height", 24);
! 311: options_set_number(&global_window_options, "mode-bg", 3);
! 312: options_set_number(&global_window_options, "mode-fg", 0);
! 313: options_set_number(&global_window_options, "mode-keys", MODEKEY_EMACS);
! 314: options_set_number(&global_window_options, "monitor-activity", 0);
! 315: options_set_string(&global_window_options, "monitor-content", "%s", "");
! 316: options_set_number(&global_window_options, "utf8", 0);
! 317: options_set_number(&global_window_options, "window-status-attr", 0);
! 318: options_set_number(&global_window_options, "window-status-bg", 8);
! 319: options_set_number(&global_window_options, "window-status-fg", 8);
! 320: options_set_number(&global_window_options, "xterm-keys", 0);
! 321: options_set_number(&global_window_options, "remain-on-exit", 0);
! 322:
! 323: if (!(flags & IDENTIFY_UTF8)) {
! 324: /*
! 325: * If the user has set LANG to contain UTF-8, it is a safe
! 326: * assumption that either they are using a UTF-8 terminal, or
! 327: * if not they know that output from UTF-8-capable programs may
! 328: * be wrong.
! 329: */
! 330: if ((s = getenv("LANG")) != NULL && strstr(s, "UTF-8") != NULL)
! 331: flags |= IDENTIFY_UTF8;
! 332: }
! 333:
! 334: if (cfg_file == NULL) {
! 335: home = getenv("HOME");
! 336: if (home == NULL || *home == '\0') {
! 337: pw = getpwuid(getuid());
! 338: if (pw != NULL)
! 339: home = pw->pw_dir;
! 340: }
! 341: xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG);
! 342: if (access(cfg_file, R_OK) != 0) {
! 343: xfree(cfg_file);
! 344: cfg_file = NULL;
! 345: }
! 346: } else {
! 347: if (access(cfg_file, R_OK) != 0) {
! 348: log_warn("%s", cfg_file);
! 349: exit(1);
! 350: }
! 351: }
! 352:
! 353: if (label == NULL)
! 354: label = xstrdup("default");
! 355: if (path == NULL && (path = makesockpath(label)) == NULL) {
! 356: log_warn("can't create socket");
! 357: exit(1);
! 358: }
! 359: xfree(label);
! 360:
! 361: shell = getenv("SHELL");
! 362: if (shell == NULL || *shell == '\0') {
! 363: pw = getpwuid(getuid());
! 364: if (pw != NULL)
! 365: shell = pw->pw_shell;
! 366: if (shell == NULL || *shell == '\0')
! 367: shell = _PATH_BSHELL;
! 368: }
! 369: options_set_string(
! 370: &global_options, "default-command", "exec %s", shell);
! 371:
! 372: if (getcwd(cwd, sizeof cwd) == NULL) {
! 373: log_warn("getcwd");
! 374: exit(1);
! 375: }
! 376: options_set_string(&global_options, "default-path", "%s", cwd);
! 377:
! 378: if (unlock) {
! 379: if (argc != 0) {
! 380: log_warnx("can't specify a command when unlocking");
! 381: exit(1);
! 382: }
! 383: cmdlist = NULL;
! 384: if ((pass = getpass("Password: ")) == NULL)
! 385: exit(1);
! 386: start_server = 0;
! 387: } else {
! 388: if (argc == 0) {
! 389: cmd = xmalloc(sizeof *cmd);
! 390: cmd->entry = &cmd_new_session_entry;
! 391: cmd->entry->init(cmd, 0);
! 392:
! 393: cmdlist = xmalloc(sizeof *cmdlist);
! 394: TAILQ_INIT(cmdlist);
! 395: TAILQ_INSERT_HEAD(cmdlist, cmd, qentry);
! 396: } else {
! 397: cmdlist = cmd_list_parse(argc, argv, &cause);
! 398: if (cmdlist == NULL) {
! 399: log_warnx("%s", cause);
! 400: exit(1);
! 401: }
! 402: }
! 403: start_server = 0;
! 404: TAILQ_FOREACH(cmd, cmdlist, qentry) {
! 405: if (cmd->entry->flags & CMD_STARTSERVER) {
! 406: start_server = 1;
! 407: break;
! 408: }
! 409: }
! 410: }
! 411:
! 412: memset(&cctx, 0, sizeof cctx);
! 413: if (client_init(path, &cctx, start_server, flags) != 0)
! 414: exit(1);
! 415: xfree(path);
! 416:
! 417: b = buffer_create(BUFSIZ);
! 418: if (unlock) {
! 419: cmd_send_string(b, pass);
! 420: client_write_server(
! 421: &cctx, MSG_UNLOCK, BUFFER_OUT(b), BUFFER_USED(b));
! 422: } else {
! 423: cmd_list_send(cmdlist, b);
! 424: cmd_list_free(cmdlist);
! 425: client_fill_session(&cmddata);
! 426: client_write_server2(&cctx, MSG_COMMAND,
! 427: &cmddata, sizeof cmddata, BUFFER_OUT(b), BUFFER_USED(b));
! 428: }
! 429: buffer_destroy(b);
! 430:
! 431: retcode = 0;
! 432: for (;;) {
! 433: pfd.fd = cctx.srv_fd;
! 434: pfd.events = POLLIN;
! 435: if (BUFFER_USED(cctx.srv_out) > 0)
! 436: pfd.events |= POLLOUT;
! 437:
! 438: if (poll(&pfd, 1, INFTIM) == -1) {
! 439: if (errno == EAGAIN || errno == EINTR)
! 440: continue;
! 441: fatal("poll failed");
! 442: }
! 443:
! 444: if (buffer_poll(&pfd, cctx.srv_in, cctx.srv_out) != 0)
! 445: goto out;
! 446:
! 447: restart:
! 448: if (BUFFER_USED(cctx.srv_in) < sizeof hdr)
! 449: continue;
! 450: memcpy(&hdr, BUFFER_OUT(cctx.srv_in), sizeof hdr);
! 451: if (BUFFER_USED(cctx.srv_in) < (sizeof hdr) + hdr.size)
! 452: continue;
! 453: buffer_remove(cctx.srv_in, sizeof hdr);
! 454:
! 455: switch (hdr.type) {
! 456: case MSG_EXIT:
! 457: case MSG_SHUTDOWN:
! 458: goto out;
! 459: case MSG_ERROR:
! 460: retcode = 1;
! 461: /* FALLTHROUGH */
! 462: case MSG_PRINT:
! 463: if (hdr.size > INT_MAX - 1)
! 464: fatalx("bad MSG_PRINT size");
! 465: log_info("%.*s",
! 466: (int) hdr.size, BUFFER_OUT(cctx.srv_in));
! 467: if (hdr.size != 0)
! 468: buffer_remove(cctx.srv_in, hdr.size);
! 469: goto restart;
! 470: case MSG_READY:
! 471: retcode = client_main(&cctx);
! 472: goto out;
! 473: default:
! 474: fatalx("unexpected command");
! 475: }
! 476: }
! 477:
! 478: out:
! 479: options_free(&global_options);
! 480: options_free(&global_window_options);
! 481:
! 482: close(cctx.srv_fd);
! 483: buffer_destroy(cctx.srv_in);
! 484: buffer_destroy(cctx.srv_out);
! 485:
! 486: return (retcode);
! 487: }