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