Coverage Report

Created: 2026-07-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/proc.c
Line
Count
Source
1
/* $OpenBSD: proc.c,v 1.31 2026/06/08 21:38:19 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2015 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/utsname.h>
23
24
#include <errno.h>
25
#include <signal.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <unistd.h>
29
30
#if defined(HAVE_NCURSES_H)
31
#include <ncurses.h>
32
#endif
33
34
#ifdef HAVE_JEMALLOC
35
#include <jemalloc/jemalloc.h>
36
#endif
37
38
#include "tmux.h"
39
40
struct tmuxproc {
41
  const char   *name;
42
  int     exit;
43
44
  void    (*signalcb)(int);
45
46
  struct event    ev_sigint;
47
  struct event    ev_sighup;
48
  struct event    ev_sigchld;
49
  struct event    ev_sigcont;
50
  struct event    ev_sigterm;
51
  struct event    ev_sigusr1;
52
  struct event    ev_sigusr2;
53
  struct event    ev_sigwinch;
54
55
  TAILQ_HEAD(, tmuxpeer) peers;
56
};
57
58
struct tmuxpeer {
59
  struct tmuxproc *parent;
60
61
  struct imsgbuf   ibuf;
62
  struct event   event;
63
  uid_t    uid;
64
  gid_t    gid;
65
66
  int    flags;
67
0
#define PEER_BAD 0x1
68
69
  void    (*dispatchcb)(struct imsg *, void *);
70
  void     *arg;
71
72
  TAILQ_ENTRY(tmuxpeer) entry;
73
};
74
75
static int  peer_check_version(struct tmuxpeer *, struct imsg *);
76
static void proc_update_event(struct tmuxpeer *);
77
78
static void
79
proc_event_cb(__unused int fd, short events, void *arg)
80
0
{
81
0
  struct tmuxpeer *peer = arg;
82
0
  ssize_t    n;
83
0
  struct imsg  imsg;
84
85
0
  if (!(peer->flags & PEER_BAD) && (events & EV_READ)) {
86
0
    if (imsgbuf_read(&peer->ibuf) != 1) {
87
0
      peer->dispatchcb(NULL, peer->arg);
88
0
      return;
89
0
    }
90
0
    for (;;) {
91
0
      if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) {
92
0
        peer->dispatchcb(NULL, peer->arg);
93
0
        return;
94
0
      }
95
0
      if (n == 0)
96
0
        break;
97
0
      log_debug("peer %p message %d", peer, imsg.hdr.type);
98
99
0
      if (peer_check_version(peer, &imsg) != 0) {
100
0
        imsg_free(&imsg);
101
0
        break;
102
0
      }
103
104
0
      peer->dispatchcb(&imsg, peer->arg);
105
0
      imsg_free(&imsg);
106
0
    }
107
0
  }
108
109
0
  if (events & EV_WRITE) {
110
0
    if (imsgbuf_write(&peer->ibuf) == -1) {
111
0
      peer->dispatchcb(NULL, peer->arg);
112
0
      return;
113
0
    }
114
0
  }
115
116
0
  if ((peer->flags & PEER_BAD) && imsgbuf_queuelen(&peer->ibuf) == 0) {
117
0
    peer->dispatchcb(NULL, peer->arg);
118
0
    return;
119
0
  }
120
121
0
  proc_update_event(peer);
122
0
}
123
124
static void
125
proc_signal_cb(int signo, __unused short events, void *arg)
126
0
{
127
0
  struct tmuxproc *tp = arg;
128
129
0
  tp->signalcb(signo);
130
0
}
131
132
static int
133
peer_check_version(struct tmuxpeer *peer, struct imsg *imsg)
134
0
{
135
0
  int version;
136
137
0
  version = imsg->hdr.peerid & 0xff;
138
0
  if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) {
139
0
    log_debug("peer %p bad version %d", peer, version);
140
141
0
    proc_send(peer, MSG_VERSION, -1, NULL, 0);
142
0
    peer->flags |= PEER_BAD;
143
144
0
    return (-1);
145
0
  }
146
0
  return (0);
147
0
}
148
149
static void
150
proc_update_event(struct tmuxpeer *peer)
151
0
{
152
0
  short events;
153
154
0
  event_del(&peer->event);
155
156
0
  events = EV_READ;
157
0
  if (imsgbuf_queuelen(&peer->ibuf) > 0)
158
0
    events |= EV_WRITE;
159
0
  event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer);
160
161
0
  event_add(&peer->event, NULL);
162
0
}
163
164
int
165
proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf,
166
    size_t len)
167
0
{
168
0
  struct imsgbuf  *ibuf = &peer->ibuf;
169
0
  void    *vp = (void *)buf;
170
0
  int    retval;
171
172
0
  if (peer->flags & PEER_BAD)
173
0
    return (-1);
174
0
  log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len);
175
176
0
  retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len);
177
0
  if (retval != 1)
178
0
    return (-1);
179
0
  proc_update_event(peer);
180
0
  return (0);
181
0
}
182
183
struct tmuxproc *
184
proc_start(const char *name)
185
0
{
186
0
  struct tmuxproc *tp;
187
0
  struct utsname   u;
188
#ifdef HAVE_JEMALLOC
189
  const char  *version;
190
  size_t     size = sizeof version;
191
#endif
192
193
0
  log_open(name);
194
0
  setproctitle("%s (%s)", name, socket_path);
195
196
0
  if (uname(&u) < 0)
197
0
    memset(&u, 0, sizeof u);
198
199
0
  log_debug("%s started (%ld): version %s, socket %s, protocol %d", name,
200
0
      (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION);
201
0
  log_debug("on %s %s %s", u.sysname, u.release, u.version);
202
0
  log_debug("using libevent %s %s", event_get_version(), event_get_method());
203
#ifdef HAVE_UTF8PROC
204
  log_debug("using utf8proc %s", utf8proc_version());
205
#endif
206
#ifdef HAVE_JEMALLOC
207
  if (mallctl("version", &version, &size, NULL, 0) != 0)
208
    version = "(unknown version)";
209
  log_debug("using jemalloc %s", version);
210
#endif
211
0
#ifdef NCURSES_VERSION
212
0
  log_debug("using ncurses %s %06u", NCURSES_VERSION, NCURSES_VERSION_PATCH);
213
0
#endif
214
215
0
  tp = xcalloc(1, sizeof *tp);
216
0
  tp->name = xstrdup(name);
217
0
  TAILQ_INIT(&tp->peers);
218
219
0
  return (tp);
220
0
}
221
222
void
223
proc_loop(struct tmuxproc *tp, int (*loopcb)(void))
224
0
{
225
0
  log_debug("%s loop enter", tp->name);
226
0
  do
227
0
    event_loop(EVLOOP_ONCE);
228
0
  while (!tp->exit && (loopcb == NULL || !loopcb ()));
229
0
  log_debug("%s loop exit", tp->name);
230
0
}
231
232
void
233
proc_exit(struct tmuxproc *tp)
234
0
{
235
0
  struct tmuxpeer *peer;
236
237
0
  TAILQ_FOREACH(peer, &tp->peers, entry)
238
0
      imsgbuf_flush(&peer->ibuf);
239
0
  tp->exit = 1;
240
0
}
241
242
void
243
proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int))
244
0
{
245
0
  struct sigaction  sa;
246
247
0
  tp->signalcb = signalcb;
248
249
0
  memset(&sa, 0, sizeof sa);
250
0
  sigemptyset(&sa.sa_mask);
251
0
  sa.sa_flags = SA_RESTART;
252
0
  sa.sa_handler = SIG_IGN;
253
254
0
  sigaction(SIGPIPE, &sa, NULL);
255
0
  sigaction(SIGTSTP, &sa, NULL);
256
0
  sigaction(SIGTTIN, &sa, NULL);
257
0
  sigaction(SIGTTOU, &sa, NULL);
258
0
  sigaction(SIGQUIT, &sa, NULL);
259
260
0
  signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp);
261
0
  signal_add(&tp->ev_sigint, NULL);
262
0
  signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp);
263
0
  signal_add(&tp->ev_sighup, NULL);
264
0
  signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp);
265
0
  signal_add(&tp->ev_sigchld, NULL);
266
0
  signal_set(&tp->ev_sigcont, SIGCONT, proc_signal_cb, tp);
267
0
  signal_add(&tp->ev_sigcont, NULL);
268
0
  signal_set(&tp->ev_sigterm, SIGTERM, proc_signal_cb, tp);
269
0
  signal_add(&tp->ev_sigterm, NULL);
270
0
  signal_set(&tp->ev_sigusr1, SIGUSR1, proc_signal_cb, tp);
271
0
  signal_add(&tp->ev_sigusr1, NULL);
272
0
  signal_set(&tp->ev_sigusr2, SIGUSR2, proc_signal_cb, tp);
273
0
  signal_add(&tp->ev_sigusr2, NULL);
274
0
  signal_set(&tp->ev_sigwinch, SIGWINCH, proc_signal_cb, tp);
275
0
  signal_add(&tp->ev_sigwinch, NULL);
276
0
}
277
278
void
279
proc_clear_signals(struct tmuxproc *tp, int defaults)
280
0
{
281
0
  struct sigaction  sa;
282
283
0
  memset(&sa, 0, sizeof sa);
284
0
  sigemptyset(&sa.sa_mask);
285
0
  sa.sa_flags = SA_RESTART;
286
0
  sa.sa_handler = SIG_DFL;
287
288
0
  sigaction(SIGPIPE, &sa, NULL);
289
0
  sigaction(SIGTSTP, &sa, NULL);
290
291
0
  signal_del(&tp->ev_sigint);
292
0
  signal_del(&tp->ev_sighup);
293
0
  signal_del(&tp->ev_sigchld);
294
0
  signal_del(&tp->ev_sigcont);
295
0
  signal_del(&tp->ev_sigterm);
296
0
  signal_del(&tp->ev_sigusr1);
297
0
  signal_del(&tp->ev_sigusr2);
298
0
  signal_del(&tp->ev_sigwinch);
299
300
0
  if (defaults) {
301
0
    sigaction(SIGINT, &sa, NULL);
302
0
    sigaction(SIGQUIT, &sa, NULL);
303
0
    sigaction(SIGHUP, &sa, NULL);
304
0
    sigaction(SIGCHLD, &sa, NULL);
305
0
    sigaction(SIGCONT, &sa, NULL);
306
0
    sigaction(SIGTERM, &sa, NULL);
307
0
    sigaction(SIGUSR1, &sa, NULL);
308
0
    sigaction(SIGUSR2, &sa, NULL);
309
0
    sigaction(SIGWINCH, &sa, NULL);
310
0
  }
311
0
}
312
313
struct tmuxpeer *
314
proc_add_peer(struct tmuxproc *tp, int fd,
315
    void (*dispatchcb)(struct imsg *, void *), void *arg)
316
0
{
317
0
  struct tmuxpeer *peer;
318
319
0
  peer = xcalloc(1, sizeof *peer);
320
0
  peer->parent = tp;
321
322
0
  peer->dispatchcb = dispatchcb;
323
0
  peer->arg = arg;
324
325
0
  if (imsgbuf_init(&peer->ibuf, fd) == -1)
326
0
    fatal("imsgbuf_init");
327
0
  imsgbuf_allow_fdpass(&peer->ibuf);
328
0
  event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
329
330
0
  if (getpeereid(fd, &peer->uid, &peer->gid) != 0) {
331
0
    peer->uid = (uid_t)-1;
332
0
    peer->gid = (gid_t)-1;
333
0
  }
334
335
0
  log_debug("add peer %p: %d (%p)", peer, fd, arg);
336
0
  TAILQ_INSERT_TAIL(&tp->peers, peer, entry);
337
338
0
  proc_update_event(peer);
339
0
  return (peer);
340
0
}
341
342
void
343
proc_remove_peer(struct tmuxpeer *peer)
344
0
{
345
0
  TAILQ_REMOVE(&peer->parent->peers, peer, entry);
346
0
  log_debug("remove peer %p", peer);
347
348
0
  event_del(&peer->event);
349
0
  imsgbuf_clear(&peer->ibuf);
350
351
0
  close(peer->ibuf.fd);
352
0
  free(peer);
353
0
}
354
355
void
356
proc_kill_peer(struct tmuxpeer *peer)
357
0
{
358
0
  peer->flags |= PEER_BAD;
359
0
}
360
361
void
362
proc_flush_peer(struct tmuxpeer *peer)
363
0
{
364
0
  imsgbuf_flush(&peer->ibuf);
365
0
}
366
367
void
368
proc_toggle_log(struct tmuxproc *tp)
369
0
{
370
0
  log_toggle(tp->name);
371
0
}
372
373
pid_t
374
proc_fork_and_daemon(int *fd)
375
0
{
376
0
  pid_t pid;
377
0
  int pair[2];
378
379
0
  if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
380
0
    fatal("socketpair failed");
381
0
  switch (pid = fork()) {
382
0
  case -1:
383
0
    fatal("fork failed");
384
0
  case 0:
385
0
    close(pair[0]);
386
0
    *fd = pair[1];
387
0
    if (daemon(1, 0) != 0)
388
0
      fatal("daemon failed");
389
0
    return (0);
390
0
  default:
391
0
    close(pair[1]);
392
0
    *fd = pair[0];
393
0
    return (pid);
394
0
  }
395
0
}
396
397
uid_t
398
proc_get_peer_uid(struct tmuxpeer *peer)
399
0
{
400
0
  return (peer->uid);
401
0
}
402
403
gid_t
404
proc_get_peer_gid(struct tmuxpeer *peer)
405
0
{
406
0
  return (peer->gid);
407
0
}