4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
21 #include <sys/utsname.h>
36 struct options
*global_options
; /* server options */
37 struct options
*global_s_options
; /* session options */
38 struct options
*global_w_options
; /* window options */
39 struct environ
*global_environ
;
41 struct timeval start_time
;
42 const char *socket_path
;
44 const char *shell_command
;
46 static __dead
void usage(void);
47 static char *make_label(const char *, char **);
49 static int areshell(const char *);
50 static const char *getshell(void);
56 "usage: %s [-2CDlNuVv] [-c shell-command] [-f file] [-L socket-name]\n"
57 " [-S socket-path] [-T features] [command [flags]]\n",
68 shell
= getenv("SHELL");
69 if (checkshell(shell
))
72 pw
= getpwuid(getuid());
73 if (pw
!= NULL
&& checkshell(pw
->pw_shell
))
74 return (pw
->pw_shell
);
76 return (_PATH_BSHELL
);
80 checkshell(const char *shell
)
82 if (shell
== NULL
|| *shell
!= '/')
86 if (access(shell
, X_OK
) != 0)
92 areshell(const char *shell
)
94 const char *progname
, *ptr
;
96 if ((ptr
= strrchr(shell
, '/')) != NULL
)
100 progname
= getprogname();
101 if (*progname
== '-')
103 if (strcmp(ptr
, progname
) == 0)
109 expand_path(const char *path
, const char *home
)
111 char *expanded
, *name
;
113 struct environ_entry
*value
;
115 if (strncmp(path
, "~/", 2) == 0) {
118 xasprintf(&expanded
, "%s%s", home
, path
+ 1);
123 end
= strchr(path
, '/');
125 name
= xstrdup(path
+ 1);
127 name
= xstrndup(path
+ 1, end
- path
- 1);
128 value
= environ_find(global_environ
, name
);
134 xasprintf(&expanded
, "%s%s", value
->value
, end
);
138 return (xstrdup(path
));
142 expand_paths(const char *s
, char ***paths
, u_int
*n
, int ignore_errors
)
144 const char *home
= find_home();
145 char *copy
, *next
, *tmp
, resolved
[PATH_MAX
], *expanded
;
152 copy
= tmp
= xstrdup(s
);
153 while ((next
= strsep(&tmp
, ":")) != NULL
) {
154 expanded
= expand_path(next
, home
);
155 if (expanded
== NULL
) {
156 log_debug("%s: invalid path: %s", __func__
, next
);
159 if (realpath(expanded
, resolved
) == NULL
) {
160 log_debug("%s: realpath(\"%s\") failed: %s", __func__
,
161 expanded
, strerror(errno
));
168 path
= xstrdup(resolved
);
171 for (i
= 0; i
< *n
; i
++) {
172 if (strcmp(path
, (*paths
)[i
]) == 0)
176 log_debug("%s: duplicate path: %s", __func__
, path
);
180 *paths
= xreallocarray(*paths
, (*n
) + 1, sizeof *paths
);
181 (*paths
)[(*n
)++] = path
;
187 make_label(const char *label
, char **cause
)
189 char **paths
, *path
, *base
;
199 expand_paths(TMUX_SOCK
, &paths
, &n
, 1);
201 xasprintf(cause
, "no suitable socket path");
204 path
= paths
[0]; /* can only have one socket! */
205 for (i
= 1; i
< n
; i
++)
209 xasprintf(&base
, "%s/tmux-%ld", path
, (long)uid
);
211 if (mkdir(base
, S_IRWXU
) != 0 && errno
!= EEXIST
) {
212 xasprintf(cause
, "couldn't create directory %s (%s)", base
,
216 if (lstat(base
, &sb
) != 0) {
217 xasprintf(cause
, "couldn't read directory %s (%s)", base
,
221 if (!S_ISDIR(sb
.st_mode
)) {
222 xasprintf(cause
, "%s is not a directory", base
);
225 if (sb
.st_uid
!= uid
|| (sb
.st_mode
& S_IRWXO
) != 0) {
226 xasprintf(cause
, "directory %s has unsafe permissions", base
);
229 xasprintf(&path
, "%s/%s", base
, label
);
239 shell_argv0(const char *shell
, int is_login
)
241 const char *slash
, *name
;
244 slash
= strrchr(shell
, '/');
245 if (slash
!= NULL
&& slash
[1] != '\0')
250 xasprintf(&argv0
, "-%s", name
);
252 xasprintf(&argv0
, "%s", name
);
257 setblocking(int fd
, int state
)
261 if ((mode
= fcntl(fd
, F_GETFL
)) != -1) {
266 fcntl(fd
, F_SETFL
, mode
);
276 * We want a timestamp in milliseconds suitable for time measurement,
277 * so prefer the monotonic clock.
279 if (clock_gettime(CLOCK_MONOTONIC
, &ts
) != 0)
280 clock_gettime(CLOCK_REALTIME
, &ts
);
281 return ((ts
.tv_sec
* 1000ULL) + (ts
.tv_nsec
/ 1000000ULL));
289 #ifdef HAVE_SYS_SIGNAME
290 if (signo
> 0 && signo
< NSIG
)
291 return (sys_signame
[signo
]);
293 xsnprintf(s
, sizeof s
, "%d", signo
);
300 char resolved1
[PATH_MAX
], resolved2
[PATH_MAX
];
301 static char cwd
[PATH_MAX
];
304 if (getcwd(cwd
, sizeof cwd
) == NULL
)
306 if ((pwd
= getenv("PWD")) == NULL
|| *pwd
== '\0')
310 * We want to use PWD so that symbolic links are maintained,
311 * but only if it matches the actual working directory.
313 if (realpath(pwd
, resolved1
) == NULL
)
315 if (realpath(cwd
, resolved2
) == NULL
)
317 if (strcmp(resolved1
, resolved2
) != 0)
326 static const char *home
;
331 home
= getenv("HOME");
332 if (home
== NULL
|| *home
== '\0') {
333 pw
= getpwuid(getuid());
346 return (TMUX_VERSION
);
350 main(int argc
, char **argv
)
352 char *path
= NULL
, *label
= NULL
;
355 int opt
, keys
, feat
= 0, fflag
= 0;
357 const struct options_table_entry
*oe
;
360 if (setlocale(LC_CTYPE
, "en_US.UTF-8") == NULL
&&
361 setlocale(LC_CTYPE
, "C.UTF-8") == NULL
) {
362 if (setlocale(LC_CTYPE
, "") == NULL
)
363 errx(1, "invalid LC_ALL, LC_CTYPE or LANG");
364 s
= nl_langinfo(CODESET
);
365 if (strcasecmp(s
, "UTF-8") != 0 && strcasecmp(s
, "UTF8") != 0)
366 errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s
);
369 setlocale(LC_TIME
, "");
373 flags
= CLIENT_LOGIN
;
375 global_environ
= environ_create();
376 for (var
= environ
; *var
!= NULL
; var
++)
377 environ_put(global_environ
, *var
, 0);
378 if ((cwd
= find_cwd()) != NULL
)
379 environ_set(global_environ
, "PWD", 0, "%s", cwd
);
380 expand_paths(TMUX_CONF
, &cfg_files
, &cfg_nfiles
, 1);
382 while ((opt
= getopt(argc
, argv
, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
385 tty_add_features(&feat
, "256", ":,");
388 shell_command
= optarg
;
391 flags
|= CLIENT_NOFORK
;
394 if (flags
& CLIENT_CONTROL
)
395 flags
|= CLIENT_CONTROLCONTROL
;
397 flags
|= CLIENT_CONTROL
;
402 for (i
= 0; i
< cfg_nfiles
; i
++)
406 cfg_files
= xreallocarray(cfg_files
, cfg_nfiles
+ 1,
408 cfg_files
[cfg_nfiles
++] = xstrdup(optarg
);
412 printf("tmux %s\n", getversion());
415 flags
|= CLIENT_LOGIN
;
419 label
= xstrdup(optarg
);
422 flags
|= CLIENT_NOSTARTSERVER
;
428 path
= xstrdup(optarg
);
431 tty_add_features(&feat
, optarg
, ":,");
434 flags
|= CLIENT_UTF8
;
446 if (shell_command
!= NULL
&& argc
!= 0)
448 if ((flags
& CLIENT_NOFORK
) && argc
!= 0)
451 if ((ptm_fd
= getptmfd()) == -1)
453 if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd "
454 "recvfd proc exec tty ps", NULL
) != 0)
458 * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8.
459 * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain
460 * UTF-8, it is a safe assumption that either they are using a UTF-8
461 * terminal, or if not they know that output from UTF-8-capable
462 * programs may be wrong.
464 if (getenv("TMUX") != NULL
)
465 flags
|= CLIENT_UTF8
;
467 s
= getenv("LC_ALL");
468 if (s
== NULL
|| *s
== '\0')
469 s
= getenv("LC_CTYPE");
470 if (s
== NULL
|| *s
== '\0')
472 if (s
== NULL
|| *s
== '\0')
474 if (strcasestr(s
, "UTF-8") != NULL
||
475 strcasestr(s
, "UTF8") != NULL
)
476 flags
|= CLIENT_UTF8
;
479 global_options
= options_create(NULL
);
480 global_s_options
= options_create(NULL
);
481 global_w_options
= options_create(NULL
);
482 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
483 if (oe
->scope
& OPTIONS_TABLE_SERVER
)
484 options_default(global_options
, oe
);
485 if (oe
->scope
& OPTIONS_TABLE_SESSION
)
486 options_default(global_s_options
, oe
);
487 if (oe
->scope
& OPTIONS_TABLE_WINDOW
)
488 options_default(global_w_options
, oe
);
492 * The default shell comes from SHELL or from the user's passwd entry
495 options_set_string(global_s_options
, "default-shell", 0, "%s",
498 /* Override keys to vi if VISUAL or EDITOR are set. */
499 if ((s
= getenv("VISUAL")) != NULL
|| (s
= getenv("EDITOR")) != NULL
) {
500 options_set_string(global_options
, "editor", 0, "%s", s
);
501 if (strrchr(s
, '/') != NULL
)
502 s
= strrchr(s
, '/') + 1;
503 if (strstr(s
, "vi") != NULL
)
506 keys
= MODEKEY_EMACS
;
507 options_set_number(global_s_options
, "status-keys", keys
);
508 options_set_number(global_w_options
, "mode-keys", keys
);
512 * If socket is specified on the command-line with -S or -L, it is
513 * used. Otherwise, $TMUX is checked and if that fails "default" is
516 if (path
== NULL
&& label
== NULL
) {
518 if (s
!= NULL
&& *s
!= '\0' && *s
!= ',') {
520 path
[strcspn(path
, ",")] = '\0';
524 if ((path
= make_label(label
, &cause
)) == NULL
) {
526 fprintf(stderr
, "%s\n", cause
);
531 flags
|= CLIENT_DEFAULTSOCKET
;
536 /* Pass control to the client. */
537 exit(client_main(osdep_event_init(), argc
, argv
, flags
, feat
));