Coverage Report

Created: 2024-09-08 06:17

/src/tmux/client.c
Line
Count
Source (jump to first uncovered line)
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/socket.h>
21
#include <sys/uio.h>
22
#include <sys/un.h>
23
#include <sys/wait.h>
24
#include <sys/file.h>
25
26
#include <errno.h>
27
#include <fcntl.h>
28
#include <signal.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <unistd.h>
32
33
#include "tmux.h"
34
35
static struct tmuxproc  *client_proc;
36
static struct tmuxpeer  *client_peer;
37
static uint64_t    client_flags;
38
static int     client_suspended;
39
static enum {
40
  CLIENT_EXIT_NONE,
41
  CLIENT_EXIT_DETACHED,
42
  CLIENT_EXIT_DETACHED_HUP,
43
  CLIENT_EXIT_LOST_TTY,
44
  CLIENT_EXIT_TERMINATED,
45
  CLIENT_EXIT_LOST_SERVER,
46
  CLIENT_EXIT_EXITED,
47
  CLIENT_EXIT_SERVER_EXITED,
48
  CLIENT_EXIT_MESSAGE_PROVIDED
49
} client_exitreason = CLIENT_EXIT_NONE;
50
static int     client_exitflag;
51
static int     client_exitval;
52
static enum msgtype  client_exittype;
53
static const char *client_exitsession;
54
static char   *client_exitmessage;
55
static const char *client_execshell;
56
static const char *client_execcmd;
57
static int     client_attached;
58
static struct client_files client_files = RB_INITIALIZER(&client_files);
59
60
static __dead void   client_exec(const char *,const char *);
61
static int     client_get_lock(char *);
62
static int     client_connect(struct event_base *, const char *,
63
           uint64_t);
64
static void    client_send_identify(const char *, const char *,
65
           char **, u_int, const char *, int);
66
static void    client_signal(int);
67
static void    client_dispatch(struct imsg *, void *);
68
static void    client_dispatch_attached(struct imsg *);
69
static void    client_dispatch_wait(struct imsg *);
70
static const char *client_exit_message(void);
71
72
/*
73
 * Get server create lock. If already held then server start is happening in
74
 * another client, so block until the lock is released and return -2 to
75
 * retry. Return -1 on failure to continue and start the server anyway.
76
 */
77
static int
78
client_get_lock(char *lockfile)
79
0
{
80
0
  int lockfd;
81
82
0
  log_debug("lock file is %s", lockfile);
83
84
0
  if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) {
85
0
    log_debug("open failed: %s", strerror(errno));
86
0
    return (-1);
87
0
  }
88
89
0
  if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) {
90
0
    log_debug("flock failed: %s", strerror(errno));
91
0
    if (errno != EAGAIN)
92
0
      return (lockfd);
93
0
    while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR)
94
0
      /* nothing */;
95
0
    close(lockfd);
96
0
    return (-2);
97
0
  }
98
0
  log_debug("flock succeeded");
99
100
0
  return (lockfd);
101
0
}
102
103
/* Connect client to server. */
104
static int
105
client_connect(struct event_base *base, const char *path, uint64_t flags)
106
0
{
107
0
  struct sockaddr_un  sa;
108
0
  size_t      size;
109
0
  int     fd, lockfd = -1, locked = 0;
110
0
  char           *lockfile = NULL;
111
112
0
  memset(&sa, 0, sizeof sa);
113
0
  sa.sun_family = AF_UNIX;
114
0
  size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
115
0
  if (size >= sizeof sa.sun_path) {
116
0
    errno = ENAMETOOLONG;
117
0
    return (-1);
118
0
  }
119
0
  log_debug("socket is %s", path);
120
121
0
retry:
122
0
  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
123
0
    return (-1);
124
125
0
  log_debug("trying connect");
126
0
  if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) {
127
0
    log_debug("connect failed: %s", strerror(errno));
128
0
    if (errno != ECONNREFUSED && errno != ENOENT)
129
0
      goto failed;
130
0
    if (flags & CLIENT_NOSTARTSERVER)
131
0
      goto failed;
132
0
    if (~flags & CLIENT_STARTSERVER)
133
0
      goto failed;
134
0
    close(fd);
135
136
0
    if (!locked) {
137
0
      xasprintf(&lockfile, "%s.lock", path);
138
0
      if ((lockfd = client_get_lock(lockfile)) < 0) {
139
0
        log_debug("didn't get lock (%d)", lockfd);
140
141
0
        free(lockfile);
142
0
        lockfile = NULL;
143
144
0
        if (lockfd == -2)
145
0
          goto retry;
146
0
      }
147
0
      log_debug("got lock (%d)", lockfd);
148
149
      /*
150
       * Always retry at least once, even if we got the lock,
151
       * because another client could have taken the lock,
152
       * started the server and released the lock between our
153
       * connect() and flock().
154
       */
155
0
      locked = 1;
156
0
      goto retry;
157
0
    }
158
159
0
    if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) {
160
0
      free(lockfile);
161
0
      close(lockfd);
162
0
      return (-1);
163
0
    }
164
0
    fd = server_start(client_proc, flags, base, lockfd, lockfile);
165
0
  }
166
167
0
  if (locked && lockfd >= 0) {
168
0
    free(lockfile);
169
0
    close(lockfd);
170
0
  }
171
0
  setblocking(fd, 0);
172
0
  return (fd);
173
174
0
failed:
175
0
  if (locked) {
176
0
    free(lockfile);
177
0
    close(lockfd);
178
0
  }
179
0
  close(fd);
180
0
  return (-1);
181
0
}
182
183
/* Get exit string from reason number. */
184
const char *
185
client_exit_message(void)
186
0
{
187
0
  static char msg[256];
188
189
0
  switch (client_exitreason) {
190
0
  case CLIENT_EXIT_NONE:
191
0
    break;
192
0
  case CLIENT_EXIT_DETACHED:
193
0
    if (client_exitsession != NULL) {
194
0
      xsnprintf(msg, sizeof msg, "detached "
195
0
          "(from session %s)", client_exitsession);
196
0
      return (msg);
197
0
    }
198
0
    return ("detached");
199
0
  case CLIENT_EXIT_DETACHED_HUP:
200
0
    if (client_exitsession != NULL) {
201
0
      xsnprintf(msg, sizeof msg, "detached and SIGHUP "
202
0
          "(from session %s)", client_exitsession);
203
0
      return (msg);
204
0
    }
205
0
    return ("detached and SIGHUP");
206
0
  case CLIENT_EXIT_LOST_TTY:
207
0
    return ("lost tty");
208
0
  case CLIENT_EXIT_TERMINATED:
209
0
    return ("terminated");
210
0
  case CLIENT_EXIT_LOST_SERVER:
211
0
    return ("server exited unexpectedly");
212
0
  case CLIENT_EXIT_EXITED:
213
0
    return ("exited");
214
0
  case CLIENT_EXIT_SERVER_EXITED:
215
0
    return ("server exited");
216
0
  case CLIENT_EXIT_MESSAGE_PROVIDED:
217
0
    return (client_exitmessage);
218
0
  }
219
0
  return ("unknown reason");
220
0
}
221
222
/* Exit if all streams flushed. */
223
static void
224
client_exit(void)
225
0
{
226
0
  if (!file_write_left(&client_files))
227
0
    proc_exit(client_proc);
228
0
}
229
230
/* Client main loop. */
231
int
232
client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
233
    int feat)
234
0
{
235
0
  struct cmd_parse_result *pr;
236
0
  struct msg_command  *data;
237
0
  int      fd, i;
238
0
  const char    *ttynam, *termname, *cwd;
239
0
  pid_t      ppid;
240
0
  enum msgtype     msg;
241
0
  struct termios     tio, saved_tio;
242
0
  size_t       size, linesize = 0;
243
0
  ssize_t      linelen;
244
0
  char      *line = NULL, **caps = NULL, *cause;
245
0
  u_int      ncaps = 0;
246
0
  struct args_value *values;
247
248
  /* Set up the initial command. */
249
0
  if (shell_command != NULL) {
250
0
    msg = MSG_SHELL;
251
0
    flags |= CLIENT_STARTSERVER;
252
0
  } else if (argc == 0) {
253
0
    msg = MSG_COMMAND;
254
0
    flags |= CLIENT_STARTSERVER;
255
0
  } else {
256
0
    msg = MSG_COMMAND;
257
258
    /*
259
     * It's annoying parsing the command string twice (in client
260
     * and later in server) but it is necessary to get the start
261
     * server flag.
262
     */
263
0
    values = args_from_vector(argc, argv);
264
0
    pr = cmd_parse_from_arguments(values, argc, NULL);
265
0
    if (pr->status == CMD_PARSE_SUCCESS) {
266
0
      if (cmd_list_any_have(pr->cmdlist, CMD_STARTSERVER))
267
0
        flags |= CLIENT_STARTSERVER;
268
0
      cmd_list_free(pr->cmdlist);
269
0
    } else
270
0
      free(pr->error);
271
0
    args_free_values(values, argc);
272
0
    free(values);
273
0
  }
274
275
  /* Create client process structure (starts logging). */
276
0
  client_proc = proc_start("client");
277
0
  proc_set_signals(client_proc, client_signal);
278
279
  /* Save the flags. */
280
0
  client_flags = flags;
281
0
  log_debug("flags are %#llx", (unsigned long long)client_flags);
282
283
  /* Initialize the client socket and start the server. */
284
#ifdef HAVE_SYSTEMD
285
  if (systemd_activated()) {
286
    /* socket-based activation, do not even try to be a client. */
287
    fd = server_start(client_proc, flags, base, 0, NULL);
288
  } else
289
#endif
290
0
  fd = client_connect(base, socket_path, client_flags);
291
0
  if (fd == -1) {
292
0
    if (errno == ECONNREFUSED) {
293
0
      fprintf(stderr, "no server running on %s\n",
294
0
          socket_path);
295
0
    } else {
296
0
      fprintf(stderr, "error connecting to %s (%s)\n",
297
0
          socket_path, strerror(errno));
298
0
    }
299
0
    return (1);
300
0
  }
301
0
  client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL);
302
303
  /* Save these before pledge(). */
304
0
  if ((cwd = find_cwd()) == NULL && (cwd = find_home()) == NULL)
305
0
    cwd = "/";
306
0
  if ((ttynam = ttyname(STDIN_FILENO)) == NULL)
307
0
    ttynam = "";
308
0
  if ((termname = getenv("TERM")) == NULL)
309
0
    termname = "";
310
311
  /*
312
   * Drop privileges for client. "proc exec" is needed for -c and for
313
   * locking (which uses system(3)).
314
   *
315
   * "tty" is needed to restore termios(4) and also for some reason -CC
316
   * does not work properly without it (input is not recognised).
317
   *
318
   * "sendfd" is dropped later in client_dispatch_wait().
319
   */
320
0
  if (pledge(
321
0
      "stdio rpath wpath cpath unix sendfd proc exec tty",
322
0
      NULL) != 0)
323
0
    fatal("pledge failed");
324
325
  /* Load terminfo entry if any. */
326
0
  if (isatty(STDIN_FILENO) &&
327
0
      *termname != '\0' &&
328
0
      tty_term_read_list(termname, STDIN_FILENO, &caps, &ncaps,
329
0
      &cause) != 0) {
330
0
    fprintf(stderr, "%s\n", cause);
331
0
    free(cause);
332
0
    return (1);
333
0
  }
334
335
  /* Free stuff that is not used in the client. */
336
0
  if (ptm_fd != -1)
337
0
    close(ptm_fd);
338
0
  options_free(global_options);
339
0
  options_free(global_s_options);
340
0
  options_free(global_w_options);
341
0
  environ_free(global_environ);
342
343
  /* Set up control mode. */
344
0
  if (client_flags & CLIENT_CONTROLCONTROL) {
345
0
    if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
346
0
      fprintf(stderr, "tcgetattr failed: %s\n",
347
0
          strerror(errno));
348
0
      return (1);
349
0
    }
350
0
    cfmakeraw(&tio);
351
0
    tio.c_iflag = ICRNL|IXANY;
352
0
    tio.c_oflag = OPOST|ONLCR;
353
#ifdef NOKERNINFO
354
    tio.c_lflag = NOKERNINFO;
355
#endif
356
0
    tio.c_cflag = CREAD|CS8|HUPCL;
357
0
    tio.c_cc[VMIN] = 1;
358
0
    tio.c_cc[VTIME] = 0;
359
0
    cfsetispeed(&tio, cfgetispeed(&saved_tio));
360
0
    cfsetospeed(&tio, cfgetospeed(&saved_tio));
361
0
    tcsetattr(STDIN_FILENO, TCSANOW, &tio);
362
0
  }
363
364
  /* Send identify messages. */
365
0
  client_send_identify(ttynam, termname, caps, ncaps, cwd, feat);
366
0
  tty_term_free_list(caps, ncaps);
367
0
  proc_flush_peer(client_peer);
368
369
  /* Send first command. */
370
0
  if (msg == MSG_COMMAND) {
371
    /* How big is the command? */
372
0
    size = 0;
373
0
    for (i = 0; i < argc; i++)
374
0
      size += strlen(argv[i]) + 1;
375
0
    if (size > MAX_IMSGSIZE - (sizeof *data)) {
376
0
      fprintf(stderr, "command too long\n");
377
0
      return (1);
378
0
    }
379
0
    data = xmalloc((sizeof *data) + size);
380
381
    /* Prepare command for server. */
382
0
    data->argc = argc;
383
0
    if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) {
384
0
      fprintf(stderr, "command too long\n");
385
0
      free(data);
386
0
      return (1);
387
0
    }
388
0
    size += sizeof *data;
389
390
    /* Send the command. */
391
0
    if (proc_send(client_peer, msg, -1, data, size) != 0) {
392
0
      fprintf(stderr, "failed to send command\n");
393
0
      free(data);
394
0
      return (1);
395
0
    }
396
0
    free(data);
397
0
  } else if (msg == MSG_SHELL)
398
0
    proc_send(client_peer, msg, -1, NULL, 0);
399
400
  /* Start main loop. */
401
0
  proc_loop(client_proc, NULL);
402
403
  /* Run command if user requested exec, instead of exiting. */
404
0
  if (client_exittype == MSG_EXEC) {
405
0
    if (client_flags & CLIENT_CONTROLCONTROL)
406
0
      tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
407
0
    client_exec(client_execshell, client_execcmd);
408
0
  }
409
410
  /* Restore streams to blocking. */
411
0
  setblocking(STDIN_FILENO, 1);
412
0
  setblocking(STDOUT_FILENO, 1);
413
0
  setblocking(STDERR_FILENO, 1);
414
415
  /* Print the exit message, if any, and exit. */
416
0
  if (client_attached) {
417
0
    if (client_exitreason != CLIENT_EXIT_NONE)
418
0
      printf("[%s]\n", client_exit_message());
419
420
0
    ppid = getppid();
421
0
    if (client_exittype == MSG_DETACHKILL && ppid > 1)
422
0
      kill(ppid, SIGHUP);
423
0
  } else if (client_flags & CLIENT_CONTROL) {
424
0
    if (client_exitreason != CLIENT_EXIT_NONE)
425
0
      printf("%%exit %s\n", client_exit_message());
426
0
    else
427
0
      printf("%%exit\n");
428
0
    fflush(stdout);
429
0
    if (client_flags & CLIENT_CONTROL_WAITEXIT) {
430
0
      setvbuf(stdin, NULL, _IOLBF, 0);
431
0
      for (;;) {
432
0
        linelen = getline(&line, &linesize, stdin);
433
0
        if (linelen <= 1)
434
0
          break;
435
0
      }
436
0
      free(line);
437
0
    }
438
0
    if (client_flags & CLIENT_CONTROLCONTROL) {
439
0
      printf("\033\\");
440
0
      fflush(stdout);
441
0
      tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
442
0
    }
443
0
  } else if (client_exitreason != CLIENT_EXIT_NONE)
444
0
    fprintf(stderr, "%s\n", client_exit_message());
445
0
  return (client_exitval);
446
0
}
447
448
/* Send identify messages to server. */
449
static void
450
client_send_identify(const char *ttynam, const char *termname, char **caps,
451
    u_int ncaps, const char *cwd, int feat)
452
0
{
453
0
  char  **ss;
454
0
  size_t    sslen;
455
0
  int   fd;
456
0
  uint64_t  flags = client_flags;
457
0
  pid_t   pid;
458
0
  u_int   i;
459
460
0
  proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &flags, sizeof flags);
461
0
  proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags,
462
0
      sizeof client_flags);
463
464
0
  proc_send(client_peer, MSG_IDENTIFY_TERM, -1, termname,
465
0
      strlen(termname) + 1);
466
0
  proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat);
467
468
0
  proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam,
469
0
      strlen(ttynam) + 1);
470
0
  proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1);
471
472
0
  for (i = 0; i < ncaps; i++) {
473
0
    proc_send(client_peer, MSG_IDENTIFY_TERMINFO, -1,
474
0
        caps[i], strlen(caps[i]) + 1);
475
0
  }
476
477
0
  if ((fd = dup(STDIN_FILENO)) == -1)
478
0
    fatal("dup failed");
479
0
  proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0);
480
0
  if ((fd = dup(STDOUT_FILENO)) == -1)
481
0
    fatal("dup failed");
482
0
  proc_send(client_peer, MSG_IDENTIFY_STDOUT, fd, NULL, 0);
483
484
0
  pid = getpid();
485
0
  proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid);
486
487
0
  for (ss = environ; *ss != NULL; ss++) {
488
0
    sslen = strlen(*ss) + 1;
489
0
    if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
490
0
      continue;
491
0
    proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen);
492
0
  }
493
494
0
  proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
495
0
}
496
497
/* Run command in shell; used for -c. */
498
static __dead void
499
client_exec(const char *shell, const char *shellcmd)
500
0
{
501
0
  char  *argv0;
502
503
0
  log_debug("shell %s, command %s", shell, shellcmd);
504
0
  argv0 = shell_argv0(shell, !!(client_flags & CLIENT_LOGIN));
505
0
  setenv("SHELL", shell, 1);
506
507
0
  proc_clear_signals(client_proc, 1);
508
509
0
  setblocking(STDIN_FILENO, 1);
510
0
  setblocking(STDOUT_FILENO, 1);
511
0
  setblocking(STDERR_FILENO, 1);
512
0
  closefrom(STDERR_FILENO + 1);
513
514
0
  execl(shell, argv0, "-c", shellcmd, (char *) NULL);
515
0
  fatal("execl failed");
516
0
}
517
518
/* Callback to handle signals in the client. */
519
static void
520
client_signal(int sig)
521
0
{
522
0
  struct sigaction sigact;
523
0
  int    status;
524
0
  pid_t    pid;
525
526
0
  log_debug("%s: %s", __func__, strsignal(sig));
527
0
  if (sig == SIGCHLD) {
528
0
    for (;;) {
529
0
      pid = waitpid(WAIT_ANY, &status, WNOHANG);
530
0
      if (pid == 0)
531
0
        break;
532
0
      if (pid == -1) {
533
0
        if (errno == ECHILD)
534
0
          break;
535
0
        log_debug("waitpid failed: %s",
536
0
            strerror(errno));
537
0
      }
538
0
    }
539
0
  } else if (!client_attached) {
540
0
    if (sig == SIGTERM || sig == SIGHUP)
541
0
      proc_exit(client_proc);
542
0
  } else {
543
0
    switch (sig) {
544
0
    case SIGHUP:
545
0
      client_exitreason = CLIENT_EXIT_LOST_TTY;
546
0
      client_exitval = 1;
547
0
      proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
548
0
      break;
549
0
    case SIGTERM:
550
0
      if (!client_suspended)
551
0
        client_exitreason = CLIENT_EXIT_TERMINATED;
552
0
      client_exitval = 1;
553
0
      proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
554
0
      break;
555
0
    case SIGWINCH:
556
0
      proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
557
0
      break;
558
0
    case SIGCONT:
559
0
      memset(&sigact, 0, sizeof sigact);
560
0
      sigemptyset(&sigact.sa_mask);
561
0
      sigact.sa_flags = SA_RESTART;
562
0
      sigact.sa_handler = SIG_IGN;
563
0
      if (sigaction(SIGTSTP, &sigact, NULL) != 0)
564
0
        fatal("sigaction failed");
565
0
      proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0);
566
0
      client_suspended = 0;
567
0
      break;
568
0
    }
569
0
  }
570
0
}
571
572
/* Callback for file write error or close. */
573
static void
574
client_file_check_cb(__unused struct client *c, __unused const char *path,
575
    __unused int error, __unused int closed, __unused struct evbuffer *buffer,
576
    __unused void *data)
577
0
{
578
0
  if (client_exitflag)
579
0
    client_exit();
580
0
}
581
582
/* Callback for client read events. */
583
static void
584
client_dispatch(struct imsg *imsg, __unused void *arg)
585
0
{
586
0
  if (imsg == NULL) {
587
0
    if (!client_exitflag) {
588
0
      client_exitreason = CLIENT_EXIT_LOST_SERVER;
589
0
      client_exitval = 1;
590
0
    }
591
0
    proc_exit(client_proc);
592
0
    return;
593
0
  }
594
595
0
  if (client_attached)
596
0
    client_dispatch_attached(imsg);
597
0
  else
598
0
    client_dispatch_wait(imsg);
599
0
}
600
601
/* Process an exit message. */
602
static void
603
client_dispatch_exit_message(char *data, size_t datalen)
604
0
{
605
0
  int retval;
606
607
0
  if (datalen < sizeof retval && datalen != 0)
608
0
    fatalx("bad MSG_EXIT size");
609
610
0
  if (datalen >= sizeof retval) {
611
0
    memcpy(&retval, data, sizeof retval);
612
0
    client_exitval = retval;
613
0
  }
614
615
0
  if (datalen > sizeof retval) {
616
0
    datalen -= sizeof retval;
617
0
    data += sizeof retval;
618
619
0
    client_exitmessage = xmalloc(datalen);
620
0
    memcpy(client_exitmessage, data, datalen);
621
0
    client_exitmessage[datalen - 1] = '\0';
622
623
0
    client_exitreason = CLIENT_EXIT_MESSAGE_PROVIDED;
624
0
  }
625
0
}
626
627
/* Dispatch imsgs when in wait state (before MSG_READY). */
628
static void
629
client_dispatch_wait(struct imsg *imsg)
630
0
{
631
0
  char    *data;
632
0
  ssize_t    datalen;
633
0
  static int   pledge_applied;
634
635
  /*
636
   * "sendfd" is no longer required once all of the identify messages
637
   * have been sent. We know the server won't send us anything until that
638
   * point (because we don't ask it to), so we can drop "sendfd" once we
639
   * get the first message from the server.
640
   */
641
0
  if (!pledge_applied) {
642
0
    if (pledge(
643
0
        "stdio rpath wpath cpath unix proc exec tty",
644
0
        NULL) != 0)
645
0
      fatal("pledge failed");
646
0
    pledge_applied = 1;
647
0
  }
648
649
0
  data = imsg->data;
650
0
  datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
651
652
0
  switch (imsg->hdr.type) {
653
0
  case MSG_EXIT:
654
0
  case MSG_SHUTDOWN:
655
0
    client_dispatch_exit_message(data, datalen);
656
0
    client_exitflag = 1;
657
0
    client_exit();
658
0
    break;
659
0
  case MSG_READY:
660
0
    if (datalen != 0)
661
0
      fatalx("bad MSG_READY size");
662
663
0
    client_attached = 1;
664
0
    proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
665
0
    break;
666
0
  case MSG_VERSION:
667
0
    if (datalen != 0)
668
0
      fatalx("bad MSG_VERSION size");
669
670
0
    fprintf(stderr, "protocol version mismatch "
671
0
        "(client %d, server %u)\n", PROTOCOL_VERSION,
672
0
        imsg->hdr.peerid & 0xff);
673
0
    client_exitval = 1;
674
0
    proc_exit(client_proc);
675
0
    break;
676
0
  case MSG_FLAGS:
677
0
    if (datalen != sizeof client_flags)
678
0
      fatalx("bad MSG_FLAGS string");
679
680
0
    memcpy(&client_flags, data, sizeof client_flags);
681
0
    log_debug("new flags are %#llx",
682
0
        (unsigned long long)client_flags);
683
0
    break;
684
0
  case MSG_SHELL:
685
0
    if (datalen == 0 || data[datalen - 1] != '\0')
686
0
      fatalx("bad MSG_SHELL string");
687
688
0
    client_exec(data, shell_command);
689
    /* NOTREACHED */
690
0
  case MSG_DETACH:
691
0
  case MSG_DETACHKILL:
692
0
    proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
693
0
    break;
694
0
  case MSG_EXITED:
695
0
    proc_exit(client_proc);
696
0
    break;
697
0
  case MSG_READ_OPEN:
698
0
    file_read_open(&client_files, client_peer, imsg, 1,
699
0
        !(client_flags & CLIENT_CONTROL), client_file_check_cb,
700
0
        NULL);
701
0
    break;
702
0
  case MSG_READ_CANCEL:
703
0
    file_read_cancel(&client_files, imsg);
704
0
    break;
705
0
  case MSG_WRITE_OPEN:
706
0
    file_write_open(&client_files, client_peer, imsg, 1,
707
0
        !(client_flags & CLIENT_CONTROL), client_file_check_cb,
708
0
        NULL);
709
0
    break;
710
0
  case MSG_WRITE:
711
0
    file_write_data(&client_files, imsg);
712
0
    break;
713
0
  case MSG_WRITE_CLOSE:
714
0
    file_write_close(&client_files, imsg);
715
0
    break;
716
0
  case MSG_OLDSTDERR:
717
0
  case MSG_OLDSTDIN:
718
0
  case MSG_OLDSTDOUT:
719
0
    fprintf(stderr, "server version is too old for client\n");
720
0
    proc_exit(client_proc);
721
0
    break;
722
0
  }
723
0
}
724
725
/* Dispatch imsgs in attached state (after MSG_READY). */
726
static void
727
client_dispatch_attached(struct imsg *imsg)
728
0
{
729
0
  struct sigaction   sigact;
730
0
  char      *data;
731
0
  ssize_t      datalen;
732
733
0
  data = imsg->data;
734
0
  datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
735
736
0
  switch (imsg->hdr.type) {
737
0
  case MSG_FLAGS:
738
0
    if (datalen != sizeof client_flags)
739
0
      fatalx("bad MSG_FLAGS string");
740
741
0
    memcpy(&client_flags, data, sizeof client_flags);
742
0
    log_debug("new flags are %#llx",
743
0
        (unsigned long long)client_flags);
744
0
    break;
745
0
  case MSG_DETACH:
746
0
  case MSG_DETACHKILL:
747
0
    if (datalen == 0 || data[datalen - 1] != '\0')
748
0
      fatalx("bad MSG_DETACH string");
749
750
0
    client_exitsession = xstrdup(data);
751
0
    client_exittype = imsg->hdr.type;
752
0
    if (imsg->hdr.type == MSG_DETACHKILL)
753
0
      client_exitreason = CLIENT_EXIT_DETACHED_HUP;
754
0
    else
755
0
      client_exitreason = CLIENT_EXIT_DETACHED;
756
0
    proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
757
0
    break;
758
0
  case MSG_EXEC:
759
0
    if (datalen == 0 || data[datalen - 1] != '\0' ||
760
0
        strlen(data) + 1 == (size_t)datalen)
761
0
      fatalx("bad MSG_EXEC string");
762
0
    client_execcmd = xstrdup(data);
763
0
    client_execshell = xstrdup(data + strlen(data) + 1);
764
765
0
    client_exittype = imsg->hdr.type;
766
0
    proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
767
0
    break;
768
0
  case MSG_EXIT:
769
0
    client_dispatch_exit_message(data, datalen);
770
0
    if (client_exitreason == CLIENT_EXIT_NONE)
771
0
      client_exitreason = CLIENT_EXIT_EXITED;
772
0
    proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
773
0
    break;
774
0
  case MSG_EXITED:
775
0
    if (datalen != 0)
776
0
      fatalx("bad MSG_EXITED size");
777
778
0
    proc_exit(client_proc);
779
0
    break;
780
0
  case MSG_SHUTDOWN:
781
0
    if (datalen != 0)
782
0
      fatalx("bad MSG_SHUTDOWN size");
783
784
0
    proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
785
0
    client_exitreason = CLIENT_EXIT_SERVER_EXITED;
786
0
    client_exitval = 1;
787
0
    break;
788
0
  case MSG_SUSPEND:
789
0
    if (datalen != 0)
790
0
      fatalx("bad MSG_SUSPEND size");
791
792
0
    memset(&sigact, 0, sizeof sigact);
793
0
    sigemptyset(&sigact.sa_mask);
794
0
    sigact.sa_flags = SA_RESTART;
795
0
    sigact.sa_handler = SIG_DFL;
796
0
    if (sigaction(SIGTSTP, &sigact, NULL) != 0)
797
0
      fatal("sigaction failed");
798
0
    client_suspended = 1;
799
0
    kill(getpid(), SIGTSTP);
800
0
    break;
801
0
  case MSG_LOCK:
802
0
    if (datalen == 0 || data[datalen - 1] != '\0')
803
0
      fatalx("bad MSG_LOCK string");
804
805
0
    system(data);
806
0
    proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0);
807
0
    break;
808
0
  }
809
0
}