Coverage Report

Created: 2025-08-09 06:27

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