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 */
32 /* Valerie header files */
33 #include <valerie/valerie_util.h>
35 /* MLT header files. */
36 #include <framework/mlt_factory.h>
38 /* Application header files */
39 #include "miracle_local.h"
40 #include "miracle_connection.h"
41 #include "miracle_commands.h"
42 #include "miracle_unit_commands.h"
43 #include "miracle_log.h"
45 /** Private miracle_local structure.
50 valerie_parser parser
;
53 *miracle_local
, miracle_local_t
;
55 /** Forward declarations.
58 static valerie_response
miracle_local_connect( miracle_local
);
59 static valerie_response
miracle_local_execute( miracle_local
, char * );
60 static void miracle_local_close( miracle_local
);
61 response_codes
miracle_help( command_argument arg
);
62 response_codes
miracle_run( command_argument arg
);
63 response_codes
miracle_shutdown( command_argument arg
);
65 /** DV Parser constructor.
68 valerie_parser
miracle_parser_init_local( )
70 valerie_parser parser
= malloc( sizeof( valerie_parser_t
) );
71 miracle_local local
= malloc( sizeof( miracle_local_t
) );
75 memset( parser
, 0, sizeof( valerie_parser_t
) );
77 parser
->connect
= (parser_connect
)miracle_local_connect
;
78 parser
->execute
= (parser_execute
)miracle_local_execute
;
79 parser
->close
= (parser_close
)miracle_local_close
;
84 memset( local
, 0, sizeof( miracle_local_t
) );
85 local
->parser
= parser
;
86 local
->root_dir
[0] = '/';
89 // Construct the factory
90 mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
95 /** response status code/message pair
105 /** response messages
108 static responses_t responses
[] =
110 {RESPONSE_SUCCESS
, "OK"},
111 {RESPONSE_SUCCESS_N
, "OK"},
112 {RESPONSE_SUCCESS_1
, "OK"},
113 {RESPONSE_UNKNOWN_COMMAND
, "Unknown command"},
114 {RESPONSE_TIMEOUT
, "Operation timed out"},
115 {RESPONSE_MISSING_ARG
, "Argument missing"},
116 {RESPONSE_INVALID_UNIT
, "Unit not found"},
117 {RESPONSE_BAD_FILE
, "Failed to locate or open clip"},
118 {RESPONSE_OUT_OF_RANGE
, "Argument value out of range"},
119 {RESPONSE_TOO_MANY_FILES
, "Too many files open"},
120 {RESPONSE_ERROR
, "Server Error"}
135 /** A command definition.
140 /* The command string corresponding to this operation (e.g. "play") */
142 /* The function associated with it */
143 response_codes (*operation
) ( command_argument
);
144 /* a boolean to indicate if this is a unit or global command
145 unit commands require a unit identifier as first argument */
147 /* What type is the argument (RTTI :-) ATYPE_whatever */
149 /* online help information */
154 /* The following define the queue of commands available to the user. The
155 first entry is the name of the command (the string which must be typed),
156 the second command is the function associated with it, the third argument
157 is for the type of the argument, and the last argument specifies whether
158 this is something which should be handled immediately or whether it
159 should be queued (only robot motion commands need to be queued). */
161 static command_t vocabulary
[] =
163 {"BYE", NULL
, 0, ATYPE_NONE
, "Terminates the session. Units are not removed and task queue is not flushed."},
164 {"HELP", miracle_help
, 0, ATYPE_NONE
, "Display this information!"},
165 {"NLS", miracle_list_nodes
, 0, ATYPE_NONE
, "List the AV/C nodes on the 1394 bus."},
166 {"UADD", miracle_add_unit
, 0, ATYPE_STRING
, "Create a new DV unit (virtual VTR) to transmit to receiver specified in GUID argument."},
167 {"ULS", miracle_list_units
, 0, ATYPE_NONE
, "Lists the units that have already been added to the server."},
168 {"CLS", miracle_list_clips
, 0, ATYPE_STRING
, "Lists the clips at directory name argument."},
169 {"SET", miracle_set_global_property
, 0, ATYPE_STRING
, "Set a server configuration property."},
170 {"GET", miracle_get_global_property
, 0, ATYPE_STRING
, "Get a server configuration property."},
171 {"RUN", miracle_run
, 0, ATYPE_STRING
, "Run a batch file." },
172 {"LIST", miracle_list
, 1, ATYPE_NONE
, "List the playlist associated to a unit."},
173 {"LOAD", miracle_load
, 1, ATYPE_STRING
, "Load clip specified in absolute filename argument."},
174 {"INSERT", miracle_insert
, 1, ATYPE_STRING
, "Insert a clip at the given clip index."},
175 {"REMOVE", miracle_remove
, 1, ATYPE_NONE
, "Remove a clip at the given clip index."},
176 {"CLEAN", miracle_clean
, 1, ATYPE_NONE
, "Clean a unit by removing all but the currently playing clip."},
177 {"MOVE", miracle_move
, 1, ATYPE_INT
, "Move a clip to another clip index."},
178 {"APND", miracle_append
, 1, ATYPE_STRING
, "Append a clip specified in absolute filename argument."},
179 {"PLAY", miracle_play
, 1, ATYPE_NONE
, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."},
180 {"STOP", miracle_stop
, 1, ATYPE_NONE
, "Stop a loaded and playing clip."},
181 {"PAUSE", miracle_pause
, 1, ATYPE_NONE
, "Pause a playing clip."},
182 {"REW", miracle_rewind
, 1, ATYPE_NONE
, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."},
183 {"FF", miracle_ff
, 1, ATYPE_NONE
, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."},
184 {"STEP", miracle_step
, 1, ATYPE_INT
, "Step argument number of frames forward or backward."},
185 {"GOTO", miracle_goto
, 1, ATYPE_INT
, "Jump to frame number supplied as argument."},
186 {"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"},
187 {"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."},
188 {"USTA", miracle_get_unit_status
, 1, ATYPE_NONE
, "Report information about the unit."},
189 {"USET", miracle_set_unit_property
, 1, ATYPE_STRING
, "Set a unit configuration property."},
190 {"UGET", miracle_get_unit_property
, 1, ATYPE_STRING
, "Get a unit configuration property."},
191 {"XFER", miracle_transfer
, 1, ATYPE_STRING
, "Transfer the unit's clip to another unit specified as argument."},
192 {"SHUTDOWN", miracle_shutdown
, 0, ATYPE_NONE
, "Shutdown the server."},
193 {NULL
, NULL
, 0, ATYPE_NONE
, NULL
}
199 static char helpstr
[] =
200 "Miracle -- A Multimedia Playout Server\n"
201 " Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n"
203 " Dan Dennedy <dan@dennedy.org>\n"
204 " Charles Yates <charles.yates@pandora.be>\n"
205 "Available commands:\n";
207 /** Lookup the response message for a status code.
210 inline char *get_response_msg( int code
)
213 for ( i
= 0; responses
[ i
].message
!= NULL
&& code
!= responses
[ i
].code
; i
++ ) ;
214 return responses
[ i
].message
;
217 /** Tell the user the miracle command set
220 response_codes
miracle_help( command_argument cmd_arg
)
224 valerie_response_printf( cmd_arg
->response
, 10240, "%s", helpstr
);
226 for ( i
= 0; vocabulary
[ i
].command
!= NULL
; i
++ )
227 valerie_response_printf( cmd_arg
->response
, 1024,
229 vocabulary
[ i
].command
,
230 vocabulary
[ i
].help
);
232 valerie_response_printf( cmd_arg
->response
, 2, "\n" );
234 return RESPONSE_SUCCESS_N
;
237 /** Execute a batch file.
240 response_codes
miracle_run( command_argument cmd_arg
)
242 valerie_response temp
= valerie_parser_run( cmd_arg
->parser
, (char *)cmd_arg
->argument
);
248 valerie_response_set_error( cmd_arg
->response
,
249 valerie_response_get_error_code( temp
),
250 valerie_response_get_error_string( temp
) );
252 for ( index
= 1; index
< valerie_response_count( temp
); index
++ )
253 valerie_response_printf( cmd_arg
->response
, 10240, "%s\n", valerie_response_get_line( temp
, index
) );
255 valerie_response_close( temp
);
258 return valerie_response_get_error_code( cmd_arg
->response
);
261 response_codes
miracle_shutdown( command_argument cmd_arg
)
264 return RESPONSE_SUCCESS
;
267 /** Processes 'thread' id
270 static pthread_t self
;
272 /* Signal handler to deal with various shutdown signals. Basically this
273 should clean up and power down the motor. Note that the death of any
274 child thread will kill all thrads. */
276 void signal_handler( int sig
)
278 if ( pthread_equal( self
, pthread_self( ) ) )
282 miracle_log( LOG_DEBUG
, "Received %s - shutting down.", strsignal(sig
) );
284 miracle_log( LOG_DEBUG
, "Received signal %i - shutting down.", sig
);
291 static void sigsegv_handler()
298 miracle_log( LOG_CRIT
, "\a\nMiracle experienced a segmentation fault.\n"
299 "Dumping stack from the offending thread\n\n" );
300 size
= backtrace( array
, 10 );
301 strings
= backtrace_symbols( array
, size
);
303 miracle_log( LOG_CRIT
, "Obtained %zd stack frames.\n", size
);
305 for ( i
= 0; i
< size
; i
++ )
306 miracle_log( LOG_CRIT
, "%s", strings
[ i
] );
310 miracle_log( LOG_CRIT
, "\nDone dumping - exiting.\n" );
311 exit( EXIT_FAILURE
);
316 /** Local 'connect' function.
319 static valerie_response
miracle_local_connect( miracle_local local
)
321 valerie_response response
= valerie_response_init( );
323 self
= pthread_self( );
325 valerie_response_set_error( response
, 100, "VTR Ready" );
327 signal( SIGHUP
, signal_handler
);
328 signal( SIGINT
, signal_handler
);
329 signal( SIGTERM
, SIG_DFL
);
330 signal( SIGSTOP
, signal_handler
);
331 signal( SIGPIPE
, signal_handler
);
332 signal( SIGALRM
, signal_handler
);
333 signal( SIGCHLD
, SIG_IGN
);
334 if ( getenv( "MLT_SIGSEGV" ) )
335 signal( SIGSEGV
, sigsegv_handler
);
340 /** Set the error and determine the message associated to this command.
343 void miracle_command_set_error( command_argument cmd
, response_codes code
)
345 valerie_response_set_error( cmd
->response
, code
, get_response_msg( code
) );
348 /** Parse the unit argument.
351 int miracle_command_parse_unit( command_argument cmd
, int argument
)
354 char *string
= valerie_tokeniser_get_string( cmd
->tokeniser
, argument
);
355 if ( string
!= NULL
&& ( string
[ 0 ] == 'U' || string
[ 0 ] == 'u' ) && strlen( string
) > 1 )
356 unit
= atoi( string
+ 1 );
360 /** Parse a normal argument.
363 void *miracle_command_parse_argument( command_argument cmd
, int argument
, arguments_types type
)
366 char *value
= valerie_tokeniser_get_string( cmd
->tokeniser
, argument
);
376 ret
= malloc( sizeof( float ) );
378 *( float * )ret
= atof( value
);
382 ret
= strdup( value
);
386 ret
= malloc( sizeof( int ) );
388 *( int * )ret
= atoi( value
);
396 /** Get the error code - note that we simply the success return.
399 response_codes
miracle_command_get_error( command_argument cmd
)
401 response_codes ret
= valerie_response_get_error_code( cmd
->response
);
402 if ( ret
== RESPONSE_SUCCESS_N
|| ret
== RESPONSE_SUCCESS_1
)
403 ret
= RESPONSE_SUCCESS
;
407 /** Execute the command.
410 static valerie_response
miracle_local_execute( miracle_local local
, char *command
)
412 command_argument_t cmd
;
413 cmd
.parser
= local
->parser
;
414 cmd
.response
= valerie_response_init( );
415 cmd
.tokeniser
= valerie_tokeniser_init( );
416 cmd
.command
= command
;
419 cmd
.root_dir
= local
->root_dir
;
421 /* Set the default error */
422 miracle_command_set_error( &cmd
, RESPONSE_UNKNOWN_COMMAND
);
424 /* Parse the command */
425 if ( valerie_tokeniser_parse_new( cmd
.tokeniser
, command
, " " ) > 0 )
428 char *value
= valerie_tokeniser_get_string( cmd
.tokeniser
, 0 );
431 /* Strip quotes from all tokens */
432 for ( index
= 0; index
< valerie_tokeniser_count( cmd
.tokeniser
); index
++ )
433 valerie_util_strip( valerie_tokeniser_get_string( cmd
.tokeniser
, index
), '\"' );
435 /* Search the vocabulary array for value */
436 for ( index
= 1; !found
&& vocabulary
[ index
].command
!= NULL
; index
++ )
437 if ( ( found
= !strcasecmp( vocabulary
[ index
].command
, value
) ) )
440 /* If we found something, the handle the args and call the handler. */
445 miracle_command_set_error( &cmd
, RESPONSE_SUCCESS
);
447 if ( vocabulary
[ index
].is_unit
)
449 cmd
.unit
= miracle_command_parse_unit( &cmd
, position
);
450 if ( cmd
.unit
== -1 )
451 miracle_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
455 if ( miracle_command_get_error( &cmd
) == RESPONSE_SUCCESS
)
457 cmd
.argument
= miracle_command_parse_argument( &cmd
, position
, vocabulary
[ index
].type
);
458 if ( cmd
.argument
== NULL
&& vocabulary
[ index
].type
!= ATYPE_NONE
)
459 miracle_command_set_error( &cmd
, RESPONSE_MISSING_ARG
);
463 if ( miracle_command_get_error( &cmd
) == RESPONSE_SUCCESS
)
465 response_codes error
= vocabulary
[ index
].operation( &cmd
);
466 miracle_command_set_error( &cmd
, error
);
469 free( cmd
.argument
);
473 valerie_tokeniser_close( cmd
.tokeniser
);
478 /** Close the parser.
481 static void miracle_local_close( miracle_local local
)
483 miracle_delete_all_units();
484 pthread_kill_other_threads_np();
485 miracle_log( LOG_DEBUG
, "Clean shutdown." );
487 //mlt_factory_close( );