1 /*
2 * This file is part of mpv.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <pthread.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <limits.h>
22 #include <poll.h>
23 #include <signal.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <sys/un.h>
28
29 #include "config.h"
30
31 #include "osdep/io.h"
32 #include "osdep/threads.h"
33
34 #include "common/common.h"
35 #include "common/global.h"
36 #include "common/msg.h"
37 #include "input/input.h"
38 #include "libmpv/client.h"
39 #include "options/m_config.h"
40 #include "options/options.h"
41 #include "options/path.h"
42 #include "player/client.h"
43
44 #ifndef MSG_NOSIGNAL
45 #define MSG_NOSIGNAL 0
46 #endif
47
48 struct mp_ipc_ctx {
49 struct mp_log *log;
50 struct mp_client_api *client_api;
51 const char *path;
52
53 pthread_t thread;
54 int death_pipe[2];
55 };
56
57 struct client_arg {
58 struct mp_log *log;
59 struct mpv_handle *client;
60
61 const char *client_name;
62 int client_fd;
63 bool close_client_fd;
64 bool quit_on_close;
65
66 bool writable;
67 };
68
ipc_write_str(struct client_arg * client,const char * buf)69 static int ipc_write_str(struct client_arg *client, const char *buf)
70 {
71 size_t count = strlen(buf);
72 while (count > 0) {
73 ssize_t rc = send(client->client_fd, buf, count, MSG_NOSIGNAL);
74 if (rc <= 0) {
75 if (rc == 0)
76 return -1;
77
78 if (errno == EBADF || errno == ENOTSOCK) {
79 client->writable = false;
80 return 0;
81 }
82
83 if (errno == EINTR || errno == EAGAIN)
84 continue;
85
86 return rc;
87 }
88
89 count -= rc;
90 buf += rc;
91 }
92
93 return 0;
94 }
95
client_thread(void * p)96 static void *client_thread(void *p)
97 {
98 pthread_detach(pthread_self());
99
100 // We don't use MSG_NOSIGNAL because the moldy fruit OS doesn't support it.
101 struct sigaction sa = { .sa_handler = SIG_IGN, .sa_flags = SA_RESTART };
102 sigfillset(&sa.sa_mask);
103 sigaction(SIGPIPE, &sa, NULL);
104
105 int rc;
106
107 struct client_arg *arg = p;
108 bstr client_msg = { talloc_strdup(NULL, ""), 0 };
109
110 mpthread_set_name(arg->client_name);
111
112 int pipe_fd = mpv_get_wakeup_pipe(arg->client);
113 if (pipe_fd < 0) {
114 MP_ERR(arg, "Could not get wakeup pipe\n");
115 goto done;
116 }
117
118 MP_VERBOSE(arg, "Client connected\n");
119
120 struct pollfd fds[2] = {
121 {.events = POLLIN, .fd = pipe_fd},
122 {.events = POLLIN, .fd = arg->client_fd},
123 };
124
125 fcntl(arg->client_fd, F_SETFL, fcntl(arg->client_fd, F_GETFL, 0) | O_NONBLOCK);
126
127 while (1) {
128 rc = poll(fds, 2, 0);
129 if (rc == 0)
130 rc = poll(fds, 2, -1);
131 if (rc < 0) {
132 MP_ERR(arg, "Poll error\n");
133 continue;
134 }
135
136 if (fds[0].revents & POLLIN) {
137 mp_flush_wakeup_pipe(pipe_fd);
138
139 while (1) {
140 mpv_event *event = mpv_wait_event(arg->client, 0);
141
142 if (event->event_id == MPV_EVENT_NONE)
143 break;
144
145 if (event->event_id == MPV_EVENT_SHUTDOWN)
146 goto done;
147
148 if (!arg->writable)
149 continue;
150
151 char *event_msg = mp_json_encode_event(event);
152 if (!event_msg) {
153 MP_ERR(arg, "Encoding error\n");
154 goto done;
155 }
156
157 rc = ipc_write_str(arg, event_msg);
158 talloc_free(event_msg);
159 if (rc < 0) {
160 MP_ERR(arg, "Write error (%s)\n", mp_strerror(errno));
161 goto done;
162 }
163 }
164 }
165
166 if (fds[1].revents & (POLLIN | POLLHUP | POLLNVAL)) {
167 while (1) {
168 char buf[128];
169 bstr append = { buf, 0 };
170
171 ssize_t bytes = read(arg->client_fd, buf, sizeof(buf));
172 if (bytes < 0) {
173 if (errno == EAGAIN)
174 break;
175
176 MP_ERR(arg, "Read error (%s)\n", mp_strerror(errno));
177 goto done;
178 }
179
180 if (bytes == 0) {
181 MP_VERBOSE(arg, "Client disconnected\n");
182 goto done;
183 }
184
185 append.len = bytes;
186
187 bstr_xappend(NULL, &client_msg, append);
188
189 while (bstrchr(client_msg, '\n') != -1) {
190 char *reply_msg = mp_ipc_consume_next_command(arg->client,
191 NULL, &client_msg);
192
193 if (reply_msg && arg->writable) {
194 rc = ipc_write_str(arg, reply_msg);
195 if (rc < 0) {
196 MP_ERR(arg, "Write error (%s)\n", mp_strerror(errno));
197 talloc_free(reply_msg);
198 goto done;
199 }
200 }
201
202 talloc_free(reply_msg);
203 }
204 }
205 }
206 }
207
208 done:
209 if (client_msg.len > 0)
210 MP_WARN(arg, "Ignoring unterminated command on disconnect.\n");
211 talloc_free(client_msg.start);
212 if (arg->close_client_fd)
213 close(arg->client_fd);
214 struct mpv_handle *h = arg->client;
215 bool quit = arg->quit_on_close;
216 talloc_free(arg);
217 if (quit) {
218 mpv_terminate_destroy(h);
219 } else {
220 mpv_destroy(h);
221 }
222 return NULL;
223 }
224
ipc_start_client(struct mp_ipc_ctx * ctx,struct client_arg * client,bool free_on_init_fail)225 static bool ipc_start_client(struct mp_ipc_ctx *ctx, struct client_arg *client,
226 bool free_on_init_fail)
227 {
228 if (!client->client)
229 client->client = mp_new_client(ctx->client_api, client->client_name);
230 if (!client->client)
231 goto err;
232
233 client->log = mp_client_get_log(client->client);
234
235 pthread_t client_thr;
236 if (pthread_create(&client_thr, NULL, client_thread, client))
237 goto err;
238
239 return true;
240
241 err:
242 if (free_on_init_fail) {
243 if (client->client)
244 mpv_destroy(client->client);
245
246 if (client->close_client_fd)
247 close(client->client_fd);
248 }
249
250 talloc_free(client);
251 return false;
252 }
253
ipc_start_client_json(struct mp_ipc_ctx * ctx,int id,int fd)254 static void ipc_start_client_json(struct mp_ipc_ctx *ctx, int id, int fd)
255 {
256 struct client_arg *client = talloc_ptrtype(NULL, client);
257 *client = (struct client_arg){
258 .client_name =
259 id >= 0 ? talloc_asprintf(client, "ipc-%d", id) : "ipc",
260 .client_fd = fd,
261 .close_client_fd = id >= 0,
262 .quit_on_close = id < 0,
263 .writable = true,
264 };
265
266 ipc_start_client(ctx, client, true);
267 }
268
mp_ipc_start_anon_client(struct mp_ipc_ctx * ctx,struct mpv_handle * h,int out_fd[2])269 bool mp_ipc_start_anon_client(struct mp_ipc_ctx *ctx, struct mpv_handle *h,
270 int out_fd[2])
271 {
272 int pair[2];
273 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair))
274 return false;
275 mp_set_cloexec(pair[0]);
276 mp_set_cloexec(pair[1]);
277
278 struct client_arg *client = talloc_ptrtype(NULL, client);
279 *client = (struct client_arg){
280 .client = h,
281 .client_name = mpv_client_name(h),
282 .client_fd = pair[1],
283 .close_client_fd = true,
284 .writable = true,
285 };
286
287 if (!ipc_start_client(ctx, client, false)) {
288 close(pair[0]);
289 close(pair[1]);
290 return false;
291 }
292
293 out_fd[0] = pair[0];
294 out_fd[1] = -1;
295 return true;
296 }
297
ipc_thread(void * p)298 static void *ipc_thread(void *p)
299 {
300 int rc;
301
302 int ipc_fd;
303 struct sockaddr_un ipc_un = {0};
304
305 struct mp_ipc_ctx *arg = p;
306
307 mpthread_set_name("ipc socket listener");
308
309 MP_VERBOSE(arg, "Starting IPC master\n");
310
311 ipc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
312 if (ipc_fd < 0) {
313 MP_ERR(arg, "Could not create IPC socket\n");
314 goto done;
315 }
316
317 fchmod(ipc_fd, 0600);
318
319 size_t path_len = strlen(arg->path);
320 if (path_len >= sizeof(ipc_un.sun_path) - 1) {
321 MP_ERR(arg, "Could not create IPC socket\n");
322 goto done;
323 }
324
325 ipc_un.sun_family = AF_UNIX,
326 strncpy(ipc_un.sun_path, arg->path, sizeof(ipc_un.sun_path) - 1);
327
328 unlink(ipc_un.sun_path);
329
330 if (ipc_un.sun_path[0] == '@') {
331 ipc_un.sun_path[0] = '\0';
332 path_len--;
333 }
334
335 size_t addr_len = offsetof(struct sockaddr_un, sun_path) + 1 + path_len;
336 rc = bind(ipc_fd, (struct sockaddr *) &ipc_un, addr_len);
337 if (rc < 0) {
338 MP_ERR(arg, "Could not bind IPC socket\n");
339 goto done;
340 }
341
342 rc = listen(ipc_fd, 10);
343 if (rc < 0) {
344 MP_ERR(arg, "Could not listen on IPC socket\n");
345 goto done;
346 }
347
348 MP_VERBOSE(arg, "Listening to IPC socket.\n");
349
350 int client_num = 0;
351
352 struct pollfd fds[2] = {
353 {.events = POLLIN, .fd = arg->death_pipe[0]},
354 {.events = POLLIN, .fd = ipc_fd},
355 };
356
357 while (1) {
358 rc = poll(fds, 2, -1);
359 if (rc < 0) {
360 MP_ERR(arg, "Poll error\n");
361 continue;
362 }
363
364 if (fds[0].revents & POLLIN)
365 goto done;
366
367 if (fds[1].revents & POLLIN) {
368 int client_fd = accept(ipc_fd, NULL, NULL);
369 if (client_fd < 0) {
370 MP_ERR(arg, "Could not accept IPC client\n");
371 goto done;
372 }
373
374 ipc_start_client_json(arg, client_num++, client_fd);
375 }
376 }
377
378 done:
379 if (ipc_fd >= 0)
380 close(ipc_fd);
381
382 return NULL;
383 }
384
mp_init_ipc(struct mp_client_api * client_api,struct mpv_global * global)385 struct mp_ipc_ctx *mp_init_ipc(struct mp_client_api *client_api,
386 struct mpv_global *global)
387 {
388 struct MPOpts *opts = mp_get_config_group(NULL, global, &mp_opt_root);
389
390 struct mp_ipc_ctx *arg = talloc_ptrtype(NULL, arg);
391 *arg = (struct mp_ipc_ctx){
392 .log = mp_log_new(arg, global->log, "ipc"),
393 .client_api = client_api,
394 .path = mp_get_user_path(arg, global, opts->ipc_path),
395 .death_pipe = {-1, -1},
396 };
397
398 if (opts->ipc_client && opts->ipc_client[0]) {
399 int fd = -1;
400 if (strncmp(opts->ipc_client, "fd://", 5) == 0) {
401 char *end;
402 unsigned long l = strtoul(opts->ipc_client + 5, &end, 0);
403 if (!end[0] && l <= INT_MAX)
404 fd = l;
405 }
406 if (fd < 0) {
407 MP_ERR(arg, "Invalid IPC client argument: '%s'\n", opts->ipc_client);
408 } else {
409 ipc_start_client_json(arg, -1, fd);
410 }
411 }
412
413 talloc_free(opts);
414
415 if (!arg->path || !arg->path[0])
416 goto out;
417
418 if (mp_make_wakeup_pipe(arg->death_pipe) < 0)
419 goto out;
420
421 if (pthread_create(&arg->thread, NULL, ipc_thread, arg))
422 goto out;
423
424 return arg;
425
426 out:
427 if (arg->death_pipe[0] >= 0) {
428 close(arg->death_pipe[0]);
429 close(arg->death_pipe[1]);
430 }
431 talloc_free(arg);
432 return NULL;
433 }
434
mp_uninit_ipc(struct mp_ipc_ctx * arg)435 void mp_uninit_ipc(struct mp_ipc_ctx *arg)
436 {
437 if (!arg)
438 return;
439
440 (void)write(arg->death_pipe[1], &(char){0}, 1);
441 pthread_join(arg->thread, NULL);
442
443 close(arg->death_pipe[0]);
444 close(arg->death_pipe[1]);
445 talloc_free(arg);
446 }
447