Memory pooling part 2 and other optimisations
[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 #include <execinfo.h>
30
31 /* Valerie header files */
32 #include <valerie/valerie_util.h>
33
34 /* MLT header files. */
35 #include <framework/mlt_factory.h>
36
37 /* Application header files */
38 #include "miracle_local.h"
39 #include "miracle_connection.h"
40 #include "miracle_commands.h"
41 #include "miracle_unit_commands.h"
42 #include "miracle_log.h"
43
44 /** Private miracle_local structure.
45 */
46
47 typedef struct
48 {
49 valerie_parser parser;
50 char root_dir[1024];
51 }
52 *miracle_local, miracle_local_t;
53
54 /** Forward declarations.
55 */
56
57 static valerie_response miracle_local_connect( miracle_local );
58 static valerie_response miracle_local_execute( miracle_local, char * );
59 static void miracle_local_close( miracle_local );
60 response_codes miracle_help( command_argument arg );
61 response_codes miracle_run( command_argument arg );
62 response_codes miracle_shutdown( command_argument arg );
63
64 /** DV Parser constructor.
65 */
66
67 valerie_parser miracle_parser_init_local( )
68 {
69 valerie_parser parser = malloc( sizeof( valerie_parser_t ) );
70 miracle_local local = malloc( sizeof( miracle_local_t ) );
71
72 if ( parser != NULL )
73 {
74 memset( parser, 0, sizeof( valerie_parser_t ) );
75
76 parser->connect = (parser_connect)miracle_local_connect;
77 parser->execute = (parser_execute)miracle_local_execute;
78 parser->close = (parser_close)miracle_local_close;
79 parser->real = local;
80
81 if ( local != NULL )
82 {
83 memset( local, 0, sizeof( miracle_local_t ) );
84 local->parser = parser;
85 local->root_dir[0] = '/';
86 }
87
88 // Construct the factory
89 mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
90 }
91 return parser;
92 }
93
94 /** response status code/message pair
95 */
96
97 typedef struct
98 {
99 int code;
100 char *message;
101 }
102 responses_t;
103
104 /** response messages
105 */
106
107 static responses_t responses [] =
108 {
109 {RESPONSE_SUCCESS, "OK"},
110 {RESPONSE_SUCCESS_N, "OK"},
111 {RESPONSE_SUCCESS_1, "OK"},
112 {RESPONSE_UNKNOWN_COMMAND, "Unknown command"},
113 {RESPONSE_TIMEOUT, "Operation timed out"},
114 {RESPONSE_MISSING_ARG, "Argument missing"},
115 {RESPONSE_INVALID_UNIT, "Unit not found"},
116 {RESPONSE_BAD_FILE, "Failed to locate or open clip"},
117 {RESPONSE_OUT_OF_RANGE, "Argument value out of range"},
118 {RESPONSE_TOO_MANY_FILES, "Too many files open"},
119 {RESPONSE_ERROR, "Server Error"}
120 };
121
122 /** Argument types.
123 */
124
125 typedef enum
126 {
127 ATYPE_NONE,
128 ATYPE_FLOAT,
129 ATYPE_STRING,
130 ATYPE_INT,
131 ATYPE_PAIR
132 }
133 arguments_types;
134
135 /** A command definition.
136 */
137
138 typedef struct
139 {
140 /* The command string corresponding to this operation (e.g. "play") */
141 char *command;
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 */
146 int is_unit;
147 /* What type is the argument (RTTI :-) ATYPE_whatever */
148 int type;
149 /* online help information */
150 char *help;
151 }
152 command_t;
153
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). */
160
161 static command_t vocabulary[] =
162 {
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_PAIR, "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_PAIR, "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}
194 };
195
196 /** Usage message
197 */
198
199 static char helpstr [] =
200 "Miracle -- A Multimedia Playout Server\n"
201 " Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n"
202 " Authors:\n"
203 " Dan Dennedy <dan@dennedy.org>\n"
204 " Charles Yates <charles.yates@pandora.be>\n"
205 "Available commands:\n";
206
207 /** Lookup the response message for a status code.
208 */
209
210 inline char *get_response_msg( int code )
211 {
212 int i = 0;
213 for ( i = 0; responses[ i ].message != NULL && code != responses[ i ].code; i ++ ) ;
214 return responses[ i ].message;
215 }
216
217 /** Tell the user the miracle command set
218 */
219
220 response_codes miracle_help( command_argument cmd_arg )
221 {
222 int i = 0;
223
224 valerie_response_printf( cmd_arg->response, 10240, "%s", helpstr );
225
226 for ( i = 0; vocabulary[ i ].command != NULL; i ++ )
227 valerie_response_printf( cmd_arg->response, 1024,
228 "%-10.10s%s\n",
229 vocabulary[ i ].command,
230 vocabulary[ i ].help );
231
232 valerie_response_printf( cmd_arg->response, 2, "\n" );
233
234 return RESPONSE_SUCCESS_N;
235 }
236
237 /** Execute a batch file.
238 */
239
240 response_codes miracle_run( command_argument cmd_arg )
241 {
242 valerie_response temp = valerie_parser_run( cmd_arg->parser, (char *)cmd_arg->argument );
243
244 if ( temp != NULL )
245 {
246 int index = 0;
247
248 valerie_response_set_error( cmd_arg->response,
249 valerie_response_get_error_code( temp ),
250 valerie_response_get_error_string( temp ) );
251
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 ) );
254
255 valerie_response_close( temp );
256 }
257
258 return valerie_response_get_error_code( cmd_arg->response );
259 }
260
261 response_codes miracle_shutdown( command_argument cmd_arg )
262 {
263 exit( 0 );
264 return RESPONSE_SUCCESS;
265 }
266
267 /** Processes 'thread' id
268 */
269
270 static pthread_t self;
271
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. */
275
276 void signal_handler( int sig )
277 {
278 if ( pthread_equal( self, pthread_self( ) ) )
279 {
280
281 #ifdef _GNU_SOURCE
282 miracle_log( LOG_DEBUG, "Received %s - shutting down.", strsignal(sig) );
283 #else
284 miracle_log( LOG_DEBUG, "Received signal %i - shutting down.", sig );
285 #endif
286
287 exit(EXIT_SUCCESS);
288 }
289 }
290
291 static void sigsegv_handler()
292 {
293 void *array[ 10 ];
294 size_t size;
295 char **strings;
296 size_t i;
297
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 );
302
303 miracle_log( LOG_CRIT, "Obtained %zd stack frames.\n", size );
304
305 for ( i = 0; i < size; i++ )
306 miracle_log( LOG_CRIT, "%s", strings[ i ] );
307
308 free( strings );
309
310 miracle_log( LOG_CRIT, "\nDone dumping - exiting.\n" );
311 exit( EXIT_FAILURE );
312 }
313
314
315
316 /** Local 'connect' function.
317 */
318
319 static valerie_response miracle_local_connect( miracle_local local )
320 {
321 valerie_response response = valerie_response_init( );
322
323 self = pthread_self( );
324
325 valerie_response_set_error( response, 100, "VTR Ready" );
326
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 );
336
337 return response;
338 }
339
340 /** Set the error and determine the message associated to this command.
341 */
342
343 void miracle_command_set_error( command_argument cmd, response_codes code )
344 {
345 valerie_response_set_error( cmd->response, code, get_response_msg( code ) );
346 }
347
348 /** Parse the unit argument.
349 */
350
351 int miracle_command_parse_unit( command_argument cmd, int argument )
352 {
353 int unit = -1;
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 );
357 return unit;
358 }
359
360 /** Parse a normal argument.
361 */
362
363 void *miracle_command_parse_argument( command_argument cmd, int argument, arguments_types type, char *command )
364 {
365 void *ret = NULL;
366 char *value = valerie_tokeniser_get_string( cmd->tokeniser, argument );
367
368 if ( value != NULL )
369 {
370 switch( type )
371 {
372 case ATYPE_NONE:
373 break;
374
375 case ATYPE_FLOAT:
376 ret = malloc( sizeof( float ) );
377 if ( ret != NULL )
378 *( float * )ret = atof( value );
379 break;
380
381 case ATYPE_STRING:
382 ret = strdup( value );
383 break;
384
385 case ATYPE_PAIR:
386 if ( strchr( command, '=' ) )
387 {
388 char *ptr = strchr( command, '=' );
389 while ( *( ptr - 1 ) != ' ' )
390 ptr --;
391 ret = strdup( ptr );
392 ptr = ret;
393 while( ptr[ strlen( ptr ) - 1 ] == ' ' )
394 ptr[ strlen( ptr ) - 1 ] = '\0';
395 }
396 break;
397
398 case ATYPE_INT:
399 ret = malloc( sizeof( int ) );
400 if ( ret != NULL )
401 *( int * )ret = atoi( value );
402 break;
403 }
404 }
405
406 return ret;
407 }
408
409 /** Get the error code - note that we simply the success return.
410 */
411
412 response_codes miracle_command_get_error( command_argument cmd )
413 {
414 response_codes ret = valerie_response_get_error_code( cmd->response );
415 if ( ret == RESPONSE_SUCCESS_N || ret == RESPONSE_SUCCESS_1 )
416 ret = RESPONSE_SUCCESS;
417 return ret;
418 }
419
420 /** Execute the command.
421 */
422
423 static valerie_response miracle_local_execute( miracle_local local, char *command )
424 {
425 command_argument_t cmd;
426 cmd.parser = local->parser;
427 cmd.response = valerie_response_init( );
428 cmd.tokeniser = valerie_tokeniser_init( );
429 cmd.command = command;
430 cmd.unit = -1;
431 cmd.argument = NULL;
432 cmd.root_dir = local->root_dir;
433
434 /* Set the default error */
435 miracle_command_set_error( &cmd, RESPONSE_UNKNOWN_COMMAND );
436
437 /* Parse the command */
438 if ( valerie_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
439 {
440 int index = 0;
441 char *value = valerie_tokeniser_get_string( cmd.tokeniser, 0 );
442 int found = 0;
443
444 /* Strip quotes from all tokens */
445 for ( index = 0; index < valerie_tokeniser_count( cmd.tokeniser ); index ++ )
446 valerie_util_strip( valerie_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
447
448 /* Search the vocabulary array for value */
449 for ( index = 1; !found && vocabulary[ index ].command != NULL; index ++ )
450 if ( ( found = !strcasecmp( vocabulary[ index ].command, value ) ) )
451 break;
452
453 /* If we found something, the handle the args and call the handler. */
454 if ( found )
455 {
456 int position = 1;
457
458 miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
459
460 if ( vocabulary[ index ].is_unit )
461 {
462 cmd.unit = miracle_command_parse_unit( &cmd, position );
463 if ( cmd.unit == -1 )
464 miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
465 position ++;
466 }
467
468 if ( miracle_command_get_error( &cmd ) == RESPONSE_SUCCESS )
469 {
470 cmd.argument = miracle_command_parse_argument( &cmd, position, vocabulary[ index ].type, command );
471 if ( cmd.argument == NULL && vocabulary[ index ].type != ATYPE_NONE )
472 miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
473 position ++;
474 }
475
476 if ( miracle_command_get_error( &cmd ) == RESPONSE_SUCCESS )
477 {
478 response_codes error = vocabulary[ index ].operation( &cmd );
479 miracle_command_set_error( &cmd, error );
480 }
481
482 free( cmd.argument );
483 }
484 }
485
486 valerie_tokeniser_close( cmd.tokeniser );
487
488 return cmd.response;
489 }
490
491 /** Close the parser.
492 */
493
494 static void miracle_local_close( miracle_local local )
495 {
496 miracle_delete_all_units();
497 pthread_kill_other_threads_np();
498 miracle_log( LOG_DEBUG, "Clean shutdown." );
499 free( local );
500 //mlt_factory_close( );
501 }