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 | } |