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>
40 struct options
*global_options
; /* server options */
41 struct options
*global_s_options
; /* session options */
42 struct options
*global_w_options
; /* window options */
43 struct environ
*global_environ
;
45 struct timeval start_time
;
46 const char *socket_path
;
48 const char *shell_command
;
50 static __dead
void usage(void);
51 static char *make_label(const char *, char **);
53 static int areshell(const char *);
54 static const char *getshell(void);
60 "usage: %s [-2CDlNuVv] [-c shell-command] [-f file] [-L socket-name]\n"
61 " [-S socket-path] [-T features] [command [flags]]\n",
72 shell
= getenv("SHELL");
73 if (checkshell(shell
))
76 pw
= getpwuid(getuid());
77 if (pw
!= NULL
&& checkshell(pw
->pw_shell
))
78 return (pw
->pw_shell
);
80 return (_PATH_BSHELL
);
84 checkshell(const char *shell
)
86 if (shell
== NULL
|| *shell
!= '/')
90 if (access(shell
, X_OK
) != 0)
96 areshell(const char *shell
)
98 const char *progname
, *ptr
;
100 if ((ptr
= strrchr(shell
, '/')) != NULL
)
104 progname
= getprogname();
105 if (*progname
== '-')
107 if (strcmp(ptr
, progname
) == 0)
113 expand_path(const char *path
, const char *home
)
115 char *expanded
, *name
;
117 struct environ_entry
*value
;
119 if (strncmp(path
, "~/", 2) == 0) {
122 xasprintf(&expanded
, "%s%s", home
, path
+ 1);
127 end
= strchr(path
, '/');
129 name
= xstrdup(path
+ 1);
131 name
= xstrndup(path
+ 1, end
- path
- 1);
132 value
= environ_find(global_environ
, name
);
138 xasprintf(&expanded
, "%s%s", value
->value
, end
);
142 return (xstrdup(path
));
146 expand_paths(const char *s
, char ***paths
, u_int
*n
, int ignore_errors
)
148 const char *home
= find_home();
149 char *copy
, *next
, *tmp
, resolved
[PATH_MAX
], *expanded
;
156 copy
= tmp
= xstrdup(s
);
157 while ((next
= strsep(&tmp
, ":")) != NULL
) {
158 expanded
= expand_path(next
, home
);
159 if (expanded
== NULL
) {
160 log_debug("%s: invalid path: %s", __func__
, next
);
163 if (realpath(expanded
, resolved
) == NULL
) {
164 log_debug("%s: realpath(\"%s\") failed: %s", __func__
,
165 expanded
, strerror(errno
));
172 path
= xstrdup(resolved
);
175 for (i
= 0; i
< *n
; i
++) {
176 if (strcmp(path
, (*paths
)[i
]) == 0)
180 log_debug("%s: duplicate path: %s", __func__
, path
);
184 *paths
= xreallocarray(*paths
, (*n
) + 1, sizeof *paths
);
185 (*paths
)[(*n
)++] = path
;
191 make_label(const char *label
, char **cause
)
193 char **paths
, *path
, *base
;
203 expand_paths(TMUX_SOCK
, &paths
, &n
, 1);
205 xasprintf(cause
, "no suitable socket path");
208 path
= paths
[0]; /* can only have one socket! */
209 for (i
= 1; i
< n
; i
++)
213 xasprintf(&base
, "%s/tmux-%ld", path
, (long)uid
);
215 if (mkdir(base
, S_IRWXU
) != 0 && errno
!= EEXIST
) {
216 xasprintf(cause
, "couldn't create directory %s (%s)", base
,
220 if (lstat(base
, &sb
) != 0) {
221 xasprintf(cause
, "couldn't read directory %s (%s)", base
,
225 if (!S_ISDIR(sb
.st_mode
)) {
226 xasprintf(cause
, "%s is not a directory", base
);
229 if (sb
.st_uid
!= uid
|| (sb
.st_mode
& TMUX_SOCK_PERM
) != 0) {
230 xasprintf(cause
, "directory %s has unsafe permissions", base
);
233 xasprintf(&path
, "%s/%s", base
, label
);
243 shell_argv0(const char *shell
, int is_login
)
245 const char *slash
, *name
;
248 slash
= strrchr(shell
, '/');
249 if (slash
!= NULL
&& slash
[1] != '\0')
254 xasprintf(&argv0
, "-%s", name
);
256 xasprintf(&argv0
, "%s", name
);
261 setblocking(int fd
, int state
)
265 if ((mode
= fcntl(fd
, F_GETFL
)) != -1) {
270 fcntl(fd
, F_SETFL
, mode
);
280 * We want a timestamp in milliseconds suitable for time measurement,
281 * so prefer the monotonic clock.
283 if (clock_gettime(CLOCK_MONOTONIC
, &ts
) != 0)
284 clock_gettime(CLOCK_REALTIME
, &ts
);
285 return ((ts
.tv_sec
* 1000ULL) + (ts
.tv_nsec
/ 1000000ULL));
293 if (signo
> 0 && signo
< NSIG
)
294 return (sys_signame
[signo
]);
295 xsnprintf(s
, sizeof s
, "%d", signo
);
302 char resolved1
[PATH_MAX
], resolved2
[PATH_MAX
];
303 static char cwd
[PATH_MAX
];
306 if (getcwd(cwd
, sizeof cwd
) == NULL
)
308 if ((pwd
= getenv("PWD")) == NULL
|| *pwd
== '\0')
312 * We want to use PWD so that symbolic links are maintained,
313 * but only if it matches the actual working directory.
315 if (realpath(pwd
, resolved1
) == NULL
)
317 if (realpath(cwd
, resolved2
) == NULL
)
319 if (strcmp(resolved1
, resolved2
) != 0)
328 static const char *home
;
333 home
= getenv("HOME");
334 if (home
== NULL
|| *home
== '\0') {
335 pw
= getpwuid(getuid());
348 static char *version
;
351 if (version
== NULL
) {
353 fatalx("uname failed");
354 xasprintf(&version
, "openbsd-%s", u
.release
);
360 main(int argc
, char **argv
)
362 char *path
= NULL
, *label
= NULL
;
365 int opt
, keys
, feat
= 0, fflag
= 0;
367 const struct options_table_entry
*oe
;
370 if (setlocale(LC_CTYPE
, "en_US.UTF-8") == NULL
&&
371 setlocale(LC_CTYPE
, "C.UTF-8") == NULL
) {
372 if (setlocale(LC_CTYPE
, "") == NULL
)
373 errx(1, "invalid LC_ALL, LC_CTYPE or LANG");
374 s
= nl_langinfo(CODESET
);
375 if (strcasecmp(s
, "UTF-8") != 0 && strcasecmp(s
, "UTF8") != 0)
376 errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s
);
379 setlocale(LC_TIME
, "");
383 flags
= CLIENT_LOGIN
;
385 global_environ
= environ_create();
386 for (var
= environ
; *var
!= NULL
; var
++)
387 environ_put(global_environ
, *var
, 0);
388 if ((cwd
= find_cwd()) != NULL
)
389 environ_set(global_environ
, "PWD", 0, "%s", cwd
);
390 expand_paths(TMUX_CONF
, &cfg_files
, &cfg_nfiles
, 1);
392 while ((opt
= getopt(argc
, argv
, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
395 tty_add_features(&feat
, "256", ":,");
398 shell_command
= optarg
;
401 flags
|= CLIENT_NOFORK
;
404 if (flags
& CLIENT_CONTROL
)
405 flags
|= CLIENT_CONTROLCONTROL
;
407 flags
|= CLIENT_CONTROL
;
412 for (i
= 0; i
< cfg_nfiles
; i
++)
416 cfg_files
= xreallocarray(cfg_files
, cfg_nfiles
+ 1,
418 cfg_files
[cfg_nfiles
++] = xstrdup(optarg
);
422 printf("tmux %s\n", getversion());
425 flags
|= CLIENT_LOGIN
;
429 label
= xstrdup(optarg
);
432 flags
|= CLIENT_NOSTARTSERVER
;
438 path
= xstrdup(optarg
);
441 tty_add_features(&feat
, optarg
, ":,");
444 flags
|= CLIENT_UTF8
;
456 if (shell_command
!= NULL
&& argc
!= 0)
458 if ((flags
& CLIENT_NOFORK
) && argc
!= 0)
461 if ((ptm_fd
= getptmfd()) == -1)
463 if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd "
464 "recvfd proc exec tty ps", NULL
) != 0)
468 * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8.
469 * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain
470 * UTF-8, it is a safe assumption that either they are using a UTF-8
471 * terminal, or if not they know that output from UTF-8-capable
472 * programs may be wrong.
474 if (getenv("TMUX") != NULL
)
475 flags
|= CLIENT_UTF8
;
477 s
= getenv("LC_ALL");
478 if (s
== NULL
|| *s
== '\0')
479 s
= getenv("LC_CTYPE");
480 if (s
== NULL
|| *s
== '\0')
482 if (s
== NULL
|| *s
== '\0')
484 if (strcasestr(s
, "UTF-8") != NULL
||
485 strcasestr(s
, "UTF8") != NULL
)
486 flags
|= CLIENT_UTF8
;
489 global_options
= options_create(NULL
);
490 global_s_options
= options_create(NULL
);
491 global_w_options
= options_create(NULL
);
492 for (oe
= options_table
; oe
->name
!= NULL
; oe
++) {
493 if (oe
->scope
& OPTIONS_TABLE_SERVER
)
494 options_default(global_options
, oe
);
495 if (oe
->scope
& OPTIONS_TABLE_SESSION
)
496 options_default(global_s_options
, oe
);
497 if (oe
->scope
& OPTIONS_TABLE_WINDOW
)
498 options_default(global_w_options
, oe
);
502 * The default shell comes from SHELL or from the user's passwd entry
505 options_set_string(global_s_options
, "default-shell", 0, "%s",
508 /* Override keys to vi if VISUAL or EDITOR are set. */
509 if ((s
= getenv("VISUAL")) != NULL
|| (s
= getenv("EDITOR")) != NULL
) {
510 options_set_string(global_options
, "editor", 0, "%s", s
);
511 if (strrchr(s
, '/') != NULL
)
512 s
= strrchr(s
, '/') + 1;
513 if (strstr(s
, "vi") != NULL
)
516 keys
= MODEKEY_EMACS
;
517 options_set_number(global_s_options
, "status-keys", keys
);
518 options_set_number(global_w_options
, "mode-keys", keys
);
522 * If socket is specified on the command-line with -S or -L, it is
523 * used. Otherwise, $TMUX is checked and if that fails "default" is
526 if (path
== NULL
&& label
== NULL
) {
528 if (s
!= NULL
&& *s
!= '\0' && *s
!= ',') {
530 path
[strcspn(path
, ",")] = '\0';
534 if ((path
= make_label(label
, &cause
)) == NULL
) {
536 fprintf(stderr
, "%s\n", cause
);
541 flags
|= CLIENT_DEFAULTSOCKET
;
546 /* Pass control to the client. */
547 exit(client_main(event_init(), argc
, argv
, flags
, feat
));