More configure and build tuning
[melted] / src / miracle / miracle_local.c
1 /*
2 * miracle_local.c -- Local Miracle Parser
3 * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
5 *
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.
10 *
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.
15 *
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.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 /* System header files */
26 #include <stdlib.h>
27 #include <string.h>
28 #include <signal.h>
29
30 /* Needed for backtrace on linux */
31 #ifdef linux
32 #include <execinfo.h>
33 #endif
34
35 /* Valerie header files */
36 #include <valerie/valerie_util.h>
37
38 /* MLT header files. */
39 #include <framework/mlt_factory.h>
40
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"
47
48 /** Private miracle_local structure.
49 */
50
51 typedef struct
52 {
53 valerie_parser parser;
54 char root_dir[1024];
55 }
56 *miracle_local, miracle_local_t;
57
58 /** Forward declarations.
59 */
60
61 static valerie_response miracle_local_connect( miracle_local );
62 static valerie_response miracle_local_execute( miracle_local, char * );
63 static void miracle_local_close( miracle_local );
64 response_codes miracle_help( command_argument arg );
65 response_codes miracle_run( command_argument arg );
66 response_codes miracle_shutdown( command_argument arg );
67
68 /** DV Parser constructor.
69 */
70
71 valerie_parser miracle_parser_init_local( )
72 {
73 valerie_parser parser = malloc( sizeof( valerie_parser_t ) );
74 miracle_local local = malloc( sizeof( miracle_local_t ) );
75
76 if ( parser != NULL )
77 {
78 memset( parser, 0, sizeof( valerie_parser_t ) );
79
80 parser->connect = (parser_connect)miracle_local_connect;
81 parser->execute = (parser_execute)miracle_local_execute;
82 parser->close = (parser_close)miracle_local_close;
83 parser->real = local;
84
85 if ( local != NULL )
86 {
87 memset( local, 0, sizeof( miracle_local_t ) );
88 local->parser = parser;
89 local->root_dir[0] = '/';
90 }
91
92 // Construct the factory
93 mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
94 }
95 return parser;
96 }
97
98 /** response status code/message pair
99 */
100
101 typedef struct
102 {
103 int code;
104 char *message;
105 }
106 responses_t;
107
108 /** response messages
109 */
110
111 static responses_t responses [] =
112 {
113 {RESPONSE_SUCCESS, "OK"},
114 {RESPONSE_SUCCESS_N, "OK"},
115 {RESPONSE_SUCCESS_1, "OK"},
116 {RESPONSE_UNKNOWN_COMMAND, "Unknown command"},
117 {RESPONSE_TIMEOUT, "Operation timed out"},
118 {RESPONSE_MISSING_ARG, "Argument missing"},
119 {RESPONSE_INVALID_UNIT, "Unit not found"},
120 {RESPONSE_BAD_FILE, "Failed to locate or open clip"},
121 {RESPONSE_OUT_OF_RANGE, "Argument value out of range"},
122 {RESPONSE_TOO_MANY_FILES, "Too many files open"},
123 {RESPONSE_ERROR, "Server Error"}
124 };
125
126 /** Argument types.
127 */
128
129 typedef enum
130 {
131 ATYPE_NONE,
132 ATYPE_FLOAT,
133 ATYPE_STRING,
134 ATYPE_INT,
135 ATYPE_PAIR
136 }
137 arguments_types;
138
139 /** A command definition.
140 */
141
142 typedef struct
143 {
144 /* The command string corresponding to this operation (e.g. "play") */
145 char *command;
146 /* The function associated with it */
147 response_codes (*operation) ( command_argument );
148 /* a boolean to indicate if this is a unit or global command
149 unit commands require a unit identifier as first argument */
150 int is_unit;
151 /* What type is the argument (RTTI :-) ATYPE_whatever */
152 int type;
153 /* online help information */
154 char *help;
155 }
156 command_t;
157
158 /* The following define the queue of commands available to the user. The
159 first entry is the name of the command (the string which must be typed),
160 the second command is the function associated with it, the third argument
161 is for the type of the argument, and the last argument specifies whether
162 this is something which should be handled immediately or whether it
163 should be queued (only robot motion commands need to be queued). */
164
165 static command_t vocabulary[] =
166 {
167 {"BYE", NULL, 0, ATYPE_NONE, "Terminates the session. Units are not removed and task queue is not flushed."},
168 {"HELP", miracle_help, 0, ATYPE_NONE, "Display this information!"},
169 {"NLS", miracle_list_nodes, 0, ATYPE_NONE, "List the AV/C nodes on the 1394 bus."},
170 {"UADD", miracle_add_unit, 0, ATYPE_STRING, "Create a new DV unit (virtual VTR) to transmit to receiver specified in GUID argument."},
171 {"ULS", miracle_list_units, 0, ATYPE_NONE, "Lists the units that have already been added to the server."},
172 {"CLS", miracle_list_clips, 0, ATYPE_STRING, "Lists the clips at directory name argument."},
173 {"SET", miracle_set_global_property, 0, ATYPE_PAIR, "Set a server configuration property."},
174 {"GET", miracle_get_global_property, 0, ATYPE_STRING, "Get a server configuration property."},
175 {"RUN", miracle_run, 0, ATYPE_STRING, "Run a batch file." },
176 {"LIST", miracle_list, 1, ATYPE_NONE, "List the playlist associated to a unit."},
177 {"LOAD", miracle_load, 1, ATYPE_STRING, "Load clip specified in absolute filename argument."},
178 {"INSERT", miracle_insert, 1, ATYPE_STRING, "Insert a clip at the given clip index."},
179 {"REMOVE", miracle_remove, 1, ATYPE_NONE, "Remove a clip at the given clip index."},
180 {"CLEAN", miracle_clean, 1, ATYPE_NONE, "Clean a unit by removing all but the currently playing clip."},
181 {"CLEAR", miracle_clear, 1, ATYPE_NONE, "Clear a unit by removing all clips."},
182 {"MOVE", miracle_move, 1, ATYPE_INT, "Move a clip to another clip index."},
183 {"APND", miracle_append, 1, ATYPE_STRING, "Append a clip specified in absolute filename argument."},
184 {"PLAY", miracle_play, 1, ATYPE_NONE, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."},
185 {"STOP", miracle_stop, 1, ATYPE_NONE, "Stop a loaded and playing clip."},
186 {"PAUSE", miracle_pause, 1, ATYPE_NONE, "Pause a playing clip."},
187 {"REW", miracle_rewind, 1, ATYPE_NONE, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."},
188 {"FF", miracle_ff, 1, ATYPE_NONE, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."},
189 {"STEP", miracle_step, 1, ATYPE_INT, "Step argument number of frames forward or backward."},
190 {"GOTO", miracle_goto, 1, ATYPE_INT, "Jump to frame number supplied as argument."},
191 {"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"},
192 {"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."},
193 {"USTA", miracle_get_unit_status, 1, ATYPE_NONE, "Report information about the unit."},
194 {"USET", miracle_set_unit_property, 1, ATYPE_PAIR, "Set a unit configuration property."},
195 {"UGET", miracle_get_unit_property, 1, ATYPE_STRING, "Get a unit configuration property."},
196 {"XFER", miracle_transfer, 1, ATYPE_STRING, "Transfer the unit's clip to another unit specified as argument."},
197 {"SHUTDOWN", miracle_shutdown, 0, ATYPE_NONE, "Shutdown the server."},
198 {NULL, NULL, 0, ATYPE_NONE, NULL}
199 };
200
201 /** Usage message
202 */
203
204 static char helpstr [] =
205 "Miracle -- A Multimedia Playout Server\n"
206 " Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n"
207 " Authors:\n"
208 " Dan Dennedy <dan@dennedy.org>\n"
209 " Charles Yates <charles.yates@pandora.be>\n"
210 "Available commands:\n";
211
212 /** Lookup the response message for a status code.
213 */
214
215 inline char *get_response_msg( int code )
216 {
217 int i = 0;
218 for ( i = 0; responses[ i ].message != NULL && code != responses[ i ].code; i ++ ) ;
219 return responses[ i ].message;
220 }
221
222 /** Tell the user the miracle command set
223 */
224
225 response_codes miracle_help( command_argument cmd_arg )
226 {
227 int i = 0;
228
229 valerie_response_printf( cmd_arg->response, 10240, "%s", helpstr );
230
231 for ( i = 0; vocabulary[ i ].command != NULL; i ++ )
232 valerie_response_printf( cmd_arg->response, 1024,
233 "%-10.10s%s\n",
234 vocabulary[ i ].command,
235 vocabulary[ i ].help );
236
237 valerie_response_printf( cmd_arg->response, 2, "\n" );
238
239 return RESPONSE_SUCCESS_N;
240 }
241
242 /** Execute a batch file.
243 */
244
245 response_codes miracle_run( command_argument cmd_arg )
246 {
247 valerie_response temp = valerie_parser_run( cmd_arg->parser, (char *)cmd_arg->argument );
248
249 if ( temp != NULL )
250 {
251 int index = 0;
252
253 valerie_response_set_error( cmd_arg->response,
254 valerie_response_get_error_code( temp ),
255 valerie_response_get_error_string( temp ) );
256
257 for ( index = 1; index < valerie_response_count( temp ); index ++ )
258 valerie_response_printf( cmd_arg->response, 10240, "%s\n", valerie_response_get_line( temp, index ) );
259
260 valerie_response_close( temp );
261 }
262
263 return valerie_response_get_error_code( cmd_arg->response );
264 }
265
266 response_codes miracle_shutdown( command_argument cmd_arg )
267 {
268 exit( 0 );
269 return RESPONSE_SUCCESS;
270 }
271
272 /** Processes 'thread' id
273 */
274
275 static pthread_t self;
276
277 /* Signal handler to deal with various shutdown signals. Basically this
278 should clean up and power down the motor. Note that the death of any
279 child thread will kill all thrads. */
280
281 void signal_handler( int sig )
282 {
283 if ( pthread_equal( self, pthread_self( ) ) )
284 {
285
286 #ifdef _GNU_SOURCE
287 miracle_log( LOG_DEBUG, "Received %s - shutting down.", strsignal(sig) );
288 #else
289 miracle_log( LOG_DEBUG, "Received signal %i - shutting down.", sig );
290 #endif
291
292 exit(EXIT_SUCCESS);
293 }
294 }
295
296 static void sigsegv_handler()
297 {
298 #ifdef linux
299 void *array[ 10 ];
300 size_t size;
301 char **strings;
302 size_t i;
303
304 miracle_log( LOG_CRIT, "\a\nMiracle experienced a segmentation fault.\n"
305 "Dumping stack from the offending thread\n\n" );
306 size = backtrace( array, 10 );
307 strings = backtrace_symbols( array, size );
308
309 miracle_log( LOG_CRIT, "Obtained %zd stack frames.\n", size );
310
311 for ( i = 0; i < size; i++ )
312 miracle_log( LOG_CRIT, "%s", strings[ i ] );
313
314 free( strings );
315
316 miracle_log( LOG_CRIT, "\nDone dumping - exiting.\n" );
317 #else
318 miracle_log( LOG_CRIT, "\a\nMiracle experienced a segmentation fault.\n" );
319 #endif
320 exit( EXIT_FAILURE );
321 }
322
323
324
325 /** Local 'connect' function.
326 */
327
328 static valerie_response miracle_local_connect( miracle_local local )
329 {
330 valerie_response response = valerie_response_init( );
331
332 self = pthread_self( );
333
334 valerie_response_set_error( response, 100, "VTR Ready" );
335
336 signal( SIGHUP, signal_handler );
337 signal( SIGINT, signal_handler );
338 signal( SIGTERM, SIG_DFL );
339 signal( SIGSTOP, signal_handler );
340 signal( SIGPIPE, signal_handler );
341 signal( SIGALRM, signal_handler );
342 signal( SIGCHLD, SIG_IGN );
343 if ( getenv( "MLT_SIGSEGV" ) )
344 signal( SIGSEGV, sigsegv_handler );
345
346 return response;
347 }
348
349 /** Set the error and determine the message associated to this command.
350 */
351
352 void miracle_command_set_error( command_argument cmd, response_codes code )
353 {
354 valerie_response_set_error( cmd->response, code, get_response_msg( code ) );
355 }
356
357 /** Parse the unit argument.
358 */
359
360 int miracle_command_parse_unit( command_argument cmd, int argument )
361 {
362 int unit = -1;
363 char *string = valerie_tokeniser_get_string( cmd->tokeniser, argument );
364 if ( string != NULL && ( string[ 0 ] == 'U' || string[ 0 ] == 'u' ) && strlen( string ) > 1 )
365 unit = atoi( string + 1 );
366 return unit;
367 }
368
369 /** Parse a normal argument.
370 */
371
372 void *miracle_command_parse_argument( command_argument cmd, int argument, arguments_types type, char *command )
373 {
374 void *ret = NULL;
375 char *value = valerie_tokeniser_get_string( cmd->tokeniser, argument );
376
377 if ( value != NULL )
378 {
379 switch( type )
380 {
381 case ATYPE_NONE:
382 break;
383
384 case ATYPE_FLOAT:
385 ret = malloc( sizeof( float ) );
386 if ( ret != NULL )
387 *( float * )ret = atof( value );
388 break;
389
390 case ATYPE_STRING:
391 ret = strdup( value );
392 break;
393
394 case ATYPE_PAIR:
395 if ( strchr( command, '=' ) )
396 {
397 char *ptr = strchr( command, '=' );
398 while ( *( ptr - 1 ) != ' ' )
399 ptr --;
400 ret = strdup( ptr );
401 ptr = ret;
402 while( ptr[ strlen( ptr ) - 1 ] == ' ' )
403 ptr[ strlen( ptr ) - 1 ] = '\0';
404 }
405 break;
406
407 case ATYPE_INT:
408 ret = malloc( sizeof( int ) );
409 if ( ret != NULL )
410 *( int * )ret = atoi( value );
411 break;
412 }
413 }
414
415 return ret;
416 }
417
418 /** Get the error code - note that we simply the success return.
419 */
420
421 response_codes miracle_command_get_error( command_argument cmd )
422 {
423 response_codes ret = valerie_response_get_error_code( cmd->response );
424 if ( ret == RESPONSE_SUCCESS_N || ret == RESPONSE_SUCCESS_1 )
425 ret = RESPONSE_SUCCESS;
426 return ret;
427 }
428
429 /** Execute the command.
430 */
431
432 static valerie_response miracle_local_execute( miracle_local local, char *command )
433 {
434 command_argument_t cmd;
435 cmd.parser = local->parser;
436 cmd.response = valerie_response_init( );
437 cmd.tokeniser = valerie_tokeniser_init( );
438 cmd.command = command;
439 cmd.unit = -1;
440 cmd.argument = NULL;
441 cmd.root_dir = local->root_dir;
442
443 /* Set the default error */
444 miracle_command_set_error( &cmd, RESPONSE_UNKNOWN_COMMAND );
445
446 /* Parse the command */
447 if ( valerie_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
448 {
449 int index = 0;
450 char *value = valerie_tokeniser_get_string( cmd.tokeniser, 0 );
451 int found = 0;
452
453 /* Strip quotes from all tokens */
454 for ( index = 0; index < valerie_tokeniser_count( cmd.tokeniser ); index ++ )
455 valerie_util_strip( valerie_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
456
457 /* Search the vocabulary array for value */
458 for ( index = 1; !found && vocabulary[ index ].command != NULL; index ++ )
459 if ( ( found = !strcasecmp( vocabulary[ index ].command, value ) ) )
460 break;
461
462 /* If we found something, the handle the args and call the handler. */
463 if ( found )
464 {
465 int position = 1;
466
467 miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
468
469 if ( vocabulary[ index ].is_unit )
470 {
471 cmd.unit = miracle_command_parse_unit( &cmd, position );
472 if ( cmd.unit == -1 )
473 miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
474 position ++;
475 }
476
477 if ( miracle_command_get_error( &cmd ) == RESPONSE_SUCCESS )
478 {
479 cmd.argument = miracle_command_parse_argument( &cmd, position, vocabulary[ index ].type, command );
480 if ( cmd.argument == NULL && vocabulary[ index ].type != ATYPE_NONE )
481 miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
482 position ++;
483 }
484
485 if ( miracle_command_get_error( &cmd ) == RESPONSE_SUCCESS )
486 {
487 response_codes error = vocabulary[ index ].operation( &cmd );
488 miracle_command_set_error( &cmd, error );
489 }
490
491 free( cmd.argument );
492 }
493 }
494
495 valerie_tokeniser_close( cmd.tokeniser );
496
497 return cmd.response;
498 }
499
500 /** Close the parser.
501 */
502
503 static void miracle_local_close( miracle_local local )
504 {
505 miracle_delete_all_units();
506 #ifdef linux
507 pthread_kill_other_threads_np();
508 miracle_log( LOG_DEBUG, "Clean shutdown." );
509 free( local );
510 mlt_factory_close( );
511 #endif
512 }