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
42 static const char* prompt = "srfsh# ";
44 static char* history_file = NULL;
46 //static int child_dead = 0;
48 static char* login_session = NULL;
50 /* true if we're pretty printing json results */
51 static int pretty_print = 1;
52 /* true if we're bypassing 'less' */
53 static int raw_print = 0;
55 /* our jabber connection */
56 static transport_client* client = NULL;
58 /* the last result we received */
59 static osrfMessage* last_result = NULL;
62 static int process_request( const char* request );
63 static void parse_args( const char* request, osrfStringArray* cmd_array );
65 /* handles router requests */
66 static int handle_router( const osrfStringArray* cmd_array );
68 /* utility method for print time data */
69 static int handle_time( const osrfStringArray* cmd_array );
71 /* handles app level requests */
72 static int handle_request( const osrfStringArray* cmd_array, int relay );
73 static int handle_set( const osrfStringArray* cmd_array );
74 static int handle_print( const osrfStringArray* cmd_array );
75 static int send_request( const char* server,
76 const char* method, growing_buffer* buffer, int relay );
77 static int parse_error( const char* request );
78 static int router_query_servers( const char* server );
79 static int print_help( void );
81 //static int srfsh_client_connect();
82 //static char* tabs(int count);
83 //static void sig_child_handler( int s );
84 //static void sig_int_handler( int s );
86 static char* get_request( void );
87 static int load_history( void );
88 static int handle_math( const osrfStringArray* cmd_array );
89 static int do_math( int count, int style );
90 static int handle_introspect( const osrfStringArray* cmd_array );
91 static int handle_login( const osrfStringArray* cmd_array );
92 static int handle_open( const osrfStringArray* cmd_array );
93 static int handle_close( const osrfStringArray* cmd_array );
94 static void close_all_sessions( void );
96 static int recv_timeout = 120;
97 static int is_from_script = 0;
98 static int no_bang = 0;
100 static osrfHash* server_hash = NULL;
102 int main( int argc, char* argv[] ) {
104 /* --------------------------------------------- */
105 /* see if they have a .srfsh.xml in their home directory */
106 char* home = getenv("HOME");
107 int l = strlen(home) + 36;
109 snprintf(fbuf, sizeof(fbuf), "%s/.srfsh.xml", home);
111 if(!access(fbuf, R_OK)) {
112 if( ! osrf_system_bootstrap_client(fbuf, "srfsh") ) {
113 fprintf(stderr,"Unable to bootstrap client for requests\n");
114 osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for requests");
119 fprintf(stderr,"No Config file found at %s\n", fbuf );
126 for (i = 1; i < argc; i++) {
128 if( !strcmp( argv[i], "--safe" ) ) {
133 /* for now.. the first unrecognized arg is used as a script file for processing */
134 if (is_from_script) continue;
136 if( (f = open(argv[i], O_RDONLY)) == -1 ) {
137 osrfLogError( OSRF_LOG_MARK, "Unable to open file %s for reading, exiting...", argv[i]);
141 if(dup2(f, STDIN_FILENO) == -1) {
142 osrfLogError( OSRF_LOG_MARK, "Unable to duplicate STDIN, exiting...");
151 // if stdin is not a tty, assume that we're running
152 // a script and don't want to record history
153 if (!isatty(fileno(stdin))) is_from_script = 1;
155 /* --------------------------------------------- */
156 if (!is_from_script) load_history();
158 client = osrfSystemGetTransportClient();
159 osrfAppSessionSetIngress("srfsh");
161 // Disable special treatment for tabs by readline
162 // (by default they invoke command completion, which
163 // is not useful for srfsh)
164 rl_bind_key( '\t', rl_insert );
166 /* main process loop */
167 int newline_needed = 1; /* used as boolean */
169 while( (request = get_request()) ) {
171 // Find first non-whitespace character
173 char * cmd = request;
174 while( isspace( (unsigned char) *cmd ) )
177 // Remove trailing whitespace. We know at this point that
178 // there is at least one non-whitespace character somewhere,
179 // or we would have already skipped this line. Hence we
180 // needn't check to make sure that we don't back up past
184 // The curly braces limit the scope of the end variable
186 char * end = cmd + strlen(cmd) - 1;
187 while( isspace( (unsigned char) *end ) )
192 if( !strcasecmp(cmd, "exit") || !strcasecmp(cmd, "quit"))
198 process_request( cmd );
199 if( !is_from_script && request && *cmd ) {
200 add_history(request);
209 if( newline_needed ) {
211 // We left the readline loop after seeing an EOF, not after
212 // seeing "quit" or "exit". So we issue a newline in order
213 // to avoid leaving a dangling prompt.
218 if(history_file != NULL )
219 write_history(history_file);
225 if( osrfHashGetCount( server_hash ) > 0 )
226 close_all_sessions();
227 osrfHashFree( server_hash );
230 osrf_system_shutdown();
234 // Get a logical line from one or more calls to readline(),
235 // skipping blank lines and comments. Stitch continuation
236 // lines together as needed. Caller is responsible for
237 // freeing the string returned.
238 // If EOF appears before a logical line is completed,
240 static char* get_request( void ) {
244 // Get the first physical line of the logical line
246 line = readline( prompt );
248 return NULL; // end of file
250 // Skip leading white space
251 for( p = line; isspace( *p ); ++p )
254 if( '\\' == *p && '\0' == p[1] ) {
255 // Just a trailing backslash; skip to next line
258 } else if( '\0' == p[0] || '#' == *p ) {
260 continue; // blank line or comment; skip it
262 break; // Not blank, not comment; take it
265 char* end = line + strlen( line ) - 1;
267 return line; // No continuation line; we're done
269 // Remove the trailing backslash and collect
270 // the continuation line(s) into a growing_buffer
273 growing_buffer* logical_line = buffer_init( 256 );
274 buffer_add( logical_line, p );
277 // Append any continuation lines
278 int finished = 0; // boolean
280 line = readline( "> " );
283 // Check for another continuation
284 end = line + strlen( line ) - 1;
290 buffer_add( logical_line, line );
293 fprintf( stderr, "Expected continuation line; found end of file\n" );
294 buffer_free( logical_line );
299 return buffer_release( logical_line );
302 static int load_history( void ) {
304 char* home = getenv("HOME");
305 int l = strlen(home) + 24;
307 snprintf(fbuf, sizeof(fbuf), "%s/.srfsh_history", home);
308 history_file = strdup(fbuf);
310 if(!access(history_file, W_OK | R_OK )) {
311 history_length = 5000;
312 read_history(history_file);
318 static int parse_error( const char* request ) {
322 fprintf( stderr, "???: %s\n", request );
328 static int process_request( const char* request ) {
330 if( request == NULL )
334 osrfStringArray* cmd_array = osrfNewStringArray( 32 );
336 parse_args( request, cmd_array );
337 int wordcount = cmd_array->size;
338 if( 0 == wordcount ) {
339 printf( "No words found in command\n" );
340 osrfStringArrayFree( cmd_array );
344 /* pass off to the top level command */
345 const char* command = osrfStringArrayGetString( cmd_array, 0 );
346 if( !strcmp( command, "router" ) )
347 ret_val = handle_router( cmd_array );
349 else if( !strcmp( command, "time" ) )
350 ret_val = handle_time( cmd_array );
352 else if ( !strcmp( command, "request" ) )
353 ret_val = handle_request( cmd_array, 0 );
355 else if ( !strcmp( command, "relay" ) )
356 ret_val = handle_request( cmd_array, 1 );
358 else if ( !strcmp( command, "help" ) )
359 ret_val = print_help();
361 else if ( !strcmp( command, "set" ) )
362 ret_val = handle_set( cmd_array );
364 else if ( !strcmp( command, "print" ) )
365 ret_val = handle_print( cmd_array );
367 else if ( !strcmp( command, "math_bench" ) )
368 ret_val = handle_math( cmd_array );
370 else if ( !strcmp( command, "introspect" ) )
371 ret_val = handle_introspect( cmd_array );
373 else if ( !strcmp( command, "login" ) )
374 ret_val = handle_login( cmd_array );
376 else if ( !strcmp( command, "open" ) )
377 ret_val = handle_open( cmd_array );
379 else if ( !strcmp( command, "close" ) )
380 ret_val = handle_close( cmd_array );
382 else if ( request[0] == '!') {
384 system( request + 1 );
389 osrfStringArrayFree( cmd_array );
392 return parse_error( request );
398 static int handle_introspect( const osrfStringArray* cmd_array ) {
400 const char* service = osrfStringArrayGetString( cmd_array, 1 );
404 fprintf(stderr, "--> %s\n", service );
406 // Build a command in a suitably-sized
407 // buffer and then parse it
410 const char* method = osrfStringArrayGetString( cmd_array, 2 );
412 static const char text[] = "request %s opensrf.system.method %s";
413 len = sizeof( text ) + strlen( service ) + strlen( method );
415 snprintf( buf, sizeof(buf), text, service, method );
416 return process_request( buf );
419 static const char text[] = "request %s opensrf.system.method.all";
420 len = sizeof( text ) + strlen( service );
422 snprintf( buf, sizeof(buf), text, service );
423 return process_request( buf );
429 static int handle_login( const osrfStringArray* cmd_array ) {
431 const char* username = osrfStringArrayGetString( cmd_array, 1 );
432 const char* password = osrfStringArrayGetString( cmd_array, 2 );
434 if( username && password ) {
436 const char* type = osrfStringArrayGetString( cmd_array, 3 );
437 const char* orgloc = osrfStringArrayGetString( cmd_array, 4 );
438 const char* workstation = osrfStringArrayGetString( cmd_array, 5 );
439 int orgloci = (orgloc) ? atoi(orgloc) : 0;
440 if(!type) type = "opac";
442 char login_text[] = "request open-ils.auth open-ils.auth.authenticate.init \"%s\"";
443 size_t len = sizeof( login_text ) + strlen(username) + 1;
446 snprintf( buf, sizeof(buf), login_text, username );
447 process_request(buf);
450 if(last_result && last_result->_result_content) {
451 jsonObject* r = last_result->_result_content;
452 hash = jsonObjectGetString(r);
455 char* pass_buf = md5sum(password);
457 size_t both_len = strlen( hash ) + strlen( pass_buf ) + 1;
458 char both_buf[both_len];
459 snprintf(both_buf, sizeof(both_buf), "%s%s", hash, pass_buf);
461 char* mess_buf = md5sum(both_buf);
463 growing_buffer* argbuf = buffer_init(64);
465 "request open-ils.auth open-ils.auth.authenticate.complete "
466 "{ \"username\" : \"%s\", \"password\" : \"%s\"", username, mess_buf );
468 if(type) buffer_fadd( argbuf, ", \"type\" : \"%s\"", type );
469 if(orgloci) buffer_fadd( argbuf, ", \"org\" : %d", orgloci );
470 if(workstation) buffer_fadd( argbuf, ", \"workstation\" : \"%s\"", workstation);
471 buffer_add_char( argbuf, '}' );
476 process_request( argbuf->buf );
479 if( login_session != NULL )
480 free( login_session );
482 const jsonObject* x = last_result->_result_content;
485 const char* authtoken = jsonObjectGetString(
486 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtoken"));
487 authtime = jsonObjectGetNumber(
488 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtime"));
491 login_session = strdup(authtoken);
493 login_session = NULL;
495 else login_session = NULL;
497 printf("Login Session: %s. Session timeout: %f\n",
498 (login_session ? login_session : "(none)"), authtime );
508 @brief Open connections to one or more specified services.
509 @param cmd_array Pointer to a list of command line chunks.
510 @return 1 in all cases.
512 The first chunk of the command line is the "open" command. Subsequent chunks, if any,
515 Try to open all specified servers. If no servers are specified, report what servers are
518 static int handle_open( const osrfStringArray* cmd_array ) {
519 if( NULL == osrfStringArrayGetString( cmd_array, 1 ) ) {
520 if( ! server_hash || osrfHashGetCount( server_hash ) == 0 ) {
521 printf( "No services are currently open\n" );
525 printf( "Service(s) currently open:\n" );
527 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
528 while( osrfHashIteratorNext( itr ) ) {
529 printf( "\t%s\n", osrfHashIteratorKey( itr ) );
531 osrfHashIteratorFree( itr );
536 server_hash = osrfNewHash( 6 );
539 for( i = 1; ; ++i ) { // for each requested service
540 const char* server = osrfStringArrayGetString( cmd_array, i );
544 if( osrfHashGet( server_hash, server ) ) {
545 printf( "Service %s is already open\n", server );
549 // Try to open a session with the current specified server
550 osrfAppSession* session = osrfAppSessionClientInit(server);
552 if(!osrfAppSessionConnect(session)) {
553 fprintf(stderr, "Unable to open service %s\n", server);
554 osrfLogWarning( OSRF_LOG_MARK, "Unable to open remote service %s\n", server );
555 osrfAppSessionFree( session );
557 osrfHashSet( server_hash, session, server );
558 printf( "Service %s opened\n", server );
566 @brief Close connections to one or more specified services.
567 @param cmd_array Pointer to a list of command line chunks.
568 @return 1 if any services were closed, or 0 if there were none to close.
570 The first chunk of the command line is the "close" command. Subsequent chunks, if any,
573 static int handle_close( const osrfStringArray* cmd_array ) {
574 if( cmd_array->size < 2 ) {
575 fprintf( stderr, "No service specified for close\n" );
580 for( i = 1; ; ++i ) {
581 const char* server = osrfStringArrayGetString( cmd_array, i );
585 osrfAppSession* session = osrfHashRemove( server_hash, server );
587 printf( "Service \"%s\" is not open\n", server );
591 osrf_app_session_disconnect( session );
592 osrfAppSessionFree( session );
593 printf( "Service \"%s\" closed\n", server );
600 @brief Close all currently open connections to services.
602 static void close_all_sessions( void ) {
604 osrfAppSession* session;
605 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
607 while(( session = osrfHashIteratorNext( itr ) )) {
608 osrf_app_session_disconnect( session );
609 osrfAppSessionFree( session );
612 osrfHashIteratorFree( itr );
615 static int handle_set( const osrfStringArray* cmd_array ) {
617 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
620 const char* val = osrfStringArrayGetString( cmd_array, 2 );
623 if(!strcmp(variable,"pretty_print")) {
624 if(!strcmp(val,"true")) {
626 printf("pretty_print = true\n");
628 } else if(!strcmp(val,"false")) {
630 printf("pretty_print = false\n");
635 if(!strcmp(variable,"raw_print")) {
636 if(!strcmp(val,"true")) {
638 printf("raw_print = true\n");
640 } else if(!strcmp(val,"false")) {
642 printf("raw_print = false\n");
654 static int handle_print( const osrfStringArray* cmd_array ) {
656 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
659 if(!strcmp(variable,"pretty_print")) {
661 printf("pretty_print = true\n");
664 printf("pretty_print = false\n");
669 if(!strcmp(variable,"raw_print")) {
671 printf("raw_print = true\n");
674 printf("raw_print = false\n");
679 if(!strcmp(variable,"login")) {
680 printf("login session = %s\n",
681 login_session ? login_session : "(none)" );
689 static int handle_router( const osrfStringArray* cmd_array ) {
696 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
697 const char* word_2 = osrfStringArrayGetString( cmd_array, 2 );
699 if( !strcmp( word_1,"query") ) {
701 if( word_2 && !strcmp( word_2, "servers" ) ) {
702 for( i=3; i < COMMAND_BUFSIZE - 3; i++ ) {
703 const char* word = osrfStringArrayGetString( cmd_array, i );
705 router_query_servers( word );
720 static int handle_request( const osrfStringArray* cmd_array, int relay ) {
725 const char* server = osrfStringArrayGetString( cmd_array, 1 );
726 const char* method = osrfStringArrayGetString( cmd_array, 2 );
730 growing_buffer* buffer = NULL;
732 int first = 1; // boolean
733 buffer = buffer_init( 128 );
734 buffer_add_char( buffer, '[' );
736 const char* word = osrfStringArrayGetString( cmd_array, i );
743 buffer_add( buffer, ", " );
745 buffer_add( buffer, word );
747 /* remove trailing semicolon if user accidentally entered it */
748 if( word[ strlen( word ) - 1 ] == ';' )
749 buffer_chomp( buffer );
751 buffer_add_char( buffer, ']' );
754 int rc = send_request( server, method, buffer, relay );
755 buffer_free( buffer );
762 int send_request( const char* server,
763 const char* method, growing_buffer* buffer, int relay ) {
764 if( server == NULL || method == NULL )
767 jsonObject* params = NULL;
769 if( buffer != NULL && buffer->n_used > 0 ) {
770 // Temporarily redirect parsing error messages to stderr
772 params = jsonParse( OSRF_BUFFER_C_STR( buffer ) );
773 osrfRestoreLogType();
776 if(!last_result || ! last_result->_result_content) {
777 printf("We're not going to call 'relay' with no result params\n");
781 params = jsonNewObject(NULL);
782 jsonObjectPush(params, last_result->_result_content );
786 if(buffer->n_used > 0 && params == NULL) {
787 fprintf(stderr, "JSON error detected, not executing\n");
788 jsonObjectFree(params);
792 int session_is_temporary; // boolean
793 osrfAppSession* session = osrfHashGet( server_hash, server );
795 session_is_temporary = 0; // use an existing session
797 session = osrfAppSessionClientInit(server); // open a session
798 session_is_temporary = 1; // just for this request
801 double start = get_timestamp_millis();
803 int req_id = osrfAppSessionSendRequest( session, params, method, 1 );
805 fprintf(stderr, "Unable to communicate with service %s\n", server);
806 osrfLogWarning( OSRF_LOG_MARK,
807 "Unable to communicate with remote service %s\n", server );
808 osrfAppSessionFree( session );
809 jsonObjectFree(params);
812 jsonObjectFree(params);
814 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
817 printf("\nReceived no data from server\n");
819 signal(SIGPIPE, SIG_IGN);
822 if(!is_from_script) less = popen( "less -EX", "w");
825 if( less == NULL ) { less = stdout; }
827 growing_buffer* resp_buffer = buffer_init(4096);
833 if(omsg->_result_content) {
835 osrfMessageFree(last_result);
841 char* j = jsonObjectToJSON(omsg->_result_content);
843 content = jsonFormatString(j);
846 content = strdup( "(null)" );
848 content = jsonObjectToJSON(omsg->_result_content);
850 content = strdup( "(null)" );
853 printf( "\nReceived Data: %s\n", content );
859 snprintf( code, sizeof(code), "%d", omsg->status_code );
860 buffer_add( resp_buffer, code );
862 printf( "\nReceived Exception:\nName: %s\nStatus: %s\nStatus: %s\n",
863 omsg->status_name, omsg->status_text, code );
866 osrfMessageFree(omsg);
871 if(omsg->_result_content) {
873 osrfMessageFree(last_result);
878 if( pretty_print && omsg->_result_content ) {
879 char* j = jsonObjectToJSON(omsg->_result_content);
881 content = jsonFormatString(j);
884 content = strdup( "(null)" );
886 content = jsonObjectToJSON(omsg->_result_content);
888 content = strdup( "(null)" );
891 buffer_add( resp_buffer, "\nReceived Data: " );
892 buffer_add( resp_buffer, content );
893 buffer_add_char( resp_buffer, '\n' );
898 buffer_add( resp_buffer, "\nReceived Exception:\nName: " );
899 buffer_add( resp_buffer, omsg->status_name );
900 buffer_add( resp_buffer, "\nStatus: " );
901 buffer_add( resp_buffer, omsg->status_text );
902 buffer_add( resp_buffer, "\nStatus: " );
904 snprintf( code, sizeof(code), "%d", omsg->status_code );
905 buffer_add( resp_buffer, code );
906 osrfMessageFree(omsg);
910 omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
914 double end = get_timestamp_millis();
916 fputs( resp_buffer->buf, less );
917 buffer_free( resp_buffer );
918 fputs("\n------------------------------------\n", less);
919 if( osrf_app_session_request_complete( session, req_id ))
920 fputs("Request Completed Successfully\n", less);
922 fprintf(less, "Request Time in seconds: %.6f\n", end - start );
923 fputs("------------------------------------\n", less);
925 if(!is_from_script) pclose(less);
927 osrf_app_session_request_finish( session, req_id );
929 if( session_is_temporary )
930 osrfAppSessionFree( session );
937 static int handle_time( const osrfStringArray* cmd_array ) {
939 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
941 printf("%f\n", get_timestamp_millis());
943 time_t epoch = (time_t) atoi( word_1 );
944 printf("%s", ctime(&epoch));
950 static int router_query_servers( const char* router_server ) {
952 if( ! router_server || strlen(router_server) == 0 )
955 const static char router_text[] = "router@%s/router";
956 size_t len = sizeof( router_text ) + strlen( router_server ) + 1;
958 snprintf(rbuf, sizeof(rbuf), router_text, router_server );
960 transport_message* send =
961 message_init( "servers", NULL, NULL, rbuf, NULL );
962 message_set_router_info( send, NULL, NULL, NULL, "query", 0 );
964 client_send_message( client, send );
965 message_free( send );
967 transport_message* recv = client_recv( client, -1 );
969 fprintf(stderr, "NULL message received from router\n");
974 "---------------------------------------------------------------------------------\n"
975 "Received from 'server' query on %s\n"
976 "---------------------------------------------------------------------------------\n"
977 "original reg time | latest reg time | last used time | class | server\n"
978 "---------------------------------------------------------------------------------\n"
980 "---------------------------------------------------------------------------------\n"
981 , router_server, recv->body );
983 message_free( recv );
988 static int print_help( void ) {
991 "---------------------------------------------------------------------------------\n"
992 "General commands:\n"
993 "---------------------------------------------------------------------------------\n"
994 "help - Display this message\n",
997 "!<command> [args] - Forks and runs the given command in the shell\n",
1001 "time - Prints the current time\n"
1002 "time <timestamp> - Formats seconds since epoch into readable format\n"
1004 "set <variable> <value> - Set a srfsh variable (e.g. set pretty_print true )\n"
1005 "print <variable> - Displays the value of a srfsh variable\n"
1007 "---------------------------------------------------------------------------------\n"
1009 "---------------------------------------------------------------------------------\n"
1010 "pretty_print - Display nicely formatted JSON results\n"
1011 " - Accepted values: true, false\n"
1012 " - Default value: true\n"
1014 "raw_print - Pass JSON results through 'less' paging command\n"
1015 " - Accepted values: true, false\n"
1016 " - Default value: false\n"
1018 "---------------------------------------------------------------------------------\n"
1019 "Commands for OpenSRF services and methods:\n"
1020 "---------------------------------------------------------------------------------\n"
1021 "introspect <service> [\"method-name\"]\n"
1022 " - Prints service API, limited to the methods that match the optional\n"
1023 " right-truncated method-name parameter\n"
1025 "request <service> <method> [ <JSON formatted string of params> ]\n"
1026 " - Anything passed in will be wrapped in a JSON array,\n"
1027 " so add commas if there is more than one param\n"
1029 "router query servers <server1 [, server2, ...]>\n"
1030 " - Returns stats on connected services\n"
1032 "relay <service> <method>\n"
1033 " - Performs the requested query using the last received result as the param\n"
1035 "math_bench <num_batches> [0|1|2]\n"
1036 " - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
1037 " 2 means reconnect after every request\n"
1039 "---------------------------------------------------------------------------------\n"
1040 " Commands for Evergreen\n"
1041 "---------------------------------------------------------------------------------\n"
1042 "login <username> <password> [type] [org_unit] [workstation]\n"
1043 " - Logs into the 'server' and displays the session id\n"
1044 " - To view the session id later, enter: print login\n"
1045 "---------------------------------------------------------------------------------\n"
1047 "Note: long output is piped through 'less' unless the 'raw_print' variable\n"
1048 "is true. To search in 'less', type: /<search>\n"
1049 "---------------------------------------------------------------------------------\n"
1058 static char* tabs(int count) {
1059 growing_buffer* buf = buffer_init(24);
1061 for(i=0;i!=count;i++)
1062 buffer_add(buf, " ");
1064 char* final = buffer_data( buf );
1071 @brief Execute the "math_bench" command.
1072 @param cmd_array A list of command arguments.
1073 @return 1 if successful, 0 if not.
1075 The first command argument is required. It is the number of iterations requested. If
1076 it is less than 1, it is coerced to 1.
1078 The second command argument is optional, with allowed values of 0 (the default), 1, or 2.
1079 It controls when and whether we call osrf_app_session_disconnect(). If this argument is
1080 out of range, it is coerced to a value of 0 or 2.
1082 static int handle_math( const osrfStringArray* cmd_array ) {
1083 const char* word = osrfStringArrayGetString( cmd_array, 1 );
1085 int count = atoi( word );
1090 const char* style_arg = osrfStringArrayGetString( cmd_array, 2 );
1092 style = atoi( style_arg );
1095 else if( style < 0 )
1099 return do_math( count, style );
1105 static int do_math( int count, int style ) {
1107 osrfAppSession* session = osrfAppSessionClientInit( "opensrf.math" );
1108 osrfAppSessionConnect(session);
1110 jsonObject* params = jsonNewObjectType( JSON_ARRAY );
1111 jsonObjectPush(params,jsonNewObject("1"));
1112 jsonObjectPush(params,jsonNewObject("2"));
1114 char* methods[] = { "add", "sub", "mult", "div" };
1115 char* answers[] = { "3", "-1", "2", "0.5" };
1117 // Initialize timings to zero. This shouldn't make a difference, because
1118 // we overwrite each timing anyway before reporting them.
1119 float times[ count * 4 ];
1121 for( fi = 0; fi < count; ++fi )
1125 for(k=0;k!=100;k++) {
1127 fprintf(stderr,"|");
1129 fprintf(stderr,".");
1132 fprintf(stderr,"\n\n");
1136 for(i=0; i!= count; i++) {
1139 for(j=0; j != 4; j++) {
1143 double start = get_timestamp_millis();
1144 int req_id = osrfAppSessionSendRequest( session, params, methods[j], 1 );
1145 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, 5 );
1146 double end = get_timestamp_millis();
1148 times[(4*i) + j] = end - start;
1152 if(omsg->_result_content) {
1153 char* jsn = jsonObjectToJSON(omsg->_result_content);
1154 if(!strcmp(jsn, answers[j]))
1155 fprintf(stderr, "+");
1157 fprintf(stderr, "\n![%s] - should be %s\n", jsn, answers[j] );
1162 osrfMessageFree(omsg);
1164 } else { fprintf( stderr, "\nempty message for tt: %d\n", req_id ); }
1166 osrf_app_session_request_finish( session, req_id );
1169 osrf_app_session_disconnect( session );
1172 fprintf(stderr,"\n");
1176 osrf_app_session_disconnect( session );
1179 osrfAppSessionFree( session );
1180 jsonObjectFree(params);
1184 for(c=0; c!= count*4; c++)
1187 float avg = total / (count*4);
1188 fprintf(stderr, "\n Average round trip time: %f\n", avg );
1194 @name Command line parser
1196 This group of functions parses the command line into a series of chunks, and stores
1197 the chunks in an osrfStringArray.
1199 A chunk may consist of a JSON string, complete with square brackets, curly braces, and
1200 embedded white space. It wouldn't work simply to break up the line into tokens
1201 separated by white space. Sometimes white space separates chunks, and sometimes it
1202 occurs within a chunk.
1204 When it sees a left square bracket or curly brace, the parser goes into JSON mode,
1205 collecting characters up to the corresponding right square bracket or curly brace.
1206 It also eliminates most kinds of unnecessary white space.
1208 The JSON parsing is rudimentary. It does not validate the syntax -- it merely looks
1209 for the end of the JSON string. Eventually the JSON string will be passed to a real
1210 JSON parser, which will detect and report syntax errors.
1212 When not in JSON mode, the parser collects tokens separated by white space. It also
1213 collects character strings in quotation marks, possibly including embedded white space.
1214 Within a quoted string, an embedded quotation mark does not terminate the string if it
1215 is escaped by a preceding backslash.
1219 @brief Collect a string literal enclosed by quotation marks.
1220 @param parser Pointer to an ArgParser
1222 A quotation mark serves as a terminator unless it is escaped by a preceding backslash.
1223 In the latter case, we collect both the backslash and the escaped quotation mark.
1225 static void get_string_literal( ArgParser* parser ) {
1226 // Collect character until the first unescaped quotation mark, or EOL
1228 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1230 // Don't stop at a quotation mark if it's escaped
1231 if( '\\' == *parser->itr && '\"' == *( parser->itr + 1 ) ) {
1232 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1237 } while( *parser->itr && *parser->itr != '\"' );
1239 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1244 @brief Collect a JSON array (enclosed by square brackets).
1245 @param parser Pointer to an ArgParser.
1247 Collect characters until you find the closing square bracket. Collect any intervening
1248 JSON arrays, JSON objects, or string literals recursively.
1250 static void get_json_array( ArgParser* parser ) {
1252 OSRF_BUFFER_ADD_CHAR( parser->buf, '[' );
1255 // Collect characters through the closing square bracket
1256 while( *parser->itr != ']' ) {
1258 if( '\"' == *parser->itr ) {
1259 get_string_literal( parser );
1260 } else if( '[' == *parser->itr ) {
1261 get_json_array( parser );
1262 } else if( '{' == *parser->itr ) {
1263 get_json_object( parser );
1264 } else if( isspace( (unsigned char) *parser->itr ) ) {
1265 ++parser->itr; // Ignore white space
1266 } else if ( '\0' == *parser->itr ) {
1267 return; // Ignore failure to close the object
1270 // make sure that bare words don't run together
1271 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1275 OSRF_BUFFER_ADD_CHAR( parser->buf, ']' );
1280 @brief Collect a JSON object (enclosed by curly braces).
1281 @param parser Pointer to an ArgParser.
1283 Collect characters until you find the closing curly brace. Collect any intervening
1284 JSON arrays, JSON objects, or string literals recursively.
1286 static void get_json_object( ArgParser* parser ) {
1288 OSRF_BUFFER_ADD_CHAR( parser->buf, '{' );
1291 // Collect characters through the closing curly brace
1292 while( *parser->itr != '}' ) {
1294 if( '\"' == *parser->itr ) {
1295 get_string_literal( parser );
1296 } else if( '[' == *parser->itr ) {
1297 get_json_array( parser );
1298 } else if( '{' == *parser->itr ) {
1299 get_json_object( parser );
1300 } else if( isspace( (unsigned char) *parser->itr ) ) {
1301 ++parser->itr; // Ignore white space
1302 } else if ( '\0' == *parser->itr ) {
1303 return; // Ignore failure to close the object
1306 // make sure that bare words don't run together
1307 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1311 OSRF_BUFFER_ADD_CHAR( parser->buf, '}' );
1316 @brief Collect a token terminated by white space or a ']' or '}' character.
1317 @param parser Pointer to an ArgParser
1319 For valid JSON, the chunk collected here would be either a number or one of the
1320 JSON key words "null", "true", or "false". However at this stage we're not finicky.
1321 We just collect whatever we see until we find a terminator.
1323 static void get_misc( ArgParser* parser ) {
1324 // Collect characters until we see one that doesn't belong
1326 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1328 char c = *parser->itr;
1329 if( '\0' == c || isspace( (unsigned char) c )
1330 || '{' == c || '}' == c || '[' == c || ']' == c || '\"' == c ) {
1337 @brief Parse the command line.
1338 @param request Pointer to the command line
1339 @param cmd_array Pointer to an osrfStringArray to hold the output of the parser.
1341 The parser operates by recursive descent. We build each chunk of command line in a
1342 growing_buffer belonging to an ArgParser, and then load the chunk into a slot in an
1343 osrfStringArray supplied by the calling code.
1345 static void parse_args( const char* request, osrfStringArray* cmd_array )
1349 // Initialize the ArgParser
1350 parser.itr = request;
1351 parser.buf = buffer_init( 128 );
1353 int done = 0; // boolean
1355 OSRF_BUFFER_RESET( parser.buf );
1357 // skip any white space or commas
1359 && ( isspace( (unsigned char) *parser.itr ) || ',' == *parser.itr ) )
1362 if( '\0' == *parser.itr )
1364 else if( '{' == *parser.itr ) {
1365 // Load a JSON object
1366 get_json_object( &parser );
1367 } else if( '[' == *parser.itr ) {
1368 // Load a JSON array
1369 get_json_array( &parser );
1370 } else if( '\"' == *parser.itr ) {
1371 // Load a string literal
1372 get_string_literal( &parser );
1374 // Anything else is delimited by white space
1376 OSRF_BUFFER_ADD_CHAR( parser.buf, *parser.itr );
1378 } while( *parser.itr && ! isspace( (unsigned char) *parser.itr ) );
1381 // Remove a trailing comma, if present
1382 char lastc = OSRF_BUFFER_C_STR( parser.buf )[
1383 strlen( OSRF_BUFFER_C_STR( parser.buf ) ) - 1 ];
1385 buffer_chomp( parser.buf );
1387 // Add the chunk to the osrfStringArray
1388 const char* s = OSRF_BUFFER_C_STR( parser.buf );
1390 osrfStringArrayAdd( cmd_array, s );
1394 buffer_free( parser.buf );