3 @brief Command-line tool for OSRF
6 #include <opensrf/transport_client.h>
7 #include <opensrf/osrf_message.h>
8 #include <opensrf/osrf_app_session.h>
11 #include <sys/timeb.h>
12 #include <sys/types.h>
15 #include <opensrf/utils.h>
16 #include <opensrf/log.h>
21 #include <readline/readline.h>
22 #include <readline/history.h>
25 @brief A struct of convenience for parsing a command line
28 const char* itr; /**< iterator for input buffer */
29 growing_buffer* buf; /**< output buffer */
32 static void get_string_literal( ArgParser* parser );
33 static void get_json_array( ArgParser* parser );
34 static void get_json_object( ArgParser* parser );
35 static void get_misc( ArgParser* parser );
37 #define SRFSH_PORT 5222
38 #define COMMAND_BUFSIZE 4096
40 static char* tz = NULL;
43 static const char* prompt = "srfsh# ";
45 static char* history_file = NULL;
47 //static int child_dead = 0;
49 static char* login_session = NULL;
51 /* true if we're pretty printing json results */
52 static int pretty_print = 1;
53 /* true if we're bypassing 'less' */
54 static int raw_print = 0;
56 /* our jabber connection */
57 static transport_client* client = NULL;
59 /* the last result we received */
60 static osrfMessage* last_result = NULL;
63 static int process_request( const char* request );
64 static void parse_args( const char* request, osrfStringArray* cmd_array );
66 /* handles router requests */
67 static int handle_router( const osrfStringArray* cmd_array );
69 /* utility method for print time data */
70 static int handle_time( const osrfStringArray* cmd_array );
72 /* handles app level requests */
73 static int handle_request( const osrfStringArray* cmd_array, int relay );
74 static int handle_set( const osrfStringArray* cmd_array );
75 static int handle_print( const osrfStringArray* cmd_array );
76 static int send_request( const char* server,
77 const char* method, growing_buffer* buffer, int relay );
78 static int parse_error( const char* request );
79 static int router_query_servers( const char* server );
80 static int print_help( void );
82 //static int srfsh_client_connect();
83 //static char* tabs(int count);
84 //static void sig_child_handler( int s );
85 //static void sig_int_handler( int s );
87 static char* get_request( void );
88 static int load_history( void );
89 static int handle_math( const osrfStringArray* cmd_array );
90 static int do_math( int count, int style );
91 static int handle_introspect( const osrfStringArray* cmd_array );
92 static int handle_login( const osrfStringArray* cmd_array );
93 static int handle_open( const osrfStringArray* cmd_array );
94 static int handle_close( const osrfStringArray* cmd_array );
95 static void close_all_sessions( void );
97 static int recv_timeout = 120;
98 static int is_from_script = 0;
99 static int no_bang = 0;
101 static osrfHash* server_hash = NULL;
103 int main( int argc, char* argv[] ) {
105 /* --------------------------------------------- */
106 /* see if they have a .srfsh.xml in their home directory */
107 char* home = getenv("HOME");
109 int l = strlen(home) + 36;
111 snprintf(fbuf, sizeof(fbuf), "%s/.srfsh.xml", home);
113 if(!access(fbuf, R_OK)) {
114 if( ! osrf_system_bootstrap_client(fbuf, "srfsh") ) {
115 fprintf(stderr,"Unable to bootstrap client for requests\n");
116 osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for requests");
121 fprintf(stderr,"No Config file found at %s\n", fbuf );
128 for (i = 1; i < argc; i++) {
130 if( !strcmp( argv[i], "--safe" ) ) {
135 /* for now.. the first unrecognized arg is used as a script file for processing */
136 if (is_from_script) continue;
138 if( (f = open(argv[i], O_RDONLY)) == -1 ) {
139 osrfLogError( OSRF_LOG_MARK, "Unable to open file %s for reading, exiting...", argv[i]);
143 if(dup2(f, STDIN_FILENO) == -1) {
144 osrfLogError( OSRF_LOG_MARK, "Unable to duplicate STDIN, exiting...");
153 // if stdin is not a tty, assume that we're running
154 // a script and don't want to record history
155 if (!isatty(fileno(stdin))) is_from_script = 1;
157 /* --------------------------------------------- */
158 if (!is_from_script) load_history();
160 client = osrfSystemGetTransportClient();
161 osrfAppSessionSetIngress("srfsh");
163 // Disable special treatment for tabs by readline
164 // (by default they invoke command completion, which
165 // is not useful for srfsh)
166 rl_bind_key( '\t', rl_insert );
168 /* main process loop */
169 int newline_needed = 1; /* used as boolean */
171 while( (request = get_request()) ) {
173 // Find first non-whitespace character
175 char * cmd = request;
176 while( isspace( (unsigned char) *cmd ) )
179 // Remove trailing whitespace. We know at this point that
180 // there is at least one non-whitespace character somewhere,
181 // or we would have already skipped this line. Hence we
182 // needn't check to make sure that we don't back up past
186 // The curly braces limit the scope of the end variable
188 char * end = cmd + strlen(cmd) - 1;
189 while( isspace( (unsigned char) *end ) )
194 if( !strcasecmp(cmd, "exit") || !strcasecmp(cmd, "quit"))
200 process_request( cmd );
201 if( !is_from_script && request && *cmd ) {
202 add_history(request);
211 if( newline_needed ) {
213 // We left the readline loop after seeing an EOF, not after
214 // seeing "quit" or "exit". So we issue a newline in order
215 // to avoid leaving a dangling prompt.
220 if(history_file != NULL )
221 write_history(history_file);
227 if( osrfHashGetCount( server_hash ) > 0 )
228 close_all_sessions();
229 osrfHashFree( server_hash );
232 osrf_system_shutdown();
236 // Get a logical line from one or more calls to readline(),
237 // skipping blank lines and comments. Stitch continuation
238 // lines together as needed. Caller is responsible for
239 // freeing the string returned.
240 // If EOF appears before a logical line is completed,
242 static char* get_request( void ) {
246 // Get the first physical line of the logical line
248 line = readline( prompt );
250 return NULL; // end of file
252 // Skip leading white space
253 for( p = line; isspace( *p ); ++p )
256 if( '\\' == *p && '\0' == p[1] ) {
257 // Just a trailing backslash; skip to next line
260 } else if( '\0' == p[0] || '#' == *p ) {
262 continue; // blank line or comment; skip it
264 break; // Not blank, not comment; take it
267 char* end = line + strlen( line ) - 1;
269 return line; // No continuation line; we're done
271 // Remove the trailing backslash and collect
272 // the continuation line(s) into a growing_buffer
275 growing_buffer* logical_line = buffer_init( 256 );
276 buffer_add( logical_line, p );
279 // Append any continuation lines
280 int finished = 0; // boolean
282 line = readline( "> " );
285 // Check for another continuation
286 end = line + strlen( line ) - 1;
292 buffer_add( logical_line, line );
295 fprintf( stderr, "Expected continuation line; found end of file\n" );
296 buffer_free( logical_line );
301 return buffer_release( logical_line );
304 static int load_history( void ) {
306 char* home = getenv("HOME");
307 int l = strlen(home) + 24;
309 snprintf(fbuf, sizeof(fbuf), "%s/.srfsh_history", home);
310 history_file = strdup(fbuf);
312 if(!access(history_file, W_OK | R_OK )) {
313 history_length = 5000;
314 read_history(history_file);
320 static int parse_error( const char* request ) {
324 fprintf( stderr, "???: %s\n", request );
330 static int process_request( const char* request ) {
332 if( request == NULL )
336 osrfStringArray* cmd_array = osrfNewStringArray( 32 );
338 parse_args( request, cmd_array );
339 int wordcount = cmd_array->size;
340 if( 0 == wordcount ) {
341 printf( "No words found in command\n" );
342 osrfStringArrayFree( cmd_array );
346 /* pass off to the top level command */
347 const char* command = osrfStringArrayGetString( cmd_array, 0 );
348 if( !strcmp( command, "router" ) )
349 ret_val = handle_router( cmd_array );
351 else if( !strcmp( command, "time" ) )
352 ret_val = handle_time( cmd_array );
354 else if ( !strcmp( command, "request" ) )
355 ret_val = handle_request( cmd_array, 0 );
357 else if ( !strcmp( command, "relay" ) )
358 ret_val = handle_request( cmd_array, 1 );
360 else if ( !strcmp( command, "help" ) )
361 ret_val = print_help();
363 else if ( !strcmp( command, "set" ) )
364 ret_val = handle_set( cmd_array );
366 else if ( !strcmp( command, "print" ) )
367 ret_val = handle_print( cmd_array );
369 else if ( !strcmp( command, "math_bench" ) )
370 ret_val = handle_math( cmd_array );
372 else if ( !strcmp( command, "introspect" ) )
373 ret_val = handle_introspect( cmd_array );
375 else if ( !strcmp( command, "login" ) )
376 ret_val = handle_login( cmd_array );
378 else if ( !strcmp( command, "open" ) )
379 ret_val = handle_open( cmd_array );
381 else if ( !strcmp( command, "close" ) )
382 ret_val = handle_close( cmd_array );
384 else if ( request[0] == '!') {
386 (void) (system( request + 1 )+1);
391 osrfStringArrayFree( cmd_array );
394 return parse_error( request );
400 static int handle_introspect( const osrfStringArray* cmd_array ) {
402 const char* service = osrfStringArrayGetString( cmd_array, 1 );
406 fprintf(stderr, "--> %s\n", service );
408 // Build a command in a suitably-sized
409 // buffer and then parse it
412 const char* method = osrfStringArrayGetString( cmd_array, 2 );
414 static const char text[] = "request %s opensrf.system.method %s";
415 len = sizeof( text ) + strlen( service ) + strlen( method );
417 snprintf( buf, sizeof(buf), text, service, method );
418 return process_request( buf );
421 static const char text[] = "request %s opensrf.system.method.all";
422 len = sizeof( text ) + strlen( service );
424 snprintf( buf, sizeof(buf), text, service );
425 return process_request( buf );
431 static int handle_login( const osrfStringArray* cmd_array ) {
433 const char* username = osrfStringArrayGetString( cmd_array, 1 );
434 const char* password = osrfStringArrayGetString( cmd_array, 2 );
436 if( username && password ) {
438 const char* type = osrfStringArrayGetString( cmd_array, 3 );
439 const char* orgloc = osrfStringArrayGetString( cmd_array, 4 );
440 const char* workstation = osrfStringArrayGetString( cmd_array, 5 );
441 int orgloci = (orgloc) ? atoi(orgloc) : 0;
442 if(!type) type = "opac";
444 char login_text[] = "request open-ils.auth open-ils.auth.authenticate.init \"%s\"";
445 size_t len = sizeof( login_text ) + strlen(username) + 1;
448 snprintf( buf, sizeof(buf), login_text, username );
449 process_request(buf);
452 if(last_result && last_result->_result_content) {
453 jsonObject* r = last_result->_result_content;
454 hash = jsonObjectGetString(r);
457 char* pass_buf = md5sum(password);
459 size_t both_len = strlen( hash ) + strlen( pass_buf ) + 1;
460 char both_buf[both_len];
461 snprintf(both_buf, sizeof(both_buf), "%s%s", hash, pass_buf);
463 char* mess_buf = md5sum(both_buf);
465 growing_buffer* argbuf = buffer_init(64);
467 "request open-ils.auth open-ils.auth.authenticate.complete "
468 "{ \"username\" : \"%s\", \"password\" : \"%s\"", username, mess_buf );
470 if(type) buffer_fadd( argbuf, ", \"type\" : \"%s\"", type );
471 if(orgloci) buffer_fadd( argbuf, ", \"org\" : %d", orgloci );
472 if(workstation) buffer_fadd( argbuf, ", \"workstation\" : \"%s\"", workstation);
473 buffer_add_char( argbuf, '}' );
478 process_request( argbuf->buf );
481 if( login_session != NULL )
482 free( login_session );
484 const jsonObject* x = last_result->_result_content;
487 const char* authtoken = jsonObjectGetString(
488 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtoken"));
489 authtime = jsonObjectGetNumber(
490 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtime"));
493 login_session = strdup(authtoken);
495 login_session = NULL;
497 else login_session = NULL;
499 printf("Login Session: %s. Session timeout: %f\n",
500 (login_session ? login_session : "(none)"), authtime );
510 @brief Open connections to one or more specified services.
511 @param cmd_array Pointer to a list of command line chunks.
512 @return 1 in all cases.
514 The first chunk of the command line is the "open" command. Subsequent chunks, if any,
517 Try to open all specified servers. If no servers are specified, report what servers are
520 static int handle_open( const osrfStringArray* cmd_array ) {
521 if( NULL == osrfStringArrayGetString( cmd_array, 1 ) ) {
522 if( ! server_hash || osrfHashGetCount( server_hash ) == 0 ) {
523 printf( "No services are currently open\n" );
527 printf( "Service(s) currently open:\n" );
529 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
530 while( osrfHashIteratorNext( itr ) ) {
531 printf( "\t%s\n", osrfHashIteratorKey( itr ) );
533 osrfHashIteratorFree( itr );
538 server_hash = osrfNewHash( 6 );
541 for( i = 1; ; ++i ) { // for each requested service
542 const char* server = osrfStringArrayGetString( cmd_array, i );
546 if( osrfHashGet( server_hash, server ) ) {
547 printf( "Service %s is already open\n", server );
551 // Try to open a session with the current specified server
552 osrfAppSession* session = osrfAppSessionClientInit(server);
554 if(!osrfAppSessionConnect(session)) {
555 fprintf(stderr, "Unable to open service %s\n", server);
556 osrfLogWarning( OSRF_LOG_MARK, "Unable to open remote service %s\n", server );
557 osrfAppSessionFree( session );
559 osrfHashSet( server_hash, session, server );
560 printf( "Service %s opened\n", server );
568 @brief Close connections to one or more specified services.
569 @param cmd_array Pointer to a list of command line chunks.
570 @return 1 if any services were closed, or 0 if there were none to close.
572 The first chunk of the command line is the "close" command. Subsequent chunks, if any,
575 static int handle_close( const osrfStringArray* cmd_array ) {
576 if( cmd_array->size < 2 ) {
577 fprintf( stderr, "No service specified for close\n" );
582 for( i = 1; ; ++i ) {
583 const char* server = osrfStringArrayGetString( cmd_array, i );
587 osrfAppSession* session = osrfHashRemove( server_hash, server );
589 printf( "Service \"%s\" is not open\n", server );
593 osrf_app_session_disconnect( session );
594 osrfAppSessionFree( session );
595 printf( "Service \"%s\" closed\n", server );
602 @brief Close all currently open connections to services.
604 static void close_all_sessions( void ) {
606 osrfAppSession* session;
607 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
609 while(( session = osrfHashIteratorNext( itr ) )) {
610 osrf_app_session_disconnect( session );
611 osrfAppSessionFree( session );
614 osrfHashIteratorFree( itr );
617 static int handle_set( const osrfStringArray* cmd_array ) {
619 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
622 const char* val = osrfStringArrayGetString( cmd_array, 2 );
625 if(!strcmp(variable,"pretty_print")) {
626 if(!strcmp(val,"true")) {
628 printf("pretty_print = true\n");
630 } else if(!strcmp(val,"false")) {
632 printf("pretty_print = false\n");
637 if(!strcmp(variable,"raw_print")) {
638 if(!strcmp(val,"true")) {
640 printf("raw_print = true\n");
642 } else if(!strcmp(val,"false")) {
644 printf("raw_print = false\n");
656 static int handle_print( const osrfStringArray* cmd_array ) {
658 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
661 if(!strcmp(variable,"pretty_print")) {
663 printf("pretty_print = true\n");
666 printf("pretty_print = false\n");
671 if(!strcmp(variable,"raw_print")) {
673 printf("raw_print = true\n");
676 printf("raw_print = false\n");
681 if(!strcmp(variable,"login")) {
682 printf("login session = %s\n",
683 login_session ? login_session : "(none)" );
691 static int handle_router( const osrfStringArray* cmd_array ) {
698 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
699 const char* word_2 = osrfStringArrayGetString( cmd_array, 2 );
701 if( !strcmp( word_1,"query") ) {
703 if( word_2 && !strcmp( word_2, "servers" ) ) {
704 for( i=3; i < COMMAND_BUFSIZE - 3; i++ ) {
705 const char* word = osrfStringArrayGetString( cmd_array, i );
707 router_query_servers( word );
722 static int handle_request( const osrfStringArray* cmd_array, int relay ) {
727 const char* server = osrfStringArrayGetString( cmd_array, 1 );
728 const char* method = osrfStringArrayGetString( cmd_array, 2 );
732 growing_buffer* buffer = NULL;
734 int first = 1; // boolean
735 buffer = buffer_init( 128 );
736 buffer_add_char( buffer, '[' );
738 const char* word = osrfStringArrayGetString( cmd_array, i );
745 buffer_add( buffer, ", " );
747 buffer_add( buffer, word );
749 /* remove trailing semicolon if user accidentally entered it */
750 if( word[ strlen( word ) - 1 ] == ';' )
751 buffer_chomp( buffer );
753 buffer_add_char( buffer, ']' );
756 int rc = send_request( server, method, buffer, relay );
757 buffer_free( buffer );
764 int send_request( const char* server,
765 const char* method, growing_buffer* buffer, int relay ) {
766 if( server == NULL || method == NULL )
769 jsonObject* params = NULL;
771 if( buffer != NULL && buffer->n_used > 0 ) {
772 // Temporarily redirect parsing error messages to stderr
774 params = jsonParse( OSRF_BUFFER_C_STR( buffer ) );
775 osrfRestoreLogType();
778 if(!last_result || ! last_result->_result_content) {
779 printf("We're not going to call 'relay' with no result params\n");
783 params = jsonNewObject(NULL);
784 jsonObjectPush(params, last_result->_result_content );
788 if(buffer->n_used > 0 && params == NULL) {
789 fprintf(stderr, "JSON error detected, not executing\n");
790 jsonObjectFree(params);
794 int session_is_temporary; // boolean
795 osrfAppSession* session = osrfHashGet( server_hash, server );
797 session_is_temporary = 0; // use an existing session
799 session = osrfAppSessionClientInit(server); // open a session
800 session_is_temporary = 1; // just for this request
803 if (tz) osrf_app_session_set_tz(session,tz);
805 double start = get_timestamp_millis();
807 int req_id = osrfAppSessionSendRequest( session, params, method, 1 );
809 fprintf(stderr, "Unable to communicate with service %s\n", server);
810 osrfLogWarning( OSRF_LOG_MARK,
811 "Unable to communicate with remote service %s\n", server );
812 osrfAppSessionFree( session );
813 jsonObjectFree(params);
816 jsonObjectFree(params);
818 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
821 printf("\nReceived no data from server\n");
823 signal(SIGPIPE, SIG_IGN);
826 if(!is_from_script) less = popen( "less -EX", "w");
829 if( less == NULL ) { less = stdout; }
831 growing_buffer* resp_buffer = buffer_init(4096);
837 if(omsg->_result_content) {
839 osrfMessageFree(last_result);
845 char* j = jsonObjectToJSON(omsg->_result_content);
847 content = jsonFormatString(j);
850 content = strdup( "(null)" );
852 content = jsonObjectToJSON(omsg->_result_content);
854 content = strdup( "(null)" );
857 printf( "\nReceived Data: %s\n", content );
863 snprintf( code, sizeof(code), "%d", omsg->status_code );
864 buffer_add( resp_buffer, code );
866 printf( "\nReceived Exception:\nName: %s\nStatus: %s\nStatus: %s\n",
867 omsg->status_name, omsg->status_text, code );
870 osrfMessageFree(omsg);
875 if(omsg->_result_content) {
877 osrfMessageFree(last_result);
882 if( pretty_print && omsg->_result_content ) {
883 char* j = jsonObjectToJSON(omsg->_result_content);
885 content = jsonFormatString(j);
888 content = strdup( "(null)" );
890 content = jsonObjectToJSON(omsg->_result_content);
892 content = strdup( "(null)" );
895 buffer_add( resp_buffer, "\nReceived Data: " );
896 buffer_add( resp_buffer, content );
897 buffer_add_char( resp_buffer, '\n' );
902 buffer_add( resp_buffer, "\nReceived Exception:\nName: " );
903 buffer_add( resp_buffer, omsg->status_name );
904 buffer_add( resp_buffer, "\nStatus: " );
905 buffer_add( resp_buffer, omsg->status_text );
906 buffer_add( resp_buffer, "\nStatus: " );
908 snprintf( code, sizeof(code), "%d", omsg->status_code );
909 buffer_add( resp_buffer, code );
910 osrfMessageFree(omsg);
914 omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
918 double end = get_timestamp_millis();
920 fputs( resp_buffer->buf, less );
921 buffer_free( resp_buffer );
922 fputs("\n------------------------------------\n", less);
923 if( osrf_app_session_request_complete( session, req_id ))
924 fputs("Request Completed Successfully\n", less);
926 fprintf(less, "Request Time in seconds: %.6f\n", end - start );
927 fputs("------------------------------------\n", less);
929 if(!is_from_script) pclose(less);
931 osrf_app_session_request_finish( session, req_id );
933 if( session_is_temporary )
934 osrfAppSessionFree( session );
941 static int handle_time( const osrfStringArray* cmd_array ) {
943 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
945 printf("%f\n", get_timestamp_millis());
947 time_t epoch = (time_t) atoi( word_1 );
948 printf("%s", ctime(&epoch));
954 static int router_query_servers( const char* router_server ) {
956 if( ! router_server || strlen(router_server) == 0 )
959 const static char router_text[] = "router@%s/router";
960 size_t len = sizeof( router_text ) + strlen( router_server ) + 1;
962 snprintf(rbuf, sizeof(rbuf), router_text, router_server );
964 transport_message* send =
965 message_init( "servers", NULL, NULL, rbuf, NULL );
966 message_set_router_info( send, NULL, NULL, NULL, "query", 0 );
968 client_send_message( client, send );
969 message_free( send );
971 transport_message* recv = client_recv( client, -1 );
973 fprintf(stderr, "NULL message received from router\n");
978 "---------------------------------------------------------------------------------\n"
979 "Received from 'server' query on %s\n"
980 "---------------------------------------------------------------------------------\n"
981 "original reg time | latest reg time | last used time | class | server\n"
982 "---------------------------------------------------------------------------------\n"
984 "---------------------------------------------------------------------------------\n"
985 , router_server, recv->body );
987 message_free( recv );
992 static int print_help( void ) {
995 "---------------------------------------------------------------------------------\n"
996 "General commands:\n"
997 "---------------------------------------------------------------------------------\n"
998 "help - Display this message\n",
1000 if (!no_bang) fputs(
1001 "!<command> [args] - Forks and runs the given command in the shell\n",
1005 "time - Prints the current time\n"
1006 "time <timestamp> - Formats seconds since epoch into readable format\n"
1008 "set <variable> <value> - Set a srfsh variable (e.g. set pretty_print true )\n"
1009 "print <variable> - Displays the value of a srfsh variable\n"
1011 "---------------------------------------------------------------------------------\n"
1013 "---------------------------------------------------------------------------------\n"
1014 "pretty_print - Display nicely formatted JSON results\n"
1015 " - Accepted values: true, false\n"
1016 " - Default value: true\n"
1018 "raw_print - Pass JSON results through 'less' paging command\n"
1019 " - Accepted values: true, false\n"
1020 " - Default value: false\n"
1022 "---------------------------------------------------------------------------------\n"
1023 "Commands for OpenSRF services and methods:\n"
1024 "---------------------------------------------------------------------------------\n"
1025 "introspect <service> [\"method-name\"]\n"
1026 " - Prints service API, limited to the methods that match the optional\n"
1027 " right-truncated method-name parameter\n"
1029 "request <service> <method> [ <JSON formatted string of params> ]\n"
1030 " - Anything passed in will be wrapped in a JSON array,\n"
1031 " so add commas if there is more than one param\n"
1033 "router query servers <server1 [, server2, ...]>\n"
1034 " - Returns stats on connected services\n"
1036 "relay <service> <method>\n"
1037 " - Performs the requested query using the last received result as the param\n"
1039 "math_bench <num_batches> [0|1|2]\n"
1040 " - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
1041 " 2 means reconnect after every request\n"
1043 "---------------------------------------------------------------------------------\n"
1044 " Commands for Evergreen\n"
1045 "---------------------------------------------------------------------------------\n"
1046 "login <username> <password> [type] [org_unit] [workstation]\n"
1047 " - Logs into the 'server' and displays the session id\n"
1048 " - To view the session id later, enter: print login\n"
1049 "---------------------------------------------------------------------------------\n"
1051 "Note: long output is piped through 'less' unless the 'raw_print' variable\n"
1052 "is true. To search in 'less', type: /<search>\n"
1053 "---------------------------------------------------------------------------------\n"
1062 static char* tabs(int count) {
1063 growing_buffer* buf = buffer_init(24);
1065 for(i=0;i!=count;i++)
1066 buffer_add(buf, " ");
1068 char* final = buffer_data( buf );
1075 @brief Execute the "math_bench" command.
1076 @param cmd_array A list of command arguments.
1077 @return 1 if successful, 0 if not.
1079 The first command argument is required. It is the number of iterations requested. If
1080 it is less than 1, it is coerced to 1.
1082 The second command argument is optional, with allowed values of 0 (the default), 1, or 2.
1083 It controls when and whether we call osrf_app_session_disconnect(). If this argument is
1084 out of range, it is coerced to a value of 0 or 2.
1086 static int handle_math( const osrfStringArray* cmd_array ) {
1087 const char* word = osrfStringArrayGetString( cmd_array, 1 );
1089 int count = atoi( word );
1094 const char* style_arg = osrfStringArrayGetString( cmd_array, 2 );
1096 style = atoi( style_arg );
1099 else if( style < 0 )
1103 return do_math( count, style );
1109 static int do_math( int count, int style ) {
1111 osrfAppSession* session = osrfAppSessionClientInit( "opensrf.math" );
1112 osrfAppSessionConnect(session);
1114 jsonObject* params = jsonNewObjectType( JSON_ARRAY );
1115 jsonObjectPush(params,jsonNewObject("1"));
1116 jsonObjectPush(params,jsonNewObject("2"));
1118 char* methods[] = { "add", "sub", "mult", "div" };
1119 char* answers[] = { "3", "-1", "2", "0.5" };
1121 // Initialize timings to zero. This shouldn't make a difference, because
1122 // we overwrite each timing anyway before reporting them.
1123 float times[ count * 4 ];
1125 for( fi = 0; fi < count; ++fi )
1129 for(k=0;k!=100;k++) {
1131 fprintf(stderr,"|");
1133 fprintf(stderr,".");
1136 fprintf(stderr,"\n\n");
1140 for(i=0; i!= count; i++) {
1143 for(j=0; j != 4; j++) {
1147 double start = get_timestamp_millis();
1148 int req_id = osrfAppSessionSendRequest( session, params, methods[j], 1 );
1149 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, 5 );
1150 double end = get_timestamp_millis();
1152 times[(4*i) + j] = end - start;
1156 if(omsg->_result_content) {
1157 char* jsn = jsonObjectToJSON(omsg->_result_content);
1158 if(!strcmp(jsn, answers[j]))
1159 fprintf(stderr, "+");
1161 fprintf(stderr, "\n![%s] - should be %s\n", jsn, answers[j] );
1166 osrfMessageFree(omsg);
1168 } else { fprintf( stderr, "\nempty message for tt: %d\n", req_id ); }
1170 osrf_app_session_request_finish( session, req_id );
1173 osrf_app_session_disconnect( session );
1176 fprintf(stderr,"\n");
1180 osrf_app_session_disconnect( session );
1183 osrfAppSessionFree( session );
1184 jsonObjectFree(params);
1188 for(c=0; c!= count*4; c++)
1191 float avg = total / (count*4);
1192 fprintf(stderr, "\n Average round trip time: %f\n", avg );
1198 @name Command line parser
1200 This group of functions parses the command line into a series of chunks, and stores
1201 the chunks in an osrfStringArray.
1203 A chunk may consist of a JSON string, complete with square brackets, curly braces, and
1204 embedded white space. It wouldn't work simply to break up the line into tokens
1205 separated by white space. Sometimes white space separates chunks, and sometimes it
1206 occurs within a chunk.
1208 When it sees a left square bracket or curly brace, the parser goes into JSON mode,
1209 collecting characters up to the corresponding right square bracket or curly brace.
1210 It also eliminates most kinds of unnecessary white space.
1212 The JSON parsing is rudimentary. It does not validate the syntax -- it merely looks
1213 for the end of the JSON string. Eventually the JSON string will be passed to a real
1214 JSON parser, which will detect and report syntax errors.
1216 When not in JSON mode, the parser collects tokens separated by white space. It also
1217 collects character strings in quotation marks, possibly including embedded white space.
1218 Within a quoted string, an embedded quotation mark does not terminate the string if it
1219 is escaped by a preceding backslash.
1223 @brief Collect a string literal enclosed by quotation marks.
1224 @param parser Pointer to an ArgParser
1226 A quotation mark serves as a terminator unless it is escaped by a preceding backslash.
1227 In the latter case, we collect both the backslash and the escaped quotation mark.
1229 static void get_string_literal( ArgParser* parser ) {
1230 // Collect character until the first unescaped quotation mark, or EOL
1232 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1234 // Don't stop at a quotation mark if it's escaped
1235 if( '\\' == *parser->itr && '\"' == *( parser->itr + 1 ) ) {
1236 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1241 } while( *parser->itr && *parser->itr != '\"' );
1243 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1248 @brief Collect a JSON array (enclosed by square brackets).
1249 @param parser Pointer to an ArgParser.
1251 Collect characters until you find the closing square bracket. Collect any intervening
1252 JSON arrays, JSON objects, or string literals recursively.
1254 static void get_json_array( ArgParser* parser ) {
1256 OSRF_BUFFER_ADD_CHAR( parser->buf, '[' );
1259 // Collect characters through the closing square bracket
1260 while( *parser->itr != ']' ) {
1262 if( '\"' == *parser->itr ) {
1263 get_string_literal( parser );
1264 } else if( '[' == *parser->itr ) {
1265 get_json_array( parser );
1266 } else if( '{' == *parser->itr ) {
1267 get_json_object( parser );
1268 } else if( isspace( (unsigned char) *parser->itr ) ) {
1269 ++parser->itr; // Ignore white space
1270 } else if ( '\0' == *parser->itr ) {
1271 return; // Ignore failure to close the object
1274 // make sure that bare words don't run together
1275 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1279 OSRF_BUFFER_ADD_CHAR( parser->buf, ']' );
1284 @brief Collect a JSON object (enclosed by curly braces).
1285 @param parser Pointer to an ArgParser.
1287 Collect characters until you find the closing curly brace. Collect any intervening
1288 JSON arrays, JSON objects, or string literals recursively.
1290 static void get_json_object( ArgParser* parser ) {
1292 OSRF_BUFFER_ADD_CHAR( parser->buf, '{' );
1295 // Collect characters through the closing curly brace
1296 while( *parser->itr != '}' ) {
1298 if( '\"' == *parser->itr ) {
1299 get_string_literal( parser );
1300 } else if( '[' == *parser->itr ) {
1301 get_json_array( parser );
1302 } else if( '{' == *parser->itr ) {
1303 get_json_object( parser );
1304 } else if( isspace( (unsigned char) *parser->itr ) ) {
1305 ++parser->itr; // Ignore white space
1306 } else if ( '\0' == *parser->itr ) {
1307 return; // Ignore failure to close the object
1310 // make sure that bare words don't run together
1311 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1315 OSRF_BUFFER_ADD_CHAR( parser->buf, '}' );
1320 @brief Collect a token terminated by white space or a ']' or '}' character.
1321 @param parser Pointer to an ArgParser
1323 For valid JSON, the chunk collected here would be either a number or one of the
1324 JSON key words "null", "true", or "false". However at this stage we're not finicky.
1325 We just collect whatever we see until we find a terminator.
1327 static void get_misc( ArgParser* parser ) {
1328 // Collect characters until we see one that doesn't belong
1330 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1332 char c = *parser->itr;
1333 if( '\0' == c || isspace( (unsigned char) c )
1334 || '{' == c || '}' == c || '[' == c || ']' == c || '\"' == c ) {
1341 @brief Parse the command line.
1342 @param request Pointer to the command line
1343 @param cmd_array Pointer to an osrfStringArray to hold the output of the parser.
1345 The parser operates by recursive descent. We build each chunk of command line in a
1346 growing_buffer belonging to an ArgParser, and then load the chunk into a slot in an
1347 osrfStringArray supplied by the calling code.
1349 static void parse_args( const char* request, osrfStringArray* cmd_array )
1353 // Initialize the ArgParser
1354 parser.itr = request;
1355 parser.buf = buffer_init( 128 );
1357 int done = 0; // boolean
1359 OSRF_BUFFER_RESET( parser.buf );
1361 // skip any white space or commas
1363 && ( isspace( (unsigned char) *parser.itr ) || ',' == *parser.itr ) )
1366 if( '\0' == *parser.itr )
1368 else if( '{' == *parser.itr ) {
1369 // Load a JSON object
1370 get_json_object( &parser );
1371 } else if( '[' == *parser.itr ) {
1372 // Load a JSON array
1373 get_json_array( &parser );
1374 } else if( '\"' == *parser.itr ) {
1375 // Load a string literal
1376 get_string_literal( &parser );
1378 // Anything else is delimited by white space
1380 OSRF_BUFFER_ADD_CHAR( parser.buf, *parser.itr );
1382 } while( *parser.itr && ! isspace( (unsigned char) *parser.itr ) );
1385 // Remove a trailing comma, if present
1386 char lastc = OSRF_BUFFER_C_STR( parser.buf )[
1387 strlen( OSRF_BUFFER_C_STR( parser.buf ) ) - 1 ];
1389 buffer_chomp( parser.buf );
1391 // Add the chunk to the osrfStringArray
1392 const char* s = OSRF_BUFFER_C_STR( parser.buf );
1394 osrfStringArrayAdd( cmd_array, s );
1398 buffer_free( parser.buf );