cli: add per-client thread function
[vlc.git] / modules / control / cli / cli.c
blobd8237423e326e1559536298d5617e94a191267fc
1 /*****************************************************************************
2 * cli.c : remote control stdin/stdout module for vlc
3 *****************************************************************************
4 * Copyright (C) 2004-2009 the VideoLAN team
6 * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
7 * Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <errno.h> /* ENOMEM */
33 #include <assert.h>
34 #include <math.h>
35 #include <sys/types.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #ifdef HAVE_WORDEXP_H
39 #include <wordexp.h>
40 #endif
41 #ifdef HAVE_SEARCH_H
42 #include <search.h>
43 #endif
45 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
46 #include <vlc_common.h>
47 #include <vlc_plugin.h>
48 #include <vlc_interface.h>
49 #include <vlc_player.h>
50 #include <vlc_actions.h>
51 #include <vlc_fs.h>
52 #include <vlc_network.h>
53 #include <vlc_url.h>
54 #include <vlc_charset.h>
56 #if defined(PF_UNIX) && !defined(PF_LOCAL)
57 # define PF_LOCAL PF_UNIX
58 #endif
60 #if defined(AF_LOCAL) && ! defined(_WIN32)
61 # include <sys/un.h>
62 #endif
64 #include "cli.h"
66 struct intf_sys_t
68 vlc_thread_t thread;
69 void *commands;
70 void *player_cli;
72 #ifndef _WIN32
73 struct cli_client client;
74 char *psz_unix_path;
75 #else
76 HANDLE hConsoleIn;
77 bool b_quiet;
78 int i_socket;
79 #endif
80 int *pi_socket_listen;
83 #define MAX_LINE_LENGTH 1024
85 struct command {
86 union {
87 const char *name;
88 struct cli_handler handler;
90 void *data;
93 static int cmdcmp(const void *a, const void *b)
95 const char *const *na = a;
96 const char *const *nb = b;
98 return strcmp(*na, *nb);
101 void RegisterHandlers(intf_thread_t *intf, const struct cli_handler *handlers,
102 size_t count, void *opaque)
104 intf_sys_t *sys = intf->p_sys;
106 for (size_t i = 0; i < count; i++)
108 struct command *cmd = malloc(sizeof (*cmd));
109 if (unlikely(cmd == NULL))
110 break;
112 cmd->handler = handlers[i];
113 cmd->data = opaque;
115 struct command **pp = tsearch(&cmd->name, &sys->commands, cmdcmp);
116 if (unlikely(pp == NULL))
118 free(cmd);
119 continue;
122 assert(*pp == cmd); /* Fails if duplicate command */
126 static int Help(struct cli_client *cl, const char *const *args, size_t count,
127 void *data)
129 msg_rc("%s", _("+----[ Remote control commands ]"));
130 msg_rc( "| ");
131 msg_rc("%s", _("| add XYZ . . . . . . . . . . . . add XYZ to playlist"));
132 msg_rc("%s", _("| enqueue XYZ . . . . . . . . . queue XYZ to playlist"));
133 msg_rc("%s", _("| playlist . . . . . show items currently in playlist"));
134 msg_rc("%s", _("| play . . . . . . . . . . . . . . . . . . play stream"));
135 msg_rc("%s", _("| stop . . . . . . . . . . . . . . . . . . stop stream"));
136 msg_rc("%s", _("| next . . . . . . . . . . . . . . next playlist item"));
137 msg_rc("%s", _("| prev . . . . . . . . . . . . previous playlist item"));
138 msg_rc("%s", _("| goto . . . . . . . . . . . . . . goto item at index"));
139 msg_rc("%s", _("| repeat [on|off] . . . . toggle playlist item repeat"));
140 msg_rc("%s", _("| loop [on|off] . . . . . . . . . toggle playlist loop"));
141 msg_rc("%s", _("| random [on|off] . . . . . . . toggle random jumping"));
142 msg_rc("%s", _("| clear . . . . . . . . . . . . . . clear the playlist"));
143 msg_rc("%s", _("| status . . . . . . . . . . . current playlist status"));
144 msg_rc("%s", _("| title [X] . . . . . . set/get title in current item"));
145 msg_rc("%s", _("| title_n . . . . . . . . next title in current item"));
146 msg_rc("%s", _("| title_p . . . . . . previous title in current item"));
147 msg_rc("%s", _("| chapter [X] . . . . set/get chapter in current item"));
148 msg_rc("%s", _("| chapter_n . . . . . . next chapter in current item"));
149 msg_rc("%s", _("| chapter_p . . . . previous chapter in current item"));
150 msg_rc( "| ");
151 msg_rc("%s", _("| seek X . . . seek in seconds, for instance `seek 12'"));
152 msg_rc("%s", _("| pause . . . . . . . . . . . . . . . . toggle pause"));
153 msg_rc("%s", _("| fastforward . . . . . . . . . set to maximum rate"));
154 msg_rc("%s", _("| rewind . . . . . . . . . . . . set to minimum rate"));
155 msg_rc("%s", _("| faster . . . . . . . . . . faster playing of stream"));
156 msg_rc("%s", _("| slower . . . . . . . . . . slower playing of stream"));
157 msg_rc("%s", _("| normal . . . . . . . . . . normal playing of stream"));
158 msg_rc("%s", _("| frame. . . . . . . . . . play frame by frame"));
159 msg_rc("%s", _("| f [on|off] . . . . . . . . . . . . toggle fullscreen"));
160 msg_rc("%s", _("| info . . . . . information about the current stream"));
161 msg_rc("%s", _("| stats . . . . . . . . show statistical information"));
162 msg_rc("%s", _("| get_time . . seconds elapsed since stream's beginning"));
163 msg_rc("%s", _("| is_playing . . . . 1 if a stream plays, 0 otherwise"));
164 msg_rc("%s", _("| get_title . . . . . the title of the current stream"));
165 msg_rc("%s", _("| get_length . . . . the length of the current stream"));
166 msg_rc( "| ");
167 msg_rc("%s", _("| volume [X] . . . . . . . . . . set/get audio volume"));
168 msg_rc("%s", _("| volup [X] . . . . . . . raise audio volume X steps"));
169 msg_rc("%s", _("| voldown [X] . . . . . . lower audio volume X steps"));
170 msg_rc("%s", _("| adev [device] . . . . . . . . set/get audio device"));
171 msg_rc("%s", _("| achan [X]. . . . . . . . . . set/get audio channels"));
172 msg_rc("%s", _("| atrack [X] . . . . . . . . . . . set/get audio track"));
173 msg_rc("%s", _("| vtrack [X] . . . . . . . . . . . set/get video track"));
174 msg_rc("%s", _("| vratio [X] . . . . . . . set/get video aspect ratio"));
175 msg_rc("%s", _("| vcrop [X] . . . . . . . . . . . set/get video crop"));
176 msg_rc("%s", _("| vzoom [X] . . . . . . . . . . . set/get video zoom"));
177 msg_rc("%s", _("| snapshot . . . . . . . . . . . . take video snapshot"));
178 msg_rc("%s", _("| record [on|off] . . . . . . . . . . toggle recording"));
179 msg_rc("%s", _("| strack [X] . . . . . . . . . set/get subtitle track"));
180 msg_rc("%s", _("| key [hotkey name] . . . . . . simulate hotkey press"));
181 msg_rc( "| ");
182 msg_rc("%s", _("| help . . . . . . . . . . . . . . . this help message"));
183 msg_rc("%s", _("| logout . . . . . . . exit (if in socket connection)"));
184 msg_rc("%s", _("| quit . . . . . . . . . . . . . . . . . . . quit vlc"));
185 msg_rc( "| ");
186 msg_rc("%s", _("+----[ end of help ]"));
187 (void) args; (void) count; (void) data;
188 return 0;
191 static int Intf(struct cli_client *cl, const char *const *args, size_t count,
192 void *data)
194 intf_thread_t *intf = data;
196 (void) cl;
198 return intf_Create(vlc_object_instance(intf), count == 1 ? "" : args[1]);
201 static int Quit(struct cli_client *cl, const char *const *args, size_t count,
202 void *data)
204 intf_thread_t *intf = data;
206 libvlc_Quit(vlc_object_instance(intf));
207 (void) cl; (void) args; (void) count;
208 return 0;
211 static int LogOut(struct cli_client *cl, const char *const *args, size_t count,
212 void *data)
214 intf_thread_t *intf = data;
215 intf_sys_t *sys = intf->p_sys;
217 /* Close connection */
218 #ifndef _WIN32
219 if (sys->pi_socket_listen != NULL)
221 vlc_mutex_lock(&cl->output_lock);
222 cl->fd = -1;
223 vlc_mutex_unlock(&cl->output_lock);
224 fclose(cl->stream);
225 cl->stream = NULL;
227 else
228 { /* Force end-of-file on the standard input. */
229 int fd = vlc_open("/dev/null", O_RDONLY);
230 if (fd != -1)
231 { /* POSIX requires flushing before, and seeking after, replacing a
232 * file descriptor underneath an I/O stream.
234 fflush(cl->stream);
235 dup2(fd, 0 /* fileno(sys->stream) */);
236 fseek(cl->stream, 0, SEEK_SET);
237 vlc_close(fd);
240 #else
241 if (sys->i_socket != -1)
243 net_Close(sys->i_socket);
244 sys->i_socket = -1;
246 #endif
247 (void) args; (void) count;
248 return 0;
251 static int KeyAction(struct cli_client *cl, const char *const *args, size_t n,
252 void *data)
254 intf_thread_t *intf = data;
255 vlc_object_t *vlc = VLC_OBJECT(vlc_object_instance(intf));
257 if (n != 2)
258 return VLC_EGENERIC; /* EINVAL */
260 var_SetInteger(vlc, "key-action", vlc_actions_get_id(args[1]));
261 (void) cl;
262 return 0;
265 static const struct cli_handler cmds[] =
267 { "longhelp", Help },
268 { "h", Help },
269 { "help", Help },
270 { "H", Help },
271 { "?", Help },
272 { "logout", LogOut },
273 { "quit", Quit },
275 { "intf", Intf },
276 { "key", KeyAction },
277 { "hotkey", KeyAction },
280 static int Process(intf_thread_t *intf, struct cli_client *cl, const char *line)
282 intf_sys_t *sys = intf->p_sys;
283 /* Skip heading spaces */
284 const char *cmd = line + strspn(line, " ");
285 int ret;
287 if (*cmd == '\0')
288 return 0; /* Ignore empty line */
290 #ifdef HAVE_WORDEXP
291 wordexp_t we;
292 int val = wordexp(cmd, &we, 0);
294 if (val != 0)
296 if (val == WRDE_NOSPACE)
298 ret = VLC_ENOMEM;
299 error: wordfree(&we);
301 else
302 ret = VLC_EGENERIC;
304 cli_printf(cl, N_("parse error"));
305 return ret;
308 size_t count = we.we_wordc;
309 const char **args = vlc_alloc(count, sizeof (*args));
310 if (unlikely(args == NULL))
312 ret = VLC_ENOMEM;
313 goto error;
316 for (size_t i = 0; i < we.we_wordc; i++)
317 args[i] = we.we_wordv[i];
318 #else
319 /* Split psz_cmd at the first space and make sure that
320 * psz_arg is valid */
321 const char *args[] = { cmd, NULL };
322 size_t count = 1;
323 char *arg = strchr(cmd, ' ');
325 if (arg != NULL)
327 *(arg++) = '\0';
328 arg += strspn(arg, " ");
330 if (*arg)
331 count++;
333 #endif
335 if (count > 0)
337 const struct command **pp = tfind(&args[0], &sys->commands, cmdcmp);
339 if (pp != NULL)
341 const struct command *c = *pp;;
343 ret = c->handler.callback(cl, args, count, c->data);
345 else
347 cli_printf(cl, _("Unknown command `%s'. Type `help' for help."),
348 args[0]);
349 ret = VLC_EGENERIC;
353 #ifdef HAVE_WORDEXP
354 free(args);
355 wordfree(&we);
356 #endif
357 return ret;
360 #ifndef _WIN32
361 static ssize_t cli_writev(struct cli_client *cl,
362 const struct iovec *iov, unsigned iovlen)
364 ssize_t val;
366 vlc_mutex_lock(&cl->output_lock);
367 if (cl->fd != -1)
368 val = vlc_writev(cl->fd, iov, iovlen);
369 else
370 errno = EPIPE, val = -1;
371 vlc_mutex_unlock(&cl->output_lock);
372 return val;
375 static int cli_vprintf(struct cli_client *cl, const char *fmt, va_list args)
377 char *msg;
378 int len = vasprintf(&msg, fmt, args);
380 if (likely(len >= 0))
382 struct iovec iov[2] = { { msg, len }, { (char *)"\n", 1 } };
384 cli_writev(cl, iov, ARRAY_SIZE(iov));
385 len++;
386 free(msg);
388 return len;
391 int cli_printf(struct cli_client *cl, const char *fmt, ...)
393 va_list ap;
394 int len;
396 va_start(ap, fmt);
397 len = cli_vprintf(cl, fmt, ap);
398 va_end(ap);
399 return len;
402 static void msg_vprint(intf_thread_t *p_intf, const char *psz_fmt, va_list args)
404 intf_sys_t *sys = p_intf->p_sys;
406 cli_vprintf(&sys->client, psz_fmt, args);
409 void msg_print(intf_thread_t *intf, const char *fmt, ...)
411 va_list ap;
413 va_start(ap, fmt);
414 msg_vprint(intf, fmt, ap);
415 va_end(ap);
418 static void *cli_client_thread(void *data)
420 struct cli_client *cl = data;
421 intf_thread_t *intf = cl->intf;
423 while (cl->stream != NULL)
425 char cmd[MAX_LINE_LENGTH + 1];
427 if (fgets(cmd, sizeof (cmd), cl->stream) == NULL)
428 break;
430 int canc = vlc_savecancel();
431 if (cmd[0] != '\0')
432 cmd[strlen(cmd) - 1] = '\0'; /* remove trailing LF */
433 Process(intf, cl, cmd);
434 vlc_restorecancel(canc);
437 if (cl->stream == stdin)
439 int canc = vlc_savecancel();
440 libvlc_Quit(vlc_object_instance(intf));
441 vlc_restorecancel(canc);
444 return NULL;
447 static void *Run(void *data)
449 intf_thread_t *intf = data;
450 intf_sys_t *sys = intf->p_sys;
452 for (;;)
454 struct cli_client *cl = &sys->client;
456 while (cl->stream == NULL)
458 assert(sys->pi_socket_listen != NULL);
460 int fd = net_Accept(intf, sys->pi_socket_listen);
461 if (fd == -1)
462 continue;
464 int canc = vlc_savecancel();
466 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
467 cl->stream = fdopen(fd, "r");
468 if (cl->stream != NULL)
470 vlc_mutex_lock(&cl->output_lock);
471 cl->fd = fd;
472 vlc_mutex_unlock(&cl->output_lock);
473 cl->intf = intf;
475 else
476 vlc_close(fd);
477 vlc_restorecancel(canc);
480 cli_client_thread(cl);
482 if (sys->pi_socket_listen == NULL)
483 break;
486 return NULL;
489 #else
490 static void msg_vprint(intf_thread_t *p_intf, const char *psz_fmt, va_list args)
492 char fmt_eol[strlen (psz_fmt) + 3], *msg;
493 int len;
495 snprintf (fmt_eol, sizeof (fmt_eol), "%s\r\n", psz_fmt);
496 len = vasprintf( &msg, fmt_eol, args );
498 if( len < 0 )
499 return;
501 if( p_intf->p_sys->i_socket == -1 )
502 utf8_fprintf( stdout, "%s", msg );
503 else
504 net_Write( p_intf, p_intf->p_sys->i_socket, msg, len );
506 free( msg );
509 void msg_print(intf_thread_t *intf, const char *fmt, ...)
511 va_list ap;
513 va_start(ap, fmt);
514 msg_vprint(intf, fmt, ap);
515 va_end(ap);
518 int cli_printf(struct cli_client *cl, const char *fmt, ...)
520 va_list ap;
522 va_start(ap, fmt);
523 msg_vprint(cl->intf, fmt, ap);
524 va_end(ap);
525 return VLC_SUCCESS;
528 #if !VLC_WINSTORE_APP
529 static bool ReadWin32( intf_thread_t *p_intf, unsigned char *p_buffer, int *pi_size )
531 INPUT_RECORD input_record;
532 DWORD i_dw;
534 /* On Win32, select() only works on socket descriptors */
535 while( WaitForSingleObjectEx( p_intf->p_sys->hConsoleIn,
536 MS_FROM_VLC_TICK(INTF_IDLE_SLEEP), TRUE ) == WAIT_OBJECT_0 )
538 // Prefer to fail early when there's not enough space to store a 4 bytes
539 // UTF8 character. The function will be immediatly called again and we won't
540 // lose an input
541 while( *pi_size < MAX_LINE_LENGTH - 4 &&
542 ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record, 1, &i_dw ) )
544 if( input_record.EventType != KEY_EVENT ||
545 !input_record.Event.KeyEvent.bKeyDown ||
546 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
547 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
548 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
549 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
551 /* nothing interesting */
552 continue;
554 if( input_record.Event.KeyEvent.uChar.AsciiChar == '\n' ||
555 input_record.Event.KeyEvent.uChar.AsciiChar == '\r' )
557 putc( '\n', stdout );
558 break;
560 switch( input_record.Event.KeyEvent.uChar.AsciiChar )
562 case '\b':
563 if ( *pi_size == 0 )
564 break;
565 if ( *pi_size > 1 && (p_buffer[*pi_size - 1] & 0xC0) == 0x80 )
567 // pi_size currently points to the character to be written, so
568 // we need to roll back from 2 bytes to start erasing the previous
569 // character
570 (*pi_size) -= 2;
571 unsigned int nbBytes = 1;
572 while( *pi_size > 0 && (p_buffer[*pi_size] & 0xC0) == 0x80 )
574 (*pi_size)--;
575 nbBytes++;
577 assert( clz( (unsigned char)~(p_buffer[*pi_size]) ) == nbBytes + 1 );
578 // The first utf8 byte will be overriden by a \0
580 else
581 (*pi_size)--;
582 p_buffer[*pi_size] = 0;
584 fputs( "\b \b", stdout );
585 break;
586 default:
588 WCHAR psz_winput[] = { input_record.Event.KeyEvent.uChar.UnicodeChar, L'\0' };
589 char* psz_input = FromWide( psz_winput );
590 int input_size = strlen(psz_input);
591 if ( *pi_size + input_size > MAX_LINE_LENGTH )
593 p_buffer[ *pi_size ] = 0;
594 return false;
596 strcpy( (char*)&p_buffer[*pi_size], psz_input );
597 utf8_fprintf( stdout, "%s", psz_input );
598 free(psz_input);
599 *pi_size += input_size;
604 p_buffer[ *pi_size ] = 0;
605 return true;
608 vlc_testcancel ();
610 return false;
612 #endif
614 static bool ReadCommand(intf_thread_t *p_intf, char *p_buffer, int *pi_size)
616 #if !VLC_WINSTORE_APP
617 if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
618 return ReadWin32( p_intf, (unsigned char*)p_buffer, pi_size );
619 else if( p_intf->p_sys->i_socket == -1 )
621 vlc_tick_sleep( INTF_IDLE_SLEEP );
622 return false;
624 #endif
626 while( *pi_size < MAX_LINE_LENGTH )
628 if( p_intf->p_sys->i_socket == -1 )
630 if( read( 0/*STDIN_FILENO*/, p_buffer + *pi_size, 1 ) <= 0 )
631 { /* Standard input closed: exit */
632 libvlc_Quit( vlc_object_instance(p_intf) );
633 p_buffer[*pi_size] = 0;
634 return true;
637 else
638 { /* Connection closed */
639 if( net_Read( p_intf, p_intf->p_sys->i_socket, p_buffer + *pi_size,
640 1 ) <= 0 )
642 net_Close( p_intf->p_sys->i_socket );
643 p_intf->p_sys->i_socket = -1;
644 p_buffer[*pi_size] = 0;
645 return true;
649 if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
650 break;
652 (*pi_size)++;
655 if( *pi_size == MAX_LINE_LENGTH ||
656 p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
658 p_buffer[ *pi_size ] = 0;
659 return true;
662 return false;
665 /*****************************************************************************
666 * Run: rc thread
667 *****************************************************************************
668 * This part of the interface is in a separate thread so that we can call
669 * exec() from within it without annoying the rest of the program.
670 *****************************************************************************/
671 static void *Run( void *data )
673 intf_thread_t *p_intf = data;
674 intf_sys_t *p_sys = p_intf->p_sys;
676 char p_buffer[ MAX_LINE_LENGTH + 1 ];
678 int i_size = 0;
679 int canc = vlc_savecancel( );
681 p_buffer[0] = 0;
683 #if !VLC_WINSTORE_APP
684 /* Get the file descriptor of the console input */
685 p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
686 if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
688 msg_Err( p_intf, "couldn't find user input handle" );
689 return NULL;
691 #endif
693 for( ;; )
695 bool b_complete;
697 vlc_restorecancel( canc );
699 if( p_sys->pi_socket_listen != NULL && p_sys->i_socket == -1 )
701 p_sys->i_socket =
702 net_Accept( p_intf, p_sys->pi_socket_listen );
703 if( p_sys->i_socket == -1 ) continue;
706 b_complete = ReadCommand( p_intf, p_buffer, &i_size );
707 canc = vlc_savecancel( );
709 /* Is there something to do? */
710 if( !b_complete ) continue;
712 struct cli_client cl = { p_intf };
714 Process(p_intf, &cl, p_buffer);
716 /* Command processed */
717 i_size = 0; p_buffer[0] = 0;
720 vlc_assert_unreachable();
723 #undef msg_rc
724 #define msg_rc(...) msg_print(p_intf, __VA_ARGS__)
725 #include "../intromsg.h"
726 #endif
728 /*****************************************************************************
729 * Activate: initialize and create stuff
730 *****************************************************************************/
731 static int Activate( vlc_object_t *p_this )
733 intf_thread_t *p_intf = (intf_thread_t*)p_this;
734 char *psz_host, *psz_unix_path = NULL;
735 int *pi_socket = NULL;
737 #ifndef _WIN32
738 #if defined(HAVE_ISATTY)
739 /* Check that stdin is a TTY */
740 if( !var_InheritBool( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
742 msg_Warn( p_intf, "fd 0 is not a TTY" );
743 return VLC_EGENERIC;
745 #endif
746 #ifdef AF_LOCAL
747 psz_unix_path = var_InheritString( p_intf, "rc-unix" );
748 if( psz_unix_path )
750 int i_socket;
751 struct sockaddr_un addr;
753 memset( &addr, 0, sizeof(struct sockaddr_un) );
755 msg_Dbg( p_intf, "trying UNIX socket" );
757 /* The given unix path cannot be longer than sun_path - 1 to take into
758 * account the terminated null character. */
759 if ( strlen(psz_unix_path) + 1 >= sizeof( addr.sun_path ) )
761 msg_Err( p_intf, "rc-unix value is longer than expected" );
762 return VLC_EGENERIC;
765 if( (i_socket = vlc_socket( PF_LOCAL, SOCK_STREAM, 0, false ) ) < 0 )
767 msg_Warn( p_intf, "can't open socket: %s", vlc_strerror_c(errno) );
768 free( psz_unix_path );
769 return VLC_EGENERIC;
772 addr.sun_family = AF_LOCAL;
773 strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) - 1 );
774 addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
776 if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr))
777 && (errno == EADDRINUSE)
778 && connect (i_socket, (struct sockaddr *)&addr, sizeof (addr))
779 && (errno == ECONNREFUSED))
781 msg_Info (p_intf, "Removing dead UNIX socket: %s", psz_unix_path);
782 unlink (psz_unix_path);
784 if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr)))
786 msg_Err (p_intf, "cannot bind UNIX socket at %s: %s",
787 psz_unix_path, vlc_strerror_c(errno));
788 free (psz_unix_path);
789 net_Close (i_socket);
790 return VLC_EGENERIC;
794 if( listen( i_socket, 1 ) )
796 msg_Warn (p_intf, "can't listen on socket: %s",
797 vlc_strerror_c(errno));
798 free( psz_unix_path );
799 net_Close( i_socket );
800 return VLC_EGENERIC;
803 /* FIXME: we need a core function to merge listening sockets sets */
804 pi_socket = calloc( 2, sizeof( int ) );
805 if( pi_socket == NULL )
807 free( psz_unix_path );
808 net_Close( i_socket );
809 return VLC_ENOMEM;
811 pi_socket[0] = i_socket;
812 pi_socket[1] = -1;
814 #endif /* AF_LOCAL */
815 #endif /* !_WIN32 */
817 if( ( pi_socket == NULL ) &&
818 ( psz_host = var_InheritString( p_intf, "rc-host" ) ) != NULL )
820 vlc_url_t url;
822 vlc_UrlParse( &url, psz_host );
823 if( url.psz_host == NULL )
825 vlc_UrlClean( &url );
826 char *psz_backward_compat_host;
827 if( asprintf( &psz_backward_compat_host, "//%s", psz_host ) < 0 )
829 free( psz_host );
830 return VLC_EGENERIC;
832 free( psz_host );
833 psz_host = psz_backward_compat_host;
834 vlc_UrlParse( &url, psz_host );
837 msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port );
839 pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
840 if( pi_socket == NULL )
842 msg_Warn( p_intf, "can't listen to %s port %i",
843 url.psz_host, url.i_port );
844 vlc_UrlClean( &url );
845 free( psz_host );
846 return VLC_EGENERIC;
849 vlc_UrlClean( &url );
850 free( psz_host );
853 intf_sys_t *p_sys = malloc( sizeof( *p_sys ) );
854 if( unlikely(p_sys == NULL) )
856 net_ListenClose( pi_socket );
857 free( psz_unix_path );
858 return VLC_ENOMEM;
861 p_intf->p_sys = p_sys;
862 p_sys->commands = NULL;
863 p_sys->pi_socket_listen = pi_socket;
865 RegisterHandlers(p_intf, cmds, ARRAY_SIZE(cmds), p_intf);
867 /* Line-buffered stdout */
868 setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
870 #ifndef _WIN32
871 struct cli_client *cl = &p_sys->client;
873 vlc_mutex_init(&cl->output_lock);
875 if (pi_socket == NULL)
877 cl->stream = stdin;
878 cl->fd = 1;
879 cl->intf = p_intf;
881 else
883 cl->stream = NULL;
884 cl->fd = -1;
886 p_sys->psz_unix_path = psz_unix_path;
887 #else
888 p_sys->i_socket = -1;
889 #if VLC_WINSTORE_APP
890 p_sys->b_quiet = true;
891 #else
892 p_sys->b_quiet = var_InheritBool( p_intf, "rc-quiet" );
893 if( !p_sys->b_quiet )
894 intf_consoleIntroMsg( p_intf );
895 #endif
896 #endif
898 p_sys->player_cli = RegisterPlayer(p_intf);
899 if (unlikely(p_sys->player_cli == NULL))
900 goto error;
902 RegisterPlaylist(p_intf);
904 if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
905 goto error;
907 msg_print(p_intf, "%s",
908 _("Remote control interface initialized. Type `help' for help."));
910 return VLC_SUCCESS;
912 error:
913 if (p_sys->player_cli != NULL)
914 DeregisterPlayer(p_intf, p_sys->player_cli);
915 tdestroy(p_sys->commands, free);
916 net_ListenClose( pi_socket );
917 free( psz_unix_path );
918 free( p_sys );
919 return VLC_EGENERIC;
922 /*****************************************************************************
923 * Deactivate: uninitialize and cleanup
924 *****************************************************************************/
925 static void Deactivate( vlc_object_t *p_this )
927 intf_thread_t *p_intf = (intf_thread_t*)p_this;
928 intf_sys_t *p_sys = p_intf->p_sys;
930 vlc_cancel( p_sys->thread );
931 vlc_join( p_sys->thread, NULL );
933 DeregisterPlayer(p_intf, p_sys->player_cli);
934 tdestroy(p_sys->commands, free);
936 if (p_sys->pi_socket_listen != NULL)
938 net_ListenClose(p_sys->pi_socket_listen);
939 #ifndef _WIN32
940 struct cli_client *cl = &p_sys->client;
942 if (cl->stream != NULL)
943 fclose(cl->stream);
944 if (p_sys->psz_unix_path != NULL)
946 unlink(p_sys->psz_unix_path);
947 free(p_sys->psz_unix_path);
949 #else
950 if (p_sys->i_socket != -1)
951 net_Close(p_sys->i_socket);
952 #endif
954 free( p_sys );
957 /*****************************************************************************
958 * Module descriptor
959 *****************************************************************************/
960 #define POS_TEXT N_("Show stream position")
961 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
962 "stream from time to time." )
964 #define TTY_TEXT N_("Fake TTY")
965 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
967 #define UNIX_TEXT N_("UNIX socket command input")
968 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
969 "stdin." )
971 #define HOST_TEXT N_("TCP command input")
972 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
973 "You can set the address and port the interface will bind to." )
975 #ifdef _WIN32
976 #define QUIET_TEXT N_("Do not open a DOS command box interface")
977 #define QUIET_LONGTEXT N_( \
978 "By default the rc interface plugin will start a DOS command box. " \
979 "Enabling the quiet mode will not bring this command box but can also " \
980 "be pretty annoying when you want to stop VLC and no video window is " \
981 "open." )
982 #endif
984 vlc_module_begin()
985 set_shortname(N_("RC"))
986 set_category(CAT_INTERFACE)
987 set_subcategory(SUBCAT_INTERFACE_MAIN)
988 set_description(N_("Remote control interface"))
989 add_bool("rc-show-pos", false, POS_TEXT, POS_LONGTEXT, true)
991 #ifdef _WIN32
992 add_bool("rc-quiet", false, QUIET_TEXT, QUIET_LONGTEXT, false)
993 #else
994 #if defined (HAVE_ISATTY)
995 add_bool("rc-fake-tty", false, TTY_TEXT, TTY_LONGTEXT, true)
996 #endif
997 #ifdef AF_LOCAL
998 add_string("rc-unix", NULL, UNIX_TEXT, UNIX_LONGTEXT, true)
999 #endif
1000 #endif
1001 add_string("rc-host", NULL, HOST_TEXT, HOST_LONGTEXT, true)
1003 set_capability("interface", 20)
1005 set_callbacks(Activate, Deactivate)
1006 add_shortcut("cli", "rc", "oldrc")
1007 vlc_module_end()