"Fossies" - the Fresh Open Source Software Archive

Member "tmux-3.2a/tmux.c" (10 Jun 2021, 11608 Bytes) of package /linux/misc/tmux-3.2a.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "tmux.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.2_vs_3.2a.

    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
   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 *
   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
   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
   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 *
  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
  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 *
  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     if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
  211         goto fail;
  212     if (lstat(base, &sb) != 0)
  213         goto fail;
  214     if (!S_ISDIR(sb.st_mode)) {
  215         errno = ENOTDIR;
  216         goto fail;
  217     }
  218     if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) {
  219         errno = EACCES;
  220         goto fail;
  221     }
  222     xasprintf(&path, "%s/%s", base, label);
  223     free(base);
  224     return (path);
  225 
  226 fail:
  227     xasprintf(cause, "error creating %s (%s)", base, strerror(errno));
  228     free(base);
  229     return (NULL);
  230 }
  231 
  232 void
  233 setblocking(int fd, int state)
  234 {
  235     int mode;
  236 
  237     if ((mode = fcntl(fd, F_GETFL)) != -1) {
  238         if (!state)
  239             mode |= O_NONBLOCK;
  240         else
  241             mode &= ~O_NONBLOCK;
  242         fcntl(fd, F_SETFL, mode);
  243     }
  244 }
  245 
  246 uint64_t
  247 get_timer(void)
  248 {
  249     struct timespec ts;
  250 
  251     /*
  252      * We want a timestamp in milliseconds suitable for time measurement,
  253      * so prefer the monotonic clock.
  254      */
  255     if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
  256         clock_gettime(CLOCK_REALTIME, &ts);
  257     return ((ts.tv_sec * 1000ULL) + (ts.tv_nsec / 1000000ULL));
  258 }
  259 
  260 const char *
  261 sig2name(int signo)
  262 {
  263      static char    s[11];
  264 
  265 #ifdef HAVE_SYS_SIGNAME
  266      if (signo > 0 && signo < NSIG)
  267          return (sys_signame[signo]);
  268 #endif
  269      xsnprintf(s, sizeof s, "%d", signo);
  270      return (s);
  271 }
  272 
  273 const char *
  274 find_cwd(void)
  275 {
  276     char         resolved1[PATH_MAX], resolved2[PATH_MAX];
  277     static char  cwd[PATH_MAX];
  278     const char  *pwd;
  279 
  280     if (getcwd(cwd, sizeof cwd) == NULL)
  281         return (NULL);
  282     if ((pwd = getenv("PWD")) == NULL || *pwd == '\0')
  283         return (cwd);
  284 
  285     /*
  286      * We want to use PWD so that symbolic links are maintained,
  287      * but only if it matches the actual working directory.
  288      */
  289     if (realpath(pwd, resolved1) == NULL)
  290         return (cwd);
  291     if (realpath(cwd, resolved2) == NULL)
  292         return (cwd);
  293     if (strcmp(resolved1, resolved2) != 0)
  294         return (cwd);
  295     return (pwd);
  296 }
  297 
  298 const char *
  299 find_home(void)
  300 {
  301     struct passwd       *pw;
  302     static const char   *home;
  303 
  304     if (home != NULL)
  305         return (home);
  306 
  307     home = getenv("HOME");
  308     if (home == NULL || *home == '\0') {
  309         pw = getpwuid(getuid());
  310         if (pw != NULL)
  311             home = pw->pw_dir;
  312         else
  313             home = NULL;
  314     }
  315 
  316     return (home);
  317 }
  318 
  319 const char *
  320 getversion(void)
  321 {
  322     return TMUX_VERSION;
  323 }
  324 
  325 int
  326 main(int argc, char **argv)
  327 {
  328     char                    *path = NULL, *label = NULL;
  329     char                    *cause, **var;
  330     const char              *s, *cwd;
  331     int                  opt, keys, feat = 0, fflag = 0;
  332     uint64_t                 flags = 0;
  333     const struct options_table_entry    *oe;
  334     u_int                    i;
  335 
  336     if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
  337         setlocale(LC_CTYPE, "C.UTF-8") == NULL) {
  338         if (setlocale(LC_CTYPE, "") == NULL)
  339             errx(1, "invalid LC_ALL, LC_CTYPE or LANG");
  340         s = nl_langinfo(CODESET);
  341         if (strcasecmp(s, "UTF-8") != 0 && strcasecmp(s, "UTF8") != 0)
  342             errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s);
  343     }
  344 
  345     setlocale(LC_TIME, "");
  346     tzset();
  347 
  348     if (**argv == '-')
  349         flags = CLIENT_LOGIN;
  350 
  351     global_environ = environ_create();
  352     for (var = environ; *var != NULL; var++)
  353         environ_put(global_environ, *var, 0);
  354     if ((cwd = find_cwd()) != NULL)
  355         environ_set(global_environ, "PWD", 0, "%s", cwd);
  356     expand_paths(TMUX_CONF, &cfg_files, &cfg_nfiles, 1);
  357 
  358     while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
  359         switch (opt) {
  360         case '2':
  361             tty_add_features(&feat, "256", ":,");
  362             break;
  363         case 'c':
  364             shell_command = optarg;
  365             break;
  366         case 'D':
  367             flags |= CLIENT_NOFORK;
  368             break;
  369         case 'C':
  370             if (flags & CLIENT_CONTROL)
  371                 flags |= CLIENT_CONTROLCONTROL;
  372             else
  373                 flags |= CLIENT_CONTROL;
  374             break;
  375         case 'f':
  376             if (!fflag) {
  377                 fflag = 1;
  378                 for (i = 0; i < cfg_nfiles; i++)
  379                     free(cfg_files[i]);
  380                 cfg_nfiles = 0;
  381             }
  382             cfg_files = xreallocarray(cfg_files, cfg_nfiles + 1,
  383                 sizeof *cfg_files);
  384             cfg_files[cfg_nfiles++] = xstrdup(optarg);
  385             cfg_quiet = 0;
  386             break;
  387         case 'V':
  388             printf("%s %s\n", getprogname(), getversion());
  389             exit(0);
  390         case 'l':
  391             flags |= CLIENT_LOGIN;
  392             break;
  393         case 'L':
  394             free(label);
  395             label = xstrdup(optarg);
  396             break;
  397         case 'N':
  398             flags |= CLIENT_NOSTARTSERVER;
  399             break;
  400         case 'q':
  401             break;
  402         case 'S':
  403             free(path);
  404             path = xstrdup(optarg);
  405             break;
  406         case 'T':
  407             tty_add_features(&feat, optarg, ":,");
  408             break;
  409         case 'u':
  410             flags |= CLIENT_UTF8;
  411             break;
  412         case 'v':
  413             log_add_level();
  414             break;
  415         default:
  416             usage();
  417         }
  418     }
  419     argc -= optind;
  420     argv += optind;
  421 
  422     if (shell_command != NULL && argc != 0)
  423         usage();
  424     if ((flags & CLIENT_NOFORK) && argc != 0)
  425         usage();
  426 
  427     if ((ptm_fd = getptmfd()) == -1)
  428         err(1, "getptmfd");
  429     if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd "
  430         "recvfd proc exec tty ps", NULL) != 0)
  431         err(1, "pledge");
  432 
  433     /*
  434      * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8.
  435      * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain
  436      * UTF-8, it is a safe assumption that either they are using a UTF-8
  437      * terminal, or if not they know that output from UTF-8-capable
  438      * programs may be wrong.
  439      */
  440     if (getenv("TMUX") != NULL)
  441         flags |= CLIENT_UTF8;
  442     else {
  443         s = getenv("LC_ALL");
  444         if (s == NULL || *s == '\0')
  445             s = getenv("LC_CTYPE");
  446         if (s == NULL || *s == '\0')
  447             s = getenv("LANG");
  448         if (s == NULL || *s == '\0')
  449             s = "";
  450         if (strcasestr(s, "UTF-8") != NULL ||
  451             strcasestr(s, "UTF8") != NULL)
  452             flags |= CLIENT_UTF8;
  453     }
  454 
  455     global_options = options_create(NULL);
  456     global_s_options = options_create(NULL);
  457     global_w_options = options_create(NULL);
  458     for (oe = options_table; oe->name != NULL; oe++) {
  459         if (oe->scope & OPTIONS_TABLE_SERVER)
  460             options_default(global_options, oe);
  461         if (oe->scope & OPTIONS_TABLE_SESSION)
  462             options_default(global_s_options, oe);
  463         if (oe->scope & OPTIONS_TABLE_WINDOW)
  464             options_default(global_w_options, oe);
  465     }
  466 
  467     /*
  468      * The default shell comes from SHELL or from the user's passwd entry
  469      * if available.
  470      */
  471     options_set_string(global_s_options, "default-shell", 0, "%s",
  472         getshell());
  473 
  474     /* Override keys to vi if VISUAL or EDITOR are set. */
  475     if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) {
  476         options_set_string(global_options, "editor", 0, "%s", s);
  477         if (strrchr(s, '/') != NULL)
  478             s = strrchr(s, '/') + 1;
  479         if (strstr(s, "vi") != NULL)
  480             keys = MODEKEY_VI;
  481         else
  482             keys = MODEKEY_EMACS;
  483         options_set_number(global_s_options, "status-keys", keys);
  484         options_set_number(global_w_options, "mode-keys", keys);
  485     }
  486 
  487     /*
  488      * If socket is specified on the command-line with -S or -L, it is
  489      * used. Otherwise, $TMUX is checked and if that fails "default" is
  490      * used.
  491      */
  492     if (path == NULL && label == NULL) {
  493         s = getenv("TMUX");
  494         if (s != NULL && *s != '\0' && *s != ',') {
  495             path = xstrdup(s);
  496             path[strcspn(path, ",")] = '\0';
  497         }
  498     }
  499     if (path == NULL) {
  500         if ((path = make_label(label, &cause)) == NULL) {
  501             if (cause != NULL) {
  502                 fprintf(stderr, "%s\n", cause);
  503                 free(cause);
  504             }
  505             exit(1);
  506         }
  507         flags |= CLIENT_DEFAULTSOCKET;
  508     }
  509     socket_path = path;
  510     free(label);
  511 
  512     /* Pass control to the client. */
  513     exit(client_main(osdep_event_init(), argc, argv, flags, feat));
  514 }