1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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 #include <sys/utsname.h>
22 
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <langinfo.h>
26 #include <locale.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <unistd.h>
33 
34 #include "tmux.h"
35 
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;
40 
41 struct timeval       start_time;
42 const char          *socket_path;
43 int                  ptm_fd = -1;
44 const char          *shell_command;
45 
46 static __dead void   usage(void);
47 static char                   *make_label(const char *, char **);
48 
49 static int                     areshell(const char *);
50 static const char   *getshell(void);
51 
52 static __dead void
usage(void)53 usage(void)
54 {
55           fprintf(stderr,
56               "usage: %s [-2CDlNuVv] [-c shell-command] [-f file] [-L socket-name]\n"
57               "            [-S socket-path] [-T features] [command [flags]]\n",
58               getprogname());
59           exit(1);
60 }
61 
62 static const char *
getshell(void)63 getshell(void)
64 {
65           struct passwd       *pw;
66           const char          *shell;
67 
68           shell = getenv("SHELL");
69           if (checkshell(shell))
70                     return (shell);
71 
72           pw = getpwuid(getuid());
73           if (pw != NULL && checkshell(pw->pw_shell))
74                     return (pw->pw_shell);
75 
76           return (_PATH_BSHELL);
77 }
78 
79 int
checkshell(const char * shell)80 checkshell(const char *shell)
81 {
82           if (shell == NULL || *shell != '/')
83                     return (0);
84           if (areshell(shell))
85                     return (0);
86           if (access(shell, X_OK) != 0)
87                     return (0);
88           return (1);
89 }
90 
91 static int
areshell(const char * shell)92 areshell(const char *shell)
93 {
94           const char          *progname, *ptr;
95 
96           if ((ptr = strrchr(shell, '/')) != NULL)
97                     ptr++;
98           else
99                     ptr = shell;
100           progname = getprogname();
101           if (*progname == '-')
102                     progname++;
103           if (strcmp(ptr, progname) == 0)
104                     return (1);
105           return (0);
106 }
107 
108 static char *
expand_path(const char * path,const char * home)109 expand_path(const char *path, const char *home)
110 {
111           char                          *expanded, *name;
112           const char                    *end;
113           struct environ_entry          *value;
114 
115           if (strncmp(path, "~/", 2) == 0) {
116                     if (home == NULL)
117                               return (NULL);
118                     xasprintf(&expanded, "%s%s", home, path + 1);
119                     return (expanded);
120           }
121 
122           if (*path == '$') {
123                     end = strchr(path, '/');
124                     if (end == NULL)
125                               name = xstrdup(path + 1);
126                     else
127                               name = xstrndup(path + 1, end - path - 1);
128                     value = environ_find(global_environ, name);
129                     free(name);
130                     if (value == NULL)
131                               return (NULL);
132                     if (end == NULL)
133                               end = "";
134                     xasprintf(&expanded, "%s%s", value->value, end);
135                     return (expanded);
136           }
137 
138           return (xstrdup(path));
139 }
140 
141 static void
expand_paths(const char * s,char *** paths,u_int * n,int ignore_errors)142 expand_paths(const char *s, char ***paths, u_int *n, int ignore_errors)
143 {
144           const char          *home = find_home();
145           char                *copy, *next, *tmp, resolved[PATH_MAX], *expanded;
146           char                *path;
147           u_int                i;
148 
149           *paths = NULL;
150           *n = 0;
151 
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);
157                               continue;
158                     }
159                     if (realpath(expanded, resolved) == NULL) {
160                               log_debug("%s: realpath(\"%s\") failed: %s", __func__,
161                                   expanded, strerror(errno));
162                               if (ignore_errors) {
163                                         free(expanded);
164                                         continue;
165                               }
166                               path = expanded;
167                     } else {
168                               path = xstrdup(resolved);
169                               free(expanded);
170                     }
171                     for (i = 0; i < *n; i++) {
172                               if (strcmp(path, (*paths)[i]) == 0)
173                                         break;
174                     }
175                     if (i != *n) {
176                               log_debug("%s: duplicate path: %s", __func__, path);
177                               free(path);
178                               continue;
179                     }
180                     *paths = xreallocarray(*paths, (*n) + 1, sizeof *paths);
181                     (*paths)[(*n)++] = path;
182           }
183           free(copy);
184 }
185 
186 static char *
make_label(const char * label,char ** cause)187 make_label(const char *label, char **cause)
188 {
189           char                **paths, *path, *base;
190           u_int                 i, n;
191           struct stat           sb;
192           uid_t                 uid;
193 
194           *cause = NULL;
195           if (label == NULL)
196                     label = "default";
197           uid = getuid();
198 
199           expand_paths(TMUX_SOCK, &paths, &n, 1);
200           if (n == 0) {
201                     xasprintf(cause, "no suitable socket path");
202                     return (NULL);
203           }
204           path = paths[0]; /* can only have one socket! */
205           for (i = 1; i < n; i++)
206                     free(paths[i]);
207           free(paths);
208 
209           xasprintf(&base, "%s/tmux-%ld", path, (long)uid);
210           free(path);
211           if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) {
212                     xasprintf(cause, "couldn't create directory %s (%s)", base,
213                         strerror(errno));
214                     goto fail;
215           }
216           if (lstat(base, &sb) != 0) {
217                     xasprintf(cause, "couldn't read directory %s (%s)", base,
218                         strerror(errno));
219                     goto fail;
220           }
221           if (!S_ISDIR(sb.st_mode)) {
222                     xasprintf(cause, "%s is not a directory", base);
223                     goto fail;
224           }
225           if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) {
226                     xasprintf(cause, "directory %s has unsafe permissions", base);
227                     goto fail;
228           }
229           xasprintf(&path, "%s/%s", base, label);
230           free(base);
231           return (path);
232 
233 fail:
234           free(base);
235           return (NULL);
236 }
237 
238 char *
shell_argv0(const char * shell,int is_login)239 shell_argv0(const char *shell, int is_login)
240 {
241           const char          *slash, *name;
242           char                *argv0;
243 
244           slash = strrchr(shell, '/');
245           if (slash != NULL && slash[1] != '\0')
246                     name = slash + 1;
247           else
248                     name = shell;
249           if (is_login)
250                     xasprintf(&argv0, "-%s", name);
251           else
252                     xasprintf(&argv0, "%s", name);
253           return (argv0);
254 }
255 
256 void
setblocking(int fd,int state)257 setblocking(int fd, int state)
258 {
259           int mode;
260 
261           if ((mode = fcntl(fd, F_GETFL)) != -1) {
262                     if (!state)
263                               mode |= O_NONBLOCK;
264                     else
265                               mode &= ~O_NONBLOCK;
266                     fcntl(fd, F_SETFL, mode);
267           }
268 }
269 
270 uint64_t
get_timer(void)271 get_timer(void)
272 {
273           struct timespec     ts;
274 
275           /*
276            * We want a timestamp in milliseconds suitable for time measurement,
277            * so prefer the monotonic clock.
278            */
279           if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
280                     clock_gettime(CLOCK_REALTIME, &ts);
281           return ((ts.tv_sec * 1000ULL) + (ts.tv_nsec / 1000000ULL));
282 }
283 
284 const char *
sig2name(int signo)285 sig2name(int signo)
286 {
287      static char    s[11];
288 
289 #ifdef HAVE_SYS_SIGNAME
290      if (signo > 0 && signo < NSIG)
291                return (sys_signame[signo]);
292 #endif
293      xsnprintf(s, sizeof s, "%d", signo);
294      return (s);
295 }
296 
297 const char *
find_cwd(void)298 find_cwd(void)
299 {
300           char                 resolved1[PATH_MAX], resolved2[PATH_MAX];
301           static char          cwd[PATH_MAX];
302           const char          *pwd;
303 
304           if (getcwd(cwd, sizeof cwd) == NULL)
305                     return (NULL);
306           if ((pwd = getenv("PWD")) == NULL || *pwd == '\0')
307                     return (cwd);
308 
309           /*
310            * We want to use PWD so that symbolic links are maintained,
311            * but only if it matches the actual working directory.
312            */
313           if (realpath(pwd, resolved1) == NULL)
314                     return (cwd);
315           if (realpath(cwd, resolved2) == NULL)
316                     return (cwd);
317           if (strcmp(resolved1, resolved2) != 0)
318                     return (cwd);
319           return (pwd);
320 }
321 
322 const char *
find_home(void)323 find_home(void)
324 {
325           struct passwd                 *pw;
326           static const char   *home;
327 
328           if (home != NULL)
329                     return (home);
330 
331           home = getenv("HOME");
332           if (home == NULL || *home == '\0') {
333                     pw = getpwuid(getuid());
334                     if (pw != NULL)
335                               home = pw->pw_dir;
336                     else
337                               home = NULL;
338           }
339 
340           return (home);
341 }
342 
343 const char *
getversion(void)344 getversion(void)
345 {
346           return (TMUX_VERSION);
347 }
348 
349 int
main(int argc,char ** argv)350 main(int argc, char **argv)
351 {
352           char                                              *path = NULL, *label = NULL;
353           char                                              *cause, **var;
354           const char                                        *s, *cwd;
355           int                                                opt, keys, feat = 0, fflag = 0;
356           uint64_t                                 flags = 0;
357           const struct options_table_entry        *oe;
358           u_int                                              i;
359 
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);
367           }
368 
369           setlocale(LC_TIME, "");
370           tzset();
371 
372           if (**argv == '-')
373                     flags = CLIENT_LOGIN;
374 
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);
381 
382           while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
383                     switch (opt) {
384                     case '2':
385                               tty_add_features(&feat, "256", ":,");
386                               break;
387                     case 'c':
388                               shell_command = optarg;
389                               break;
390                     case 'D':
391                               flags |= CLIENT_NOFORK;
392                               break;
393                     case 'C':
394                               if (flags & CLIENT_CONTROL)
395                                         flags |= CLIENT_CONTROLCONTROL;
396                               else
397                                         flags |= CLIENT_CONTROL;
398                               break;
399                     case 'f':
400                               if (!fflag) {
401                                         fflag = 1;
402                                         for (i = 0; i < cfg_nfiles; i++)
403                                                   free(cfg_files[i]);
404                                         cfg_nfiles = 0;
405                               }
406                               cfg_files = xreallocarray(cfg_files, cfg_nfiles + 1,
407                                   sizeof *cfg_files);
408                               cfg_files[cfg_nfiles++] = xstrdup(optarg);
409                               cfg_quiet = 0;
410                               break;
411                     case 'V':
412                               printf("tmux %s\n", getversion());
413                               exit(0);
414                     case 'l':
415                               flags |= CLIENT_LOGIN;
416                               break;
417                     case 'L':
418                               free(label);
419                               label = xstrdup(optarg);
420                               break;
421                     case 'N':
422                               flags |= CLIENT_NOSTARTSERVER;
423                               break;
424                     case 'q':
425                               break;
426                     case 'S':
427                               free(path);
428                               path = xstrdup(optarg);
429                               break;
430                     case 'T':
431                               tty_add_features(&feat, optarg, ":,");
432                               break;
433                     case 'u':
434                               flags |= CLIENT_UTF8;
435                               break;
436                     case 'v':
437                               log_add_level();
438                               break;
439                     default:
440                               usage();
441                     }
442           }
443           argc -= optind;
444           argv += optind;
445 
446           if (shell_command != NULL && argc != 0)
447                     usage();
448           if ((flags & CLIENT_NOFORK) && argc != 0)
449                     usage();
450 
451           if ((ptm_fd = getptmfd()) == -1)
452                     err(1, "getptmfd");
453           if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd "
454               "recvfd proc exec tty ps", NULL) != 0)
455                     err(1, "pledge");
456 
457           /*
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.
463            */
464           if (getenv("TMUX") != NULL)
465                     flags |= CLIENT_UTF8;
466           else {
467                     s = getenv("LC_ALL");
468                     if (s == NULL || *s == '\0')
469                               s = getenv("LC_CTYPE");
470                     if (s == NULL || *s == '\0')
471                               s = getenv("LANG");
472                     if (s == NULL || *s == '\0')
473                               s = "";
474                     if (strcasestr(s, "UTF-8") != NULL ||
475                         strcasestr(s, "UTF8") != NULL)
476                               flags |= CLIENT_UTF8;
477           }
478 
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);
489           }
490 
491           /*
492            * The default shell comes from SHELL or from the user's passwd entry
493            * if available.
494            */
495           options_set_string(global_s_options, "default-shell", 0, "%s",
496               getshell());
497 
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)
504                               keys = MODEKEY_VI;
505                     else
506                               keys = MODEKEY_EMACS;
507                     options_set_number(global_s_options, "status-keys", keys);
508                     options_set_number(global_w_options, "mode-keys", keys);
509           }
510 
511           /*
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
514            * used.
515            */
516           if (path == NULL && label == NULL) {
517                     s = getenv("TMUX");
518                     if (s != NULL && *s != '\0' && *s != ',') {
519                               path = xstrdup(s);
520                               path[strcspn(path, ",")] = '\0';
521                     }
522           }
523           if (path == NULL) {
524                     if ((path = make_label(label, &cause)) == NULL) {
525                               if (cause != NULL) {
526                                         fprintf(stderr, "%s\n", cause);
527                                         free(cause);
528                               }
529                               exit(1);
530                     }
531                     flags |= CLIENT_DEFAULTSOCKET;
532           }
533           socket_path = path;
534           free(label);
535 
536           /* Pass control to the client. */
537           exit(client_main(osdep_event_init(), argc, argv, flags, feat));
538 }
539