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 setblocking(int fd
, int state
)
243 if ((mode
= fcntl(fd
, F_GETFL
)) != -1) {
248 fcntl(fd
, F_SETFL
, mode
);
258 * We want a timestamp in milliseconds suitable for time measurement,
259 * so prefer the monotonic clock.
261 if (clock_gettime(CLOCK_MONOTONIC
, &ts
) != 0)
262 clock_gettime(CLOCK_REALTIME
, &ts
);
263 return ((ts
.tv_sec
* 1000ULL) + (ts
.tv_nsec
/ 1000000ULL));
271 #ifdef HAVE_SYS_SIGNAME
272 if (signo
> 0 && signo
< NSIG
)
273 return (sys_signame
[signo
]);
275 xsnprintf(s
, sizeof s
, "%d", signo
);
282 char resolved1
[PATH_MAX
], resolved2
[PATH_MAX
];
283 static char cwd
[PATH_MAX
];
286 if (getcwd(cwd
, sizeof cwd
) == NULL
)
288 if ((pwd
= getenv("PWD")) == NULL
|| *pwd
== '\0')
292 * We want to use PWD so that symbolic links are maintained,
293 * but only if it matches the actual working directory.
295 if (realpath(pwd
, resolved1
) == NULL
)
297 if (realpath(cwd
, resolved2
) == NULL
)
299 if (strcmp(resolved1
, resolved2
) != 0)
308 static const char *home
;
313 home
= getenv("HOME");
314 if (home
== NULL
|| *home
== '\0') {
315 pw
= getpwuid(getuid());
328 return (TMUX_VERSION
);
332 main(int argc
, char **argv
)
334 char *path
= NULL
, *label
= NULL
;
337 int opt
, keys
, feat
= 0, fflag
= 0;
339 const struct options_table_entry
*oe
;
342 if (setlocale(LC_CTYPE
, "en_US.UTF-8") == NULL
&&
343 setlocale(LC_CTYPE
, "C.UTF-8") == NULL
) {
344 if (setlocale(LC_CTYPE
, "") == NULL
)
345 errx(1, "invalid LC_ALL, LC_CTYPE or LANG");
346 s
= nl_langinfo(CODESET
);
347 if (strcasecmp(s
, "UTF-8") != 0 && strcasecmp(s
, "UTF8") != 0)
348 errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s
);
351 setlocale(LC_TIME
, "");
355 flags
= CLIENT_LOGIN
;
357 global_environ
= environ_create();
358 for (var
= environ
; *var
!= NULL
; var
++)
359 environ_put(global_environ
, *var
, 0);
360 if ((cwd
= find_cwd()) != NULL
)
361 environ_set(global_environ
, "PWD", 0, "%s", cwd
);
362 expand_paths(TMUX_CONF
, &cfg_files
, &cfg_nfiles
, 1);
364 while ((opt
= getopt(argc
, argv
, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
367 tty_add_features(&feat
, "256", ":,");
370 shell_command
= optarg
;
373 flags
|= CLIENT_NOFORK
;
376 if (flags
& CLIENT_CONTROL
)
377 flags
|= CLIENT_CONTROLCONTROL
;
379 flags
|= CLIENT_CONTROL
;
384 for (i
= 0; i
< cfg_nfiles
; i
++)
388 cfg_files
= xreallocarray(cfg_files
, cfg_nfiles
+ 1,
390 cfg_files
[cfg_nfiles
++] = xstrdup(optarg
);
394 printf("tmux %s\n", getversion());
397 flags
|= CLIENT_LOGIN
;
401 label
= xstrdup(optarg
);
404 flags
|= CLIENT_NOSTARTSERVER
;
410 path
= xstrdup(optarg
);
413 tty_add_features(&feat
, optarg
, ":,");
416 flags
|= CLIENT_UTF8
;
428 if (shell_command
!= NULL
&& argc
!= 0)
430 if ((flags
& CLIENT_NOFORK
) && argc
!= 0)
433 if ((ptm_fd
= getptmfd()) == -1)
435 if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd "
436 "recvfd proc exec tty ps", NULL
) != 0)
440 * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8.
441 * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain
442 * UTF-8, it is a safe assumption that either they are using a UTF-8
443 * terminal, or if not they know that output from UTF-8-capable
444 * programs may be wrong.
446 if (getenv("TMUX") != NULL
)
447 flags
|= CLIENT_UTF8
;
449 s
= getenv("LC_ALL");
450 if (s
== NULL
|| *s
== '\0')
451 s
= getenv("LC_CTYPE");
452 if (s
== NULL
|| *s
== '\0')
454 if (s
== NULL
|| *s
== '\0')
456 if (strcasestr(s
, "UTF-8") != NULL
||
457 strcasestr(s
, "UTF8") != NULL
)
458 flags
|= CLIENT_UTF8
;
461 global_options
= options_create(NULL
);
462 global_s_options
= options_create(NULL
);
463 global_w_options
= options_create(NULL
);
464 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
465 if (oe
->scope
& OPTIONS_TABLE_SERVER
)
466 options_default(global_options
, oe
);
467 if (oe
->scope
& OPTIONS_TABLE_SESSION
)
468 options_default(global_s_options
, oe
);
469 if (oe
->scope
& OPTIONS_TABLE_WINDOW
)
470 options_default(global_w_options
, oe
);
474 * The default shell comes from SHELL or from the user's passwd entry
477 options_set_string(global_s_options
, "default-shell", 0, "%s",
480 /* Override keys to vi if VISUAL or EDITOR are set. */
481 if ((s
= getenv("VISUAL")) != NULL
|| (s
= getenv("EDITOR")) != NULL
) {
482 options_set_string(global_options
, "editor", 0, "%s", s
);
483 if (strrchr(s
, '/') != NULL
)
484 s
= strrchr(s
, '/') + 1;
485 if (strstr(s
, "vi") != NULL
)
488 keys
= MODEKEY_EMACS
;
489 options_set_number(global_s_options
, "status-keys", keys
);
490 options_set_number(global_w_options
, "mode-keys", keys
);
494 * If socket is specified on the command-line with -S or -L, it is
495 * used. Otherwise, $TMUX is checked and if that fails "default" is
498 if (path
== NULL
&& label
== NULL
) {
500 if (s
!= NULL
&& *s
!= '\0' && *s
!= ',') {
502 path
[strcspn(path
, ",")] = '\0';
506 if ((path
= make_label(label
, &cause
)) == NULL
) {
508 fprintf(stderr
, "%s\n", cause
);
513 flags
|= CLIENT_DEFAULTSOCKET
;
518 /* Pass control to the client. */
519 exit(client_main(osdep_event_init(), argc
, argv
, flags
, feat
));