Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 682 lines (600 sloc) 16.799 kb
8fade26 Imported Upstream version 1.1
Karl Ferdinand Ebert authored
1 /* $Id: tmux.c,v 1.184 2009/11/04 23:09:09 tcunha 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 <pwd.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <unistd.h>
29
30 #include "tmux.h"
31
32 #if defined(DEBUG) && defined(__OpenBSD__)
33 extern char *malloc_options;
34 #endif
35
36 volatile sig_atomic_t sigwinch;
37 volatile sig_atomic_t sigterm;
38 volatile sig_atomic_t sigcont;
39 volatile sig_atomic_t sigchld;
40 volatile sig_atomic_t sigusr1;
41 volatile sig_atomic_t sigusr2;
42
43 char *cfg_file;
44 struct options global_s_options; /* session options */
45 struct options global_w_options; /* window options */
46 struct environ global_environ;
47
48 int debug_level;
49 int be_quiet;
50 time_t start_time;
51 char *socket_path;
52 int login_shell;
53
54 __dead void usage(void);
55 void fill_session(struct msg_command_data *);
56 char *makesockpath(const char *);
57 int dispatch_imsg(struct imsgbuf *, const char *, int *);
58 __dead void shell_exec(const char *, const char *);
59
60 #ifndef HAVE_PROGNAME
61 char *__progname = (char *) "tmux";
62 #endif
63
64 __dead void
65 usage(void)
66 {
67 fprintf(stderr,
68 "usage: %s [-28lquv] [-c shell-command] [-f file] [-L socket-name]\n"
69 " [-S socket-path] [command [flags]]\n",
70 __progname);
71 exit(1);
72 }
73
74 void
75 logfile(const char *name)
76 {
77 char *path;
78
79 log_close();
80 if (debug_level > 0) {
81 xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid());
82 log_open_file(debug_level, path);
83 xfree(path);
84 }
85 }
86
87 void
88 sighandler(int sig)
89 {
90 int saved_errno;
91
92 saved_errno = errno;
93 switch (sig) {
94 case SIGWINCH:
95 sigwinch = 1;
96 break;
97 case SIGTERM:
98 sigterm = 1;
99 break;
100 case SIGCHLD:
101 sigchld = 1;
102 break;
103 case SIGCONT:
104 sigcont = 1;
105 break;
106 case SIGUSR1:
107 sigusr1 = 1;
108 break;
109 case SIGUSR2:
110 sigusr2 = 1;
111 break;
112 }
113 errno = saved_errno;
114 }
115
116 void
117 siginit(void)
118 {
119 struct sigaction act;
120
121 memset(&act, 0, sizeof act);
122 sigemptyset(&act.sa_mask);
123 act.sa_flags = SA_RESTART;
124
125 act.sa_handler = SIG_IGN;
126 if (sigaction(SIGPIPE, &act, NULL) != 0)
127 fatal("sigaction failed");
128 if (sigaction(SIGINT, &act, NULL) != 0)
129 fatal("sigaction failed");
130 if (sigaction(SIGTSTP, &act, NULL) != 0)
131 fatal("sigaction failed");
132 if (sigaction(SIGQUIT, &act, NULL) != 0)
133 fatal("sigaction failed");
134
135 act.sa_handler = sighandler;
136 if (sigaction(SIGWINCH, &act, NULL) != 0)
137 fatal("sigaction failed");
138 if (sigaction(SIGTERM, &act, NULL) != 0)
139 fatal("sigaction failed");
140 if (sigaction(SIGCHLD, &act, NULL) != 0)
141 fatal("sigaction failed");
142 if (sigaction(SIGUSR1, &act, NULL) != 0)
143 fatal("sigaction failed");
144 if (sigaction(SIGUSR2, &act, NULL) != 0)
145 fatal("sigaction failed");
146 }
147
148 void
149 sigreset(void)
150 {
151 struct sigaction act;
152
153 memset(&act, 0, sizeof act);
154 sigemptyset(&act.sa_mask);
155
156 act.sa_handler = SIG_DFL;
157 if (sigaction(SIGPIPE, &act, NULL) != 0)
158 fatal("sigaction failed");
159 if (sigaction(SIGUSR1, &act, NULL) != 0)
160 fatal("sigaction failed");
161 if (sigaction(SIGUSR2, &act, NULL) != 0)
162 fatal("sigaction failed");
163 if (sigaction(SIGINT, &act, NULL) != 0)
164 fatal("sigaction failed");
165 if (sigaction(SIGTSTP, &act, NULL) != 0)
166 fatal("sigaction failed");
167 if (sigaction(SIGQUIT, &act, NULL) != 0)
168 fatal("sigaction failed");
169 if (sigaction(SIGWINCH, &act, NULL) != 0)
170 fatal("sigaction failed");
171 if (sigaction(SIGTERM, &act, NULL) != 0)
172 fatal("sigaction failed");
173 if (sigaction(SIGCHLD, &act, NULL) != 0)
174 fatal("sigaction failed");
175 }
176
177 const char *
178 getshell(void)
179 {
180 struct passwd *pw;
181 const char *shell;
182
183 shell = getenv("SHELL");
184 if (checkshell(shell))
185 return (shell);
186
187 pw = getpwuid(getuid());
188 if (pw != NULL && checkshell(pw->pw_shell))
189 return (pw->pw_shell);
190
191 return (_PATH_BSHELL);
192 }
193
194 int
195 checkshell(const char *shell)
196 {
197 if (shell == NULL || *shell == '\0' || areshell(shell))
198 return (0);
199 if (access(shell, X_OK) != 0)
200 return (0);
201 return (1);
202 }
203
204 int
205 areshell(const char *shell)
206 {
207 const char *progname, *ptr;
208
209 if ((ptr = strrchr(shell, '/')) != NULL)
210 ptr++;
211 else
212 ptr = shell;
213 progname = __progname;
214 if (*progname == '-')
215 progname++;
216 if (strcmp(ptr, progname) == 0)
217 return (1);
218 return (0);
219 }
220
221 void
222 fill_session(struct msg_command_data *data)
223 {
224 char *env, *ptr1, *ptr2, buf[256];
225 size_t len;
226 const char *errstr;
227 long long ll;
228
229 data->pid = -1;
230 if ((env = getenv("TMUX")) == NULL)
231 return;
232
233 if ((ptr2 = strrchr(env, ',')) == NULL || ptr2 == env)
234 return;
235 for (ptr1 = ptr2 - 1; ptr1 > env && *ptr1 != ','; ptr1--)
236 ;
237 if (*ptr1 != ',')
238 return;
239 ptr1++;
240 ptr2++;
241
242 len = ptr2 - ptr1 - 1;
243 if (len > (sizeof buf) - 1)
244 return;
245 memcpy(buf, ptr1, len);
246 buf[len] = '\0';
247
248 ll = strtonum(buf, 0, LONG_MAX, &errstr);
249 if (errstr != NULL)
250 return;
251 data->pid = ll;
252
253 ll = strtonum(ptr2, 0, UINT_MAX, &errstr);
254 if (errstr != NULL)
255 return;
256 data->idx = ll;
257 }
258
259 char *
260 makesockpath(const char *label)
261 {
262 char base[MAXPATHLEN], *path;
263 struct stat sb;
264 u_int uid;
265
266 uid = getuid();
267 xsnprintf(base, MAXPATHLEN, "%s/tmux-%d", _PATH_TMP, uid);
268
269 if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
270 return (NULL);
271
272 if (lstat(base, &sb) != 0)
273 return (NULL);
274 if (!S_ISDIR(sb.st_mode)) {
275 errno = ENOTDIR;
276 return (NULL);
277 }
278 if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) {
279 errno = EACCES;
280 return (NULL);
281 }
282
283 xasprintf(&path, "%s/%s", base, label);
284 return (path);
285 }
286
287 int
288 main(int argc, char **argv)
289 {
290 struct cmd_list *cmdlist;
291 struct cmd *cmd;
292 struct pollfd pfd;
293 enum msgtype msg;
294 struct passwd *pw;
295 struct options *so, *wo;
296 struct keylist *keylist;
297 struct imsgbuf *ibuf;
298 struct msg_command_data cmddata;
299 char *s, *shellcmd, *path, *label, *home, *cause;
300 char cwd[MAXPATHLEN], **var;
301 void *buf;
302 size_t len;
303 int nfds, retcode, opt, flags, cmdflags = 0;
304
305 #if defined(DEBUG) && defined(__OpenBSD__)
306 malloc_options = (char *) "AFGJPX";
307 #endif
308
309 flags = 0;
310 shellcmd = label = path = NULL;
311 login_shell = (**argv == '-');
312 while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUv")) != -1) {
313 switch (opt) {
314 case '2':
315 flags |= IDENTIFY_256COLOURS;
316 flags &= ~IDENTIFY_88COLOURS;
317 break;
318 case '8':
319 flags |= IDENTIFY_88COLOURS;
320 flags &= ~IDENTIFY_256COLOURS;
321 break;
322 case 'c':
323 if (shellcmd != NULL)
324 xfree(shellcmd);
325 shellcmd = xstrdup(optarg);
326 break;
327 case 'f':
328 if (cfg_file != NULL)
329 xfree(cfg_file);
330 cfg_file = xstrdup(optarg);
331 break;
332 case 'l':
333 login_shell = 1;
334 break;
335 case 'L':
336 if (label != NULL)
337 xfree(label);
338 label = xstrdup(optarg);
339 break;
340 case 'q':
341 be_quiet = 1;
342 break;
343 case 'S':
344 if (path != NULL)
345 xfree(path);
346 path = xstrdup(optarg);
347 break;
348 case 'u':
349 flags |= IDENTIFY_UTF8;
350 break;
351 case 'v':
352 debug_level++;
353 break;
354 default:
355 usage();
356 }
357 }
358 argc -= optind;
359 argv += optind;
360
361 if (shellcmd != NULL && argc != 0)
362 usage();
363
364 log_open_tty(debug_level);
365 siginit();
366
367 if (!(flags & IDENTIFY_UTF8)) {
368 /*
369 * If the user has set whichever of LC_ALL, LC_CTYPE or LANG
370 * exist (in that order) to contain UTF-8, it is a safe
371 * assumption that either they are using a UTF-8 terminal, or
372 * if not they know that output from UTF-8-capable programs may
373 * be wrong.
374 */
375 if ((s = getenv("LC_ALL")) == NULL) {
376 if ((s = getenv("LC_CTYPE")) == NULL)
377 s = getenv("LANG");
378 }
379 if (s != NULL && (strcasestr(s, "UTF-8") != NULL ||
380 strcasestr(s, "UTF8") != NULL))
381 flags |= IDENTIFY_UTF8;
382 }
383
384 environ_init(&global_environ);
385 for (var = environ; *var != NULL; var++)
386 environ_put(&global_environ, *var);
387
388 options_init(&global_s_options, NULL);
389 so = &global_s_options;
390 options_set_number(so, "base-index", 0);
391 options_set_number(so, "bell-action", BELL_ANY);
392 options_set_number(so, "buffer-limit", 9);
393 options_set_string(so, "default-command", "%s", "");
394 options_set_string(so, "default-shell", "%s", getshell());
395 options_set_string(so, "default-terminal", "screen");
396 options_set_number(so, "display-panes-colour", 4);
397 options_set_number(so, "display-panes-time", 1000);
398 options_set_number(so, "display-time", 750);
399 options_set_number(so, "history-limit", 2000);
400 options_set_number(so, "lock-after-time", 0);
401 options_set_string(so, "lock-command", "lock -np");
402 options_set_number(so, "lock-server", 1);
403 options_set_number(so, "message-attr", 0);
404 options_set_number(so, "message-bg", 3);
405 options_set_number(so, "message-fg", 0);
406 options_set_number(so, "mouse-select-pane", 0);
407 options_set_number(so, "repeat-time", 500);
408 options_set_number(so, "set-remain-on-exit", 0);
409 options_set_number(so, "set-titles", 0);
410 options_set_string(so, "set-titles-string", "#S:#I:#W - \"#T\"");
411 options_set_number(so, "status", 1);
412 options_set_number(so, "status-attr", 0);
413 options_set_number(so, "status-bg", 2);
414 options_set_number(so, "status-fg", 0);
415 options_set_number(so, "status-interval", 15);
416 options_set_number(so, "status-justify", 0);
417 options_set_number(so, "status-keys", MODEKEY_EMACS);
418 options_set_string(so, "status-left", "[#S]");
419 options_set_number(so, "status-left-attr", 0);
420 options_set_number(so, "status-left-bg", 8);
421 options_set_number(so, "status-left-fg", 8);
422 options_set_number(so, "status-left-length", 10);
423 options_set_string(so, "status-right", "\"#22T\" %%H:%%M %%d-%%b-%%y");
424 options_set_number(so, "status-right-attr", 0);
425 options_set_number(so, "status-right-bg", 8);
426 options_set_number(so, "status-right-fg", 8);
427 options_set_number(so, "status-right-length", 40);
428 options_set_string(so, "terminal-overrides",
429 "*88col*:colors=88,*256col*:colors=256");
430 options_set_string(so, "update-environment", "DISPLAY "
431 "WINDOWID SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION");
432 options_set_number(so, "visual-activity", 0);
433 options_set_number(so, "visual-bell", 0);
434 options_set_number(so, "visual-content", 0);
435
436 keylist = xmalloc(sizeof *keylist);
437 ARRAY_INIT(keylist);
438 ARRAY_ADD(keylist, '\002');
439 options_set_data(so, "prefix", keylist, xfree);
440
441 options_init(&global_w_options, NULL);
442 wo = &global_w_options;
443 options_set_number(wo, "aggressive-resize", 0);
444 options_set_number(wo, "automatic-rename", 1);
445 options_set_number(wo, "clock-mode-colour", 4);
446 options_set_number(wo, "clock-mode-style", 1);
447 options_set_number(wo, "force-height", 0);
448 options_set_number(wo, "force-width", 0);
449 options_set_number(wo, "main-pane-height", 24);
450 options_set_number(wo, "main-pane-width", 81);
451 options_set_number(wo, "mode-attr", 0);
452 options_set_number(wo, "mode-bg", 3);
453 options_set_number(wo, "mode-fg", 0);
454 options_set_number(wo, "mode-keys", MODEKEY_EMACS);
455 options_set_number(wo, "mode-mouse", 0);
456 options_set_number(wo, "monitor-activity", 0);
457 options_set_string(wo, "monitor-content", "%s", "");
458 options_set_number(wo, "window-status-attr", 0);
459 options_set_number(wo, "window-status-bg", 8);
460 options_set_number(wo, "window-status-current-attr", 0);
461 options_set_number(wo, "window-status-current-bg", 8);
462 options_set_number(wo, "window-status-current-fg", 8);
463 options_set_number(wo, "window-status-fg", 8);
464 options_set_number(wo, "xterm-keys", 0);
465 options_set_number(wo, "remain-on-exit", 0);
466 options_set_number(wo, "synchronize-panes", 0);
467
468 if (flags & IDENTIFY_UTF8) {
469 options_set_number(so, "status-utf8", 1);
470 options_set_number(wo, "utf8", 1);
471 } else {
472 options_set_number(so, "status-utf8", 0);
473 options_set_number(wo, "utf8", 0);
474 }
475
476 if (getcwd(cwd, sizeof cwd) == NULL) {
477 pw = getpwuid(getuid());
478 if (pw->pw_dir != NULL && *pw->pw_dir != '\0')
479 strlcpy(cwd, pw->pw_dir, sizeof cwd);
480 else
481 strlcpy(cwd, "/", sizeof cwd);
482 }
483 options_set_string(so, "default-path", "%s", cwd);
484
485 if (cfg_file == NULL) {
486 home = getenv("HOME");
487 if (home == NULL || *home == '\0') {
488 pw = getpwuid(getuid());
489 if (pw != NULL)
490 home = pw->pw_dir;
491 }
492 xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG);
493 if (access(cfg_file, R_OK) != 0) {
494 xfree(cfg_file);
495 cfg_file = NULL;
496 }
497 } else {
498 if (access(cfg_file, R_OK) != 0) {
499 log_warn("%s", cfg_file);
500 exit(1);
501 }
502 }
503
504 if (label == NULL)
505 label = xstrdup("default");
506 if (path == NULL && (path = makesockpath(label)) == NULL) {
507 log_warn("can't create socket");
508 exit(1);
509 }
510 xfree(label);
511
512 if (shellcmd != NULL) {
513 msg = MSG_SHELL;
514 buf = NULL;
515 len = 0;
516 } else {
517 fill_session(&cmddata);
518
519 cmddata.argc = argc;
520 if (cmd_pack_argv(
521 argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
522 log_warnx("command too long");
523 exit(1);
524 }
525
526 msg = MSG_COMMAND;
527 buf = &cmddata;
528 len = sizeof cmddata;
529 }
530
531 if (shellcmd != NULL)
532 cmdflags |= CMD_STARTSERVER;
533 else if (argc == 0) /* new-session is the default */
534 cmdflags |= CMD_STARTSERVER|CMD_SENDENVIRON;
535 else {
536 /*
537 * It sucks parsing the command string twice (in client and
538 * later in server) but it is necessary to get the start server
539 * flag.
540 */
541 if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
542 log_warnx("%s", cause);
543 exit(1);
544 }
545 cmdflags &= ~CMD_STARTSERVER;
546 TAILQ_FOREACH(cmd, cmdlist, qentry) {
547 if (cmd->entry->flags & CMD_STARTSERVER)
548 cmdflags |= CMD_STARTSERVER;
549 if (cmd->entry->flags & CMD_SENDENVIRON)
550 cmdflags |= CMD_SENDENVIRON;
551 }
552 cmd_list_free(cmdlist);
553 }
554
555 if ((ibuf = client_init(path, cmdflags, flags)) == NULL)
556 exit(1);
557 xfree(path);
558
559 imsg_compose(ibuf, msg, PROTOCOL_VERSION, -1, -1, buf, len);
560
561 retcode = 0;
562 for (;;) {
563 pfd.fd = ibuf->fd;
564 pfd.events = POLLIN;
565 if (ibuf->w.queued != 0)
566 pfd.events |= POLLOUT;
567
568 if ((nfds = poll(&pfd, 1, INFTIM)) == -1) {
569 if (errno == EAGAIN || errno == EINTR)
570 continue;
571 fatal("poll failed");
572 }
573 if (nfds == 0)
574 continue;
575
576 if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))
577 fatalx("socket error");
578
579 if (pfd.revents & POLLIN) {
580 if (dispatch_imsg(ibuf, shellcmd, &retcode) != 0)
581 break;
582 }
583
584 if (pfd.revents & POLLOUT) {
585 if (msgbuf_write(&ibuf->w) < 0)
586 fatalx("msgbuf_write failed");
587 }
588 }
589
590 options_free(&global_s_options);
591 options_free(&global_w_options);
592
593 return (retcode);
594 }
595
596 int
597 dispatch_imsg(struct imsgbuf *ibuf, const char *shellcmd, int *retcode)
598 {
599 struct imsg imsg;
600 ssize_t n, datalen;
601 struct msg_print_data printdata;
602 struct msg_shell_data shelldata;
603
604 if ((n = imsg_read(ibuf)) == -1 || n == 0)
605 fatalx("imsg_read failed");
606
607 for (;;) {
608 if ((n = imsg_get(ibuf, &imsg)) == -1)
609 fatalx("imsg_get failed");
610 if (n == 0)
611 return (0);
612 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
613
614 switch (imsg.hdr.type) {
615 case MSG_EXIT:
616 case MSG_SHUTDOWN:
617 if (datalen != 0)
618 fatalx("bad MSG_EXIT size");
619
620 return (-1);
621 case MSG_ERROR:
622 *retcode = 1;
623 /* FALLTHROUGH */
624 case MSG_PRINT:
625 if (datalen != sizeof printdata)
626 fatalx("bad MSG_PRINT size");
627 memcpy(&printdata, imsg.data, sizeof printdata);
628 printdata.msg[(sizeof printdata.msg) - 1] = '\0';
629
630 log_info("%s", printdata.msg);
631 break;
632 case MSG_READY:
633 if (datalen != 0)
634 fatalx("bad MSG_READY size");
635
636 client_main(); /* doesn't return */
637 case MSG_VERSION:
638 if (datalen != 0)
639 fatalx("bad MSG_VERSION size");
640
641 log_warnx("protocol version mismatch (client %u, "
642 "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
643 *retcode = 1;
644 return (-1);
645 case MSG_SHELL:
646 if (datalen != sizeof shelldata)
647 fatalx("bad MSG_SHELL size");
648 memcpy(&shelldata, imsg.data, sizeof shelldata);
649 shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
650
651 shell_exec(shelldata.shell, shellcmd);
652 default:
653 fatalx("unexpected message");
654 }
655
656 imsg_free(&imsg);
657 }
658 }
659
660 __dead void
661 shell_exec(const char *shell, const char *shellcmd)
662 {
663 const char *shellname, *ptr;
664 char *argv0;
665
666 sigreset();
667
668 ptr = strrchr(shell, '/');
669 if (ptr != NULL && *(ptr + 1) != '\0')
670 shellname = ptr + 1;
671 else
672 shellname = shell;
673 if (login_shell)
674 xasprintf(&argv0, "-%s", shellname);
675 else
676 xasprintf(&argv0, "%s", shellname);
677 setenv("SHELL", shell, 1);
678
679 execl(shell, argv0, "-c", shellcmd, (char *) NULL);
680 fatal("execl failed");
681 }
Something went wrong with that request. Please try again.