2 * miracle_local.c -- Local Miracle Parser
3 * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 /* System header files */
30 /* Needed for backtrace on linux */
35 /* Valerie header files */
36 #include <valerie/valerie_util.h>
38 /* MLT header files. */
39 #include <framework/mlt_factory.h>
41 /* Application header files */
42 #include "miracle_local.h"
43 #include "miracle_connection.h"
44 #include "miracle_commands.h"
45 #include "miracle_unit_commands.h"
46 #include "miracle_log.h"
48 /** Private miracle_local structure.
53 valerie_parser parser
;
56 *miracle_local
, miracle_local_t
;
58 /** Forward declarations.
61 static valerie_response
miracle_local_connect( miracle_local
);
62 static valerie_response
miracle_local_execute( miracle_local
, char * );
63 static valerie_response
miracle_local_push( miracle_local
, char *, mlt_service
);
64 static valerie_response
miracle_local_receive( miracle_local
, char *, char * );
65 static void miracle_local_close( miracle_local
);
66 response_codes
miracle_help( command_argument arg
);
67 response_codes
miracle_run( command_argument arg
);
68 response_codes
miracle_shutdown( command_argument arg
);
70 /** DV Parser constructor.
73 valerie_parser
miracle_parser_init_local( )
75 valerie_parser parser
= malloc( sizeof( valerie_parser_t
) );
76 miracle_local local
= malloc( sizeof( miracle_local_t
) );
80 memset( parser
, 0, sizeof( valerie_parser_t
) );
82 parser
->connect
= (parser_connect
)miracle_local_connect
;
83 parser
->execute
= (parser_execute
)miracle_local_execute
;
84 parser
->push
= (parser_push
)miracle_local_push
;
85 parser
->received
= (parser_received
)miracle_local_receive
;
86 parser
->close
= (parser_close
)miracle_local_close
;
91 memset( local
, 0, sizeof( miracle_local_t
) );
92 local
->parser
= parser
;
93 local
->root_dir
[0] = '/';
96 // Construct the factory
97 mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
102 /** response status code/message pair
112 /** response messages
115 static responses_t responses
[] =
117 {RESPONSE_SUCCESS
, "OK"},
118 {RESPONSE_SUCCESS_N
, "OK"},
119 {RESPONSE_SUCCESS_1
, "OK"},
120 {RESPONSE_UNKNOWN_COMMAND
, "Unknown command"},
121 {RESPONSE_TIMEOUT
, "Operation timed out"},
122 {RESPONSE_MISSING_ARG
, "Argument missing"},
123 {RESPONSE_INVALID_UNIT
, "Unit not found"},
124 {RESPONSE_BAD_FILE
, "Failed to locate or open clip"},
125 {RESPONSE_OUT_OF_RANGE
, "Argument value out of range"},
126 {RESPONSE_TOO_MANY_FILES
, "Too many files open"},
127 {RESPONSE_ERROR
, "Server Error"}
143 /** A command definition.
148 /* The command string corresponding to this operation (e.g. "play") */
150 /* The function associated with it */
151 response_codes (*operation
) ( command_argument
);
152 /* a boolean to indicate if this is a unit or global command
153 unit commands require a unit identifier as first argument */
155 /* What type is the argument (RTTI :-) ATYPE_whatever */
157 /* online help information */
162 /* The following define the queue of commands available to the user. The
163 first entry is the name of the command (the string which must be typed),
164 the second command is the function associated with it, the third argument
165 is for the type of the argument, and the last argument specifies whether
166 this is something which should be handled immediately or whether it
167 should be queued (only robot motion commands need to be queued). */
169 static command_t vocabulary
[] =
171 {"BYE", NULL
, 0, ATYPE_NONE
, "Terminates the session. Units are not removed and task queue is not flushed."},
172 {"HELP", miracle_help
, 0, ATYPE_NONE
, "Display this information!"},
173 {"NLS", miracle_list_nodes
, 0, ATYPE_NONE
, "List the AV/C nodes on the 1394 bus."},
174 {"UADD", miracle_add_unit
, 0, ATYPE_STRING
, "Create a new DV unit (virtual VTR) to transmit to receiver specified in GUID argument."},
175 {"ULS", miracle_list_units
, 0, ATYPE_NONE
, "Lists the units that have already been added to the server."},
176 {"CLS", miracle_list_clips
, 0, ATYPE_STRING
, "Lists the clips at directory name argument."},
177 {"SET", miracle_set_global_property
, 0, ATYPE_PAIR
, "Set a server configuration property."},
178 {"GET", miracle_get_global_property
, 0, ATYPE_STRING
, "Get a server configuration property."},
179 {"RUN", miracle_run
, 0, ATYPE_STRING
, "Run a batch file." },
180 {"LIST", miracle_list
, 1, ATYPE_NONE
, "List the playlist associated to a unit."},
181 {"LOAD", miracle_load
, 1, ATYPE_STRING
, "Load clip specified in absolute filename argument."},
182 {"INSERT", miracle_insert
, 1, ATYPE_STRING
, "Insert a clip at the given clip index."},
183 {"REMOVE", miracle_remove
, 1, ATYPE_NONE
, "Remove a clip at the given clip index."},
184 {"CLEAN", miracle_clean
, 1, ATYPE_NONE
, "Clean a unit by removing all but the currently playing clip."},
185 {"CLEAR", miracle_clear
, 1, ATYPE_NONE
, "Clear a unit by removing all clips."},
186 {"MOVE", miracle_move
, 1, ATYPE_INT
, "Move a clip to another clip index."},
187 {"APND", miracle_append
, 1, ATYPE_STRING
, "Append a clip specified in absolute filename argument."},
188 {"PLAY", miracle_play
, 1, ATYPE_NONE
, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."},
189 {"STOP", miracle_stop
, 1, ATYPE_NONE
, "Stop a loaded and playing clip."},
190 {"PAUSE", miracle_pause
, 1, ATYPE_NONE
, "Pause a playing clip."},
191 {"REW", miracle_rewind
, 1, ATYPE_NONE
, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."},
192 {"FF", miracle_ff
, 1, ATYPE_NONE
, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."},
193 {"STEP", miracle_step
, 1, ATYPE_INT
, "Step argument number of frames forward or backward."},
194 {"GOTO", miracle_goto
, 1, ATYPE_INT
, "Jump to frame number supplied as argument."},
195 {"SIN", miracle_set_in_point
, 1, ATYPE_INT
, "Set the IN point of the loaded clip to frame number argument. -1 = reset in point to 0"},
196 {"SOUT", miracle_set_out_point
, 1, ATYPE_INT
, "Set the OUT point of the loaded clip to frame number argument. -1 = reset out point to maximum."},
197 {"USTA", miracle_get_unit_status
, 1, ATYPE_NONE
, "Report information about the unit."},
198 {"USET", miracle_set_unit_property
, 1, ATYPE_PAIR
, "Set a unit configuration property."},
199 {"UGET", miracle_get_unit_property
, 1, ATYPE_STRING
, "Get a unit configuration property."},
200 {"XFER", miracle_transfer
, 1, ATYPE_STRING
, "Transfer the unit's clip to another unit specified as argument."},
201 {"SHUTDOWN", miracle_shutdown
, 0, ATYPE_NONE
, "Shutdown the server."},
202 {NULL
, NULL
, 0, ATYPE_NONE
, NULL
}
208 static char helpstr
[] =
209 "Miracle -- A Multimedia Playout Server\n"
210 " Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n"
212 " Dan Dennedy <dan@dennedy.org>\n"
213 " Charles Yates <charles.yates@pandora.be>\n"
214 "Available commands:\n";
216 /** Lookup the response message for a status code.
219 inline char *get_response_msg( int code
)
222 for ( i
= 0; responses
[ i
].message
!= NULL
&& code
!= responses
[ i
].code
; i
++ ) ;
223 return responses
[ i
].message
;
226 /** Tell the user the miracle command set
229 response_codes
miracle_help( command_argument cmd_arg
)
233 valerie_response_printf( cmd_arg
->response
, 10240, "%s", helpstr
);
235 for ( i
= 0; vocabulary
[ i
].command
!= NULL
; i
++ )
236 valerie_response_printf( cmd_arg
->response
, 1024,
238 vocabulary
[ i
].command
,
239 vocabulary
[ i
].help
);
241 valerie_response_printf( cmd_arg
->response
, 2, "\n" );
243 return RESPONSE_SUCCESS_N
;
246 /** Execute a batch file.
249 response_codes
miracle_run( command_argument cmd_arg
)
251 valerie_response temp
= valerie_parser_run( cmd_arg
->parser
, (char *)cmd_arg
->argument
);
257 valerie_response_set_error( cmd_arg
->response
,
258 valerie_response_get_error_code( temp
),
259 valerie_response_get_error_string( temp
) );
261 for ( index
= 1; index
< valerie_response_count( temp
); index
++ )
262 valerie_response_printf( cmd_arg
->response
, 10240, "%s\n", valerie_response_get_line( temp
, index
) );
264 valerie_response_close( temp
);
267 return valerie_response_get_error_code( cmd_arg
->response
);
270 response_codes
miracle_shutdown( command_argument cmd_arg
)
273 return RESPONSE_SUCCESS
;
276 /** Processes 'thread' id
279 static pthread_t self
;
281 /* Signal handler to deal with various shutdown signals. Basically this
282 should clean up and power down the motor. Note that the death of any
283 child thread will kill all thrads. */
285 void signal_handler( int sig
)
287 if ( pthread_equal( self
, pthread_self( ) ) )
291 miracle_log( LOG_DEBUG
, "Received %s - shutting down.", strsignal(sig
) );
293 miracle_log( LOG_DEBUG
, "Received signal %i - shutting down.", sig
);
300 static void sigsegv_handler()
308 miracle_log( LOG_CRIT
, "\a\nMiracle experienced a segmentation fault.\n"
309 "Dumping stack from the offending thread\n\n" );
310 size
= backtrace( array
, 10 );
311 strings
= backtrace_symbols( array
, size
);
313 miracle_log( LOG_CRIT
, "Obtained %zd stack frames.\n", size
);
315 for ( i
= 0; i
< size
; i
++ )
316 miracle_log( LOG_CRIT
, "%s", strings
[ i
] );
320 miracle_log( LOG_CRIT
, "\nDone dumping - exiting.\n" );
322 miracle_log( LOG_CRIT
, "\a\nMiracle experienced a segmentation fault.\n" );
324 exit( EXIT_FAILURE
);
329 /** Local 'connect' function.
332 static valerie_response
miracle_local_connect( miracle_local local
)
334 valerie_response response
= valerie_response_init( );
336 self
= pthread_self( );
338 valerie_response_set_error( response
, 100, "VTR Ready" );
340 signal( SIGHUP
, signal_handler
);
341 signal( SIGINT
, signal_handler
);
342 signal( SIGTERM
, SIG_DFL
);
343 signal( SIGSTOP
, signal_handler
);
344 signal( SIGPIPE
, signal_handler
);
345 signal( SIGALRM
, signal_handler
);
346 signal( SIGCHLD
, SIG_IGN
);
347 if ( getenv( "MLT_SIGSEGV" ) )
348 signal( SIGSEGV
, sigsegv_handler
);
353 /** Set the error and determine the message associated to this command.
356 void miracle_command_set_error( command_argument cmd
, response_codes code
)
358 valerie_response_set_error( cmd
->response
, code
, get_response_msg( code
) );
361 /** Parse the unit argument.
364 int miracle_command_parse_unit( command_argument cmd
, int argument
)
367 char *string
= valerie_tokeniser_get_string( cmd
->tokeniser
, argument
);
368 if ( string
!= NULL
&& ( string
[ 0 ] == 'U' || string
[ 0 ] == 'u' ) && strlen( string
) > 1 )
369 unit
= atoi( string
+ 1 );
373 /** Parse a normal argument.
376 void *miracle_command_parse_argument( command_argument cmd
, int argument
, arguments_types type
, char *command
)
379 char *value
= valerie_tokeniser_get_string( cmd
->tokeniser
, argument
);
389 ret
= malloc( sizeof( float ) );
391 *( float * )ret
= atof( value
);
395 ret
= strdup( value
);
399 if ( strchr( command
, '=' ) )
401 char *ptr
= strchr( command
, '=' );
402 while ( *( ptr
- 1 ) != ' ' )
406 while( ptr
[ strlen( ptr
) - 1 ] == ' ' )
407 ptr
[ strlen( ptr
) - 1 ] = '\0';
412 ret
= malloc( sizeof( int ) );
414 *( int * )ret
= atoi( value
);
422 /** Get the error code - note that we simply the success return.
425 response_codes
miracle_command_get_error( command_argument cmd
)
427 response_codes ret
= valerie_response_get_error_code( cmd
->response
);
428 if ( ret
== RESPONSE_SUCCESS_N
|| ret
== RESPONSE_SUCCESS_1
)
429 ret
= RESPONSE_SUCCESS
;
433 /** Execute the command.
436 static valerie_response
miracle_local_execute( miracle_local local
, char *command
)
438 command_argument_t cmd
;
439 cmd
.parser
= local
->parser
;
440 cmd
.response
= valerie_response_init( );
441 cmd
.tokeniser
= valerie_tokeniser_init( );
442 cmd
.command
= command
;
445 cmd
.root_dir
= local
->root_dir
;
447 /* Set the default error */
448 miracle_command_set_error( &cmd
, RESPONSE_UNKNOWN_COMMAND
);
450 /* Parse the command */
451 if ( valerie_tokeniser_parse_new( cmd
.tokeniser
, command
, " " ) > 0 )
454 char *value
= valerie_tokeniser_get_string( cmd
.tokeniser
, 0 );
457 /* Strip quotes from all tokens */
458 for ( index
= 0; index
< valerie_tokeniser_count( cmd
.tokeniser
); index
++ )
459 valerie_util_strip( valerie_tokeniser_get_string( cmd
.tokeniser
, index
), '\"' );
461 /* Search the vocabulary array for value */
462 for ( index
= 1; !found
&& vocabulary
[ index
].command
!= NULL
; index
++ )
463 if ( ( found
= !strcasecmp( vocabulary
[ index
].command
, value
) ) )
466 /* If we found something, the handle the args and call the handler. */
471 miracle_command_set_error( &cmd
, RESPONSE_SUCCESS
);
473 if ( vocabulary
[ index
].is_unit
)
475 cmd
.unit
= miracle_command_parse_unit( &cmd
, position
);
476 if ( cmd
.unit
== -1 )
477 miracle_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
481 if ( miracle_command_get_error( &cmd
) == RESPONSE_SUCCESS
)
483 cmd
.argument
= miracle_command_parse_argument( &cmd
, position
, vocabulary
[ index
].type
, command
);
484 if ( cmd
.argument
== NULL
&& vocabulary
[ index
].type
!= ATYPE_NONE
)
485 miracle_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
489 if ( miracle_command_get_error( &cmd
) == RESPONSE_SUCCESS
)
491 response_codes error
= vocabulary
[ index
].operation( &cmd
);
492 miracle_command_set_error( &cmd
, error
);
495 free( cmd
.argument
);
499 valerie_tokeniser_close( cmd
.tokeniser
);
504 static valerie_response
miracle_local_receive( miracle_local local
, char *command
, char *doc
)
506 command_argument_t cmd
;
507 cmd
.parser
= local
->parser
;
508 cmd
.response
= valerie_response_init( );
509 cmd
.tokeniser
= valerie_tokeniser_init( );
510 cmd
.command
= command
;
513 cmd
.root_dir
= local
->root_dir
;
515 /* Set the default error */
516 miracle_command_set_error( &cmd
, RESPONSE_SUCCESS
);
518 /* Parse the command */
519 if ( valerie_tokeniser_parse_new( cmd
.tokeniser
, command
, " " ) > 0 )
524 /* Strip quotes from all tokens */
525 for ( index
= 0; index
< valerie_tokeniser_count( cmd
.tokeniser
); index
++ )
526 valerie_util_strip( valerie_tokeniser_get_string( cmd
.tokeniser
, index
), '\"' );
528 cmd
.unit
= miracle_command_parse_unit( &cmd
, position
);
529 if ( cmd
.unit
== -1 )
530 miracle_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
533 miracle_receive( &cmd
, doc
);
534 miracle_command_set_error( &cmd
, RESPONSE_SUCCESS
);
536 free( cmd
.argument
);
539 valerie_tokeniser_close( cmd
.tokeniser
);
544 static valerie_response
miracle_local_push( miracle_local local
, char *command
, mlt_service service
)
546 command_argument_t cmd
;
547 cmd
.parser
= local
->parser
;
548 cmd
.response
= valerie_response_init( );
549 cmd
.tokeniser
= valerie_tokeniser_init( );
550 cmd
.command
= command
;
553 cmd
.root_dir
= local
->root_dir
;
555 /* Set the default error */
556 miracle_command_set_error( &cmd
, RESPONSE_SUCCESS
);
558 /* Parse the command */
559 if ( valerie_tokeniser_parse_new( cmd
.tokeniser
, command
, " " ) > 0 )
564 /* Strip quotes from all tokens */
565 for ( index
= 0; index
< valerie_tokeniser_count( cmd
.tokeniser
); index
++ )
566 valerie_util_strip( valerie_tokeniser_get_string( cmd
.tokeniser
, index
), '\"' );
568 cmd
.unit
= miracle_command_parse_unit( &cmd
, position
);
569 if ( cmd
.unit
== -1 )
570 miracle_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
573 miracle_push( &cmd
, service
);
574 miracle_command_set_error( &cmd
, RESPONSE_SUCCESS
);
576 free( cmd
.argument
);
579 valerie_tokeniser_close( cmd
.tokeniser
);
584 /** Close the parser.
587 static void miracle_local_close( miracle_local local
)
589 miracle_delete_all_units();
591 //pthread_kill_other_threads_np();
592 miracle_log( LOG_DEBUG
, "Clean shutdown." );
594 //mlt_factory_close( );