1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2015 Nicholas Marriott <[email protected]> |
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/uio.h> |
21 | #include <sys/utsname.h> |
22 | |
23 | #include <errno.h> |
24 | #include <event.h> |
25 | #include <signal.h> |
26 | #include <stdlib.h> |
27 | #include <string.h> |
28 | #include <unistd.h> |
29 | |
30 | #include "tmux.h" |
31 | |
32 | struct tmuxproc { |
33 | const char *name; |
34 | int exit; |
35 | |
36 | void (*signalcb)(int); |
37 | |
38 | struct event ev_sighup; |
39 | struct event ev_sigchld; |
40 | struct event ev_sigcont; |
41 | struct event ev_sigterm; |
42 | struct event ev_sigusr1; |
43 | struct event ev_sigusr2; |
44 | struct event ev_sigwinch; |
45 | }; |
46 | |
47 | struct tmuxpeer { |
48 | struct tmuxproc *parent; |
49 | |
50 | struct imsgbuf ibuf; |
51 | struct event event; |
52 | |
53 | int flags; |
54 | #define PEER_BAD 0x1 |
55 | |
56 | void (*dispatchcb)(struct imsg *, void *); |
57 | void *arg; |
58 | }; |
59 | |
60 | static int peer_check_version(struct tmuxpeer *, struct imsg *); |
61 | static void proc_update_event(struct tmuxpeer *); |
62 | |
63 | static void |
64 | proc_event_cb(__unused int fd, short events, void *arg) |
65 | { |
66 | struct tmuxpeer *peer = arg; |
67 | ssize_t n; |
68 | struct imsg imsg; |
69 | |
70 | if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { |
71 | if (((n = imsg_read(&peer->ibuf)) == -1 && errno != EAGAIN) || |
72 | n == 0) { |
73 | peer->dispatchcb(NULL, peer->arg); |
74 | return; |
75 | } |
76 | for (;;) { |
77 | if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { |
78 | peer->dispatchcb(NULL, peer->arg); |
79 | return; |
80 | } |
81 | if (n == 0) |
82 | break; |
83 | log_debug("peer %p message %d" , peer, imsg.hdr.type); |
84 | |
85 | if (peer_check_version(peer, &imsg) != 0) { |
86 | if (imsg.fd != -1) |
87 | close(imsg.fd); |
88 | imsg_free(&imsg); |
89 | break; |
90 | } |
91 | |
92 | peer->dispatchcb(&imsg, peer->arg); |
93 | imsg_free(&imsg); |
94 | } |
95 | } |
96 | |
97 | if (events & EV_WRITE) { |
98 | if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { |
99 | peer->dispatchcb(NULL, peer->arg); |
100 | return; |
101 | } |
102 | } |
103 | |
104 | if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { |
105 | peer->dispatchcb(NULL, peer->arg); |
106 | return; |
107 | } |
108 | |
109 | proc_update_event(peer); |
110 | } |
111 | |
112 | static void |
113 | proc_signal_cb(int signo, __unused short events, void *arg) |
114 | { |
115 | struct tmuxproc *tp = arg; |
116 | |
117 | tp->signalcb(signo); |
118 | } |
119 | |
120 | static int |
121 | peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) |
122 | { |
123 | int version; |
124 | |
125 | version = imsg->hdr.peerid & 0xff; |
126 | if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) { |
127 | log_debug("peer %p bad version %d" , peer, version); |
128 | |
129 | proc_send(peer, MSG_VERSION, -1, NULL, 0); |
130 | peer->flags |= PEER_BAD; |
131 | |
132 | return (-1); |
133 | } |
134 | return (0); |
135 | } |
136 | |
137 | static void |
138 | proc_update_event(struct tmuxpeer *peer) |
139 | { |
140 | short events; |
141 | |
142 | event_del(&peer->event); |
143 | |
144 | events = EV_READ; |
145 | if (peer->ibuf.w.queued > 0) |
146 | events |= EV_WRITE; |
147 | event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); |
148 | |
149 | event_add(&peer->event, NULL); |
150 | } |
151 | |
152 | int |
153 | proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, |
154 | size_t len) |
155 | { |
156 | struct imsgbuf *ibuf = &peer->ibuf; |
157 | void *vp = (void *)buf; |
158 | int retval; |
159 | |
160 | if (peer->flags & PEER_BAD) |
161 | return (-1); |
162 | log_debug("sending message %d to peer %p (%zu bytes)" , type, peer, len); |
163 | |
164 | retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); |
165 | if (retval != 1) |
166 | return (-1); |
167 | proc_update_event(peer); |
168 | return (0); |
169 | } |
170 | |
171 | struct tmuxproc * |
172 | proc_start(const char *name) |
173 | { |
174 | struct tmuxproc *tp; |
175 | struct utsname u; |
176 | |
177 | log_open(name); |
178 | setproctitle("%s (%s)" , name, socket_path); |
179 | |
180 | if (uname(&u) < 0) |
181 | memset(&u, 0, sizeof u); |
182 | |
183 | log_debug("%s started (%ld): version %s, socket %s, protocol %d" , name, |
184 | (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION); |
185 | log_debug("on %s %s %s; libevent %s (%s)" , u.sysname, u.release, |
186 | u.version, event_get_version(), event_get_method()); |
187 | |
188 | tp = xcalloc(1, sizeof *tp); |
189 | tp->name = xstrdup(name); |
190 | |
191 | return (tp); |
192 | } |
193 | |
194 | void |
195 | proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) |
196 | { |
197 | log_debug("%s loop enter" , tp->name); |
198 | do |
199 | event_loop(EVLOOP_ONCE); |
200 | while (!tp->exit && (loopcb == NULL || !loopcb ())); |
201 | log_debug("%s loop exit" , tp->name); |
202 | } |
203 | |
204 | void |
205 | proc_exit(struct tmuxproc *tp) |
206 | { |
207 | tp->exit = 1; |
208 | } |
209 | |
210 | void |
211 | proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int)) |
212 | { |
213 | struct sigaction sa; |
214 | |
215 | tp->signalcb = signalcb; |
216 | |
217 | memset(&sa, 0, sizeof sa); |
218 | sigemptyset(&sa.sa_mask); |
219 | sa.sa_flags = SA_RESTART; |
220 | sa.sa_handler = SIG_IGN; |
221 | |
222 | sigaction(SIGINT, &sa, NULL); |
223 | sigaction(SIGPIPE, &sa, NULL); |
224 | sigaction(SIGTSTP, &sa, NULL); |
225 | |
226 | signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp); |
227 | signal_add(&tp->ev_sighup, NULL); |
228 | signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp); |
229 | signal_add(&tp->ev_sigchld, NULL); |
230 | signal_set(&tp->ev_sigcont, SIGCONT, proc_signal_cb, tp); |
231 | signal_add(&tp->ev_sigcont, NULL); |
232 | signal_set(&tp->ev_sigterm, SIGTERM, proc_signal_cb, tp); |
233 | signal_add(&tp->ev_sigterm, NULL); |
234 | signal_set(&tp->ev_sigusr1, SIGUSR1, proc_signal_cb, tp); |
235 | signal_add(&tp->ev_sigusr1, NULL); |
236 | signal_set(&tp->ev_sigusr2, SIGUSR2, proc_signal_cb, tp); |
237 | signal_add(&tp->ev_sigusr2, NULL); |
238 | signal_set(&tp->ev_sigwinch, SIGWINCH, proc_signal_cb, tp); |
239 | signal_add(&tp->ev_sigwinch, NULL); |
240 | } |
241 | |
242 | void |
243 | proc_clear_signals(struct tmuxproc *tp, int defaults) |
244 | { |
245 | struct sigaction sa; |
246 | |
247 | memset(&sa, 0, sizeof sa); |
248 | sigemptyset(&sa.sa_mask); |
249 | sa.sa_flags = SA_RESTART; |
250 | sa.sa_handler = SIG_DFL; |
251 | |
252 | sigaction(SIGINT, &sa, NULL); |
253 | sigaction(SIGPIPE, &sa, NULL); |
254 | sigaction(SIGTSTP, &sa, NULL); |
255 | |
256 | signal_del(&tp->ev_sighup); |
257 | signal_del(&tp->ev_sigchld); |
258 | signal_del(&tp->ev_sigcont); |
259 | signal_del(&tp->ev_sigterm); |
260 | signal_del(&tp->ev_sigusr1); |
261 | signal_del(&tp->ev_sigusr2); |
262 | signal_del(&tp->ev_sigwinch); |
263 | |
264 | if (defaults) { |
265 | sigaction(SIGHUP, &sa, NULL); |
266 | sigaction(SIGCHLD, &sa, NULL); |
267 | sigaction(SIGCONT, &sa, NULL); |
268 | sigaction(SIGTERM, &sa, NULL); |
269 | sigaction(SIGUSR1, &sa, NULL); |
270 | sigaction(SIGUSR2, &sa, NULL); |
271 | sigaction(SIGWINCH, &sa, NULL); |
272 | } |
273 | } |
274 | |
275 | struct tmuxpeer * |
276 | proc_add_peer(struct tmuxproc *tp, int fd, |
277 | void (*dispatchcb)(struct imsg *, void *), void *arg) |
278 | { |
279 | struct tmuxpeer *peer; |
280 | |
281 | peer = xcalloc(1, sizeof *peer); |
282 | peer->parent = tp; |
283 | |
284 | peer->dispatchcb = dispatchcb; |
285 | peer->arg = arg; |
286 | |
287 | imsg_init(&peer->ibuf, fd); |
288 | event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); |
289 | |
290 | log_debug("add peer %p: %d (%p)" , peer, fd, arg); |
291 | |
292 | proc_update_event(peer); |
293 | return (peer); |
294 | } |
295 | |
296 | void |
297 | proc_remove_peer(struct tmuxpeer *peer) |
298 | { |
299 | log_debug("remove peer %p" , peer); |
300 | |
301 | event_del(&peer->event); |
302 | imsg_clear(&peer->ibuf); |
303 | |
304 | close(peer->ibuf.fd); |
305 | free(peer); |
306 | } |
307 | |
308 | void |
309 | proc_kill_peer(struct tmuxpeer *peer) |
310 | { |
311 | peer->flags |= PEER_BAD; |
312 | } |
313 | |
314 | void |
315 | proc_toggle_log(struct tmuxproc *tp) |
316 | { |
317 | log_toggle(tp->name); |
318 | } |
319 | |