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 /* --------------------------------------------- */
154 client = osrfSystemGetTransportClient();
155 osrfAppSessionSetIngress("srfsh");
157 // Disable special treatment for tabs by readline
158 // (by default they invoke command completion, which
159 // is not useful for srfsh)
160 rl_bind_key( '\t', rl_insert );
162 /* main process loop */
163 int newline_needed = 1; /* used as boolean */
165 while( (request = get_request()) ) {
167 // Find first non-whitespace character
169 char * cmd = request;
170 while( isspace( (unsigned char) *cmd ) )
173 // Remove trailing whitespace. We know at this point that
174 // there is at least one non-whitespace character somewhere,
175 // or we would have already skipped this line. Hence we
176 // needn't check to make sure that we don't back up past
180 // The curly braces limit the scope of the end variable
182 char * end = cmd + strlen(cmd) - 1;
183 while( isspace( (unsigned char) *end ) )
188 if( !strcasecmp(cmd, "exit") || !strcasecmp(cmd, "quit"))
194 process_request( cmd );
195 if( request && *cmd ) {
196 add_history(request);
205 if( newline_needed ) {
207 // We left the readline loop after seeing an EOF, not after
208 // seeing "quit" or "exit". So we issue a newline in order
209 // to avoid leaving a dangling prompt.
214 if(history_file != NULL )
215 write_history(history_file);
221 if( osrfHashGetCount( server_hash ) > 0 )
222 close_all_sessions();
223 osrfHashFree( server_hash );
226 osrf_system_shutdown();
230 // Get a logical line from one or more calls to readline(),
231 // skipping blank lines and comments. Stitch continuation
232 // lines together as needed. Caller is responsible for
233 // freeing the string returned.
234 // If EOF appears before a logical line is completed,
236 static char* get_request( void ) {
240 // Get the first physical line of the logical line
242 line = readline( prompt );
244 return NULL; // end of file
246 // Skip leading white space
247 for( p = line; isspace( *p ); ++p )
250 if( '\\' == *p && '\0' == p[1] ) {
251 // Just a trailing backslash; skip to next line
254 } else if( '\0' == p[0] || '#' == *p ) {
256 continue; // blank line or comment; skip it
258 break; // Not blank, not comment; take it
261 char* end = line + strlen( line ) - 1;
263 return line; // No continuation line; we're done
265 // Remove the trailing backslash and collect
266 // the continuation line(s) into a growing_buffer
269 growing_buffer* logical_line = buffer_init( 256 );
270 buffer_add( logical_line, p );
273 // Append any continuation lines
274 int finished = 0; // boolean
276 line = readline( "> " );
279 // Check for another continuation
280 end = line + strlen( line ) - 1;
286 buffer_add( logical_line, line );
289 fprintf( stderr, "Expected continuation line; found end of file\n" );
290 buffer_free( logical_line );
295 return buffer_release( logical_line );
298 static int load_history( void ) {
300 char* home = getenv("HOME");
301 int l = strlen(home) + 24;
303 snprintf(fbuf, sizeof(fbuf), "%s/.srfsh_history", home);
304 history_file = strdup(fbuf);
306 if(!access(history_file, W_OK | R_OK )) {
307 history_length = 5000;
308 read_history(history_file);
314 static int parse_error( const char* request ) {
318 fprintf( stderr, "???: %s\n", request );
324 static int process_request( const char* request ) {
326 if( request == NULL )
330 osrfStringArray* cmd_array = osrfNewStringArray( 32 );
332 parse_args( request, cmd_array );
333 int wordcount = cmd_array->size;
334 if( 0 == wordcount ) {
335 printf( "No words found in command\n" );
336 osrfStringArrayFree( cmd_array );
340 /* pass off to the top level command */
341 const char* command = osrfStringArrayGetString( cmd_array, 0 );
342 if( !strcmp( command, "router" ) )
343 ret_val = handle_router( cmd_array );
345 else if( !strcmp( command, "time" ) )
346 ret_val = handle_time( cmd_array );
348 else if ( !strcmp( command, "request" ) )
349 ret_val = handle_request( cmd_array, 0 );
351 else if ( !strcmp( command, "relay" ) )
352 ret_val = handle_request( cmd_array, 1 );
354 else if ( !strcmp( command, "help" ) )
355 ret_val = print_help();
357 else if ( !strcmp( command, "set" ) )
358 ret_val = handle_set( cmd_array );
360 else if ( !strcmp( command, "print" ) )
361 ret_val = handle_print( cmd_array );
363 else if ( !strcmp( command, "math_bench" ) )
364 ret_val = handle_math( cmd_array );
366 else if ( !strcmp( command, "introspect" ) )
367 ret_val = handle_introspect( cmd_array );
369 else if ( !strcmp( command, "login" ) )
370 ret_val = handle_login( cmd_array );
372 else if ( !strcmp( command, "open" ) )
373 ret_val = handle_open( cmd_array );
375 else if ( !strcmp( command, "close" ) )
376 ret_val = handle_close( cmd_array );
378 else if ( request[0] == '!') {
380 system( request + 1 );
385 osrfStringArrayFree( cmd_array );
388 return parse_error( request );
394 static int handle_introspect( const osrfStringArray* cmd_array ) {
396 const char* service = osrfStringArrayGetString( cmd_array, 1 );
400 fprintf(stderr, "--> %s\n", service );
402 // Build a command in a suitably-sized
403 // buffer and then parse it
406 const char* method = osrfStringArrayGetString( cmd_array, 2 );
408 static const char text[] = "request %s opensrf.system.method %s";
409 len = sizeof( text ) + strlen( service ) + strlen( method );
411 snprintf( buf, sizeof(buf), text, service, method );
412 return process_request( buf );
415 static const char text[] = "request %s opensrf.system.method.all";
416 len = sizeof( text ) + strlen( service );
418 snprintf( buf, sizeof(buf), text, service );
419 return process_request( buf );
425 static int handle_login( const osrfStringArray* cmd_array ) {
427 const char* username = osrfStringArrayGetString( cmd_array, 1 );
428 const char* password = osrfStringArrayGetString( cmd_array, 2 );
430 if( username && password ) {
432 const char* type = osrfStringArrayGetString( cmd_array, 3 );
433 const char* orgloc = osrfStringArrayGetString( cmd_array, 4 );
434 const char* workstation = osrfStringArrayGetString( cmd_array, 5 );
435 int orgloci = (orgloc) ? atoi(orgloc) : 0;
436 if(!type) type = "opac";
438 char login_text[] = "request open-ils.auth open-ils.auth.authenticate.init \"%s\"";
439 size_t len = sizeof( login_text ) + strlen(username) + 1;
442 snprintf( buf, sizeof(buf), login_text, username );
443 process_request(buf);
446 if(last_result && last_result->_result_content) {
447 jsonObject* r = last_result->_result_content;
448 hash = jsonObjectGetString(r);
451 char* pass_buf = md5sum(password);
453 size_t both_len = strlen( hash ) + strlen( pass_buf ) + 1;
454 char both_buf[both_len];
455 snprintf(both_buf, sizeof(both_buf), "%s%s", hash, pass_buf);
457 char* mess_buf = md5sum(both_buf);
459 growing_buffer* argbuf = buffer_init(64);
461 "request open-ils.auth open-ils.auth.authenticate.complete "
462 "{ \"username\" : \"%s\", \"password\" : \"%s\"", username, mess_buf );
464 if(type) buffer_fadd( argbuf, ", \"type\" : \"%s\"", type );
465 if(orgloci) buffer_fadd( argbuf, ", \"org\" : %d", orgloci );
466 if(workstation) buffer_fadd( argbuf, ", \"workstation\" : \"%s\"", workstation);
467 buffer_add_char( argbuf, '}' );
472 process_request( argbuf->buf );
475 if( login_session != NULL )
476 free( login_session );
478 const jsonObject* x = last_result->_result_content;
481 const char* authtoken = jsonObjectGetString(
482 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtoken"));
483 authtime = jsonObjectGetNumber(
484 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtime"));
487 login_session = strdup(authtoken);
489 login_session = NULL;
491 else login_session = NULL;
493 printf("Login Session: %s. Session timeout: %f\n",
494 (login_session ? login_session : "(none)"), authtime );
504 @brief Open connections to one or more specified services.
505 @param cmd_array Pointer to a list of command line chunks.
506 @return 1 in all cases.
508 The first chunk of the command line is the "open" command. Subsequent chunks, if any,
511 Try to open all specified servers. If no servers are specified, report what servers are
514 static int handle_open( const osrfStringArray* cmd_array ) {
515 if( NULL == osrfStringArrayGetString( cmd_array, 1 ) ) {
516 if( ! server_hash || osrfHashGetCount( server_hash ) == 0 ) {
517 printf( "No services are currently open\n" );
521 printf( "Service(s) currently open:\n" );
523 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
524 while( osrfHashIteratorNext( itr ) ) {
525 printf( "\t%s\n", osrfHashIteratorKey( itr ) );
527 osrfHashIteratorFree( itr );
532 server_hash = osrfNewHash( 6 );
535 for( i = 1; ; ++i ) { // for each requested service
536 const char* server = osrfStringArrayGetString( cmd_array, i );
540 if( osrfHashGet( server_hash, server ) ) {
541 printf( "Service %s is already open\n", server );
545 // Try to open a session with the current specified server
546 osrfAppSession* session = osrfAppSessionClientInit(server);
548 if(!osrfAppSessionConnect(session)) {
549 fprintf(stderr, "Unable to open service %s\n", server);
550 osrfLogWarning( OSRF_LOG_MARK, "Unable to open remote service %s\n", server );
551 osrfAppSessionFree( session );
553 osrfHashSet( server_hash, session, server );
554 printf( "Service %s opened\n", server );
562 @brief Close connections to one or more specified services.
563 @param cmd_array Pointer to a list of command line chunks.
564 @return 1 if any services were closed, or 0 if there were none to close.
566 The first chunk of the command line is the "close" command. Subsequent chunks, if any,
569 static int handle_close( const osrfStringArray* cmd_array ) {
570 if( cmd_array->size < 2 ) {
571 fprintf( stderr, "No service specified for close\n" );
576 for( i = 1; ; ++i ) {
577 const char* server = osrfStringArrayGetString( cmd_array, i );
581 osrfAppSession* session = osrfHashRemove( server_hash, server );
583 printf( "Service \"%s\" is not open\n", server );
587 osrf_app_session_disconnect( session );
588 osrfAppSessionFree( session );
589 printf( "Service \"%s\" closed\n", server );
596 @brief Close all currently open connections to services.
598 static void close_all_sessions( void ) {
600 osrfAppSession* session;
601 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
603 while(( session = osrfHashIteratorNext( itr ) )) {
604 osrf_app_session_disconnect( session );
605 osrfAppSessionFree( session );
608 osrfHashIteratorFree( itr );
611 static int handle_set( const osrfStringArray* cmd_array ) {
613 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
616 const char* val = osrfStringArrayGetString( cmd_array, 2 );
619 if(!strcmp(variable,"pretty_print")) {
620 if(!strcmp(val,"true")) {
622 printf("pretty_print = true\n");
624 } else if(!strcmp(val,"false")) {
626 printf("pretty_print = false\n");
631 if(!strcmp(variable,"raw_print")) {
632 if(!strcmp(val,"true")) {
634 printf("raw_print = true\n");
636 } else if(!strcmp(val,"false")) {
638 printf("raw_print = false\n");
650 static int handle_print( const osrfStringArray* cmd_array ) {
652 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
655 if(!strcmp(variable,"pretty_print")) {
657 printf("pretty_print = true\n");
660 printf("pretty_print = false\n");
665 if(!strcmp(variable,"raw_print")) {
667 printf("raw_print = true\n");
670 printf("raw_print = false\n");
675 if(!strcmp(variable,"login")) {
676 printf("login session = %s\n",
677 login_session ? login_session : "(none)" );
685 static int handle_router( const osrfStringArray* cmd_array ) {
692 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
693 const char* word_2 = osrfStringArrayGetString( cmd_array, 2 );
695 if( !strcmp( word_1,"query") ) {
697 if( word_2 && !strcmp( word_2, "servers" ) ) {
698 for( i=3; i < COMMAND_BUFSIZE - 3; i++ ) {
699 const char* word = osrfStringArrayGetString( cmd_array, i );
701 router_query_servers( word );
716 static int handle_request( const osrfStringArray* cmd_array, int relay ) {
721 const char* server = osrfStringArrayGetString( cmd_array, 1 );
722 const char* method = osrfStringArrayGetString( cmd_array, 2 );
726 growing_buffer* buffer = NULL;
728 int first = 1; // boolean
729 buffer = buffer_init( 128 );
730 buffer_add_char( buffer, '[' );
732 const char* word = osrfStringArrayGetString( cmd_array, i );
739 buffer_add( buffer, ", " );
741 buffer_add( buffer, word );
743 /* remove trailing semicolon if user accidentally entered it */
744 if( word[ strlen( word ) - 1 ] == ';' )
745 buffer_chomp( buffer );
747 buffer_add_char( buffer, ']' );
750 int rc = send_request( server, method, buffer, relay );
751 buffer_free( buffer );
758 int send_request( const char* server,
759 const char* method, growing_buffer* buffer, int relay ) {
760 if( server == NULL || method == NULL )
763 jsonObject* params = NULL;
765 if( buffer != NULL && buffer->n_used > 0 ) {
766 // Temporarily redirect parsing error messages to stderr
768 params = jsonParse( OSRF_BUFFER_C_STR( buffer ) );
769 osrfRestoreLogType();
772 if(!last_result || ! last_result->_result_content) {
773 printf("We're not going to call 'relay' with no result params\n");
777 params = jsonNewObject(NULL);
778 jsonObjectPush(params, last_result->_result_content );
782 if(buffer->n_used > 0 && params == NULL) {
783 fprintf(stderr, "JSON error detected, not executing\n");
784 jsonObjectFree(params);
788 int session_is_temporary; // boolean
789 osrfAppSession* session = osrfHashGet( server_hash, server );
791 session_is_temporary = 0; // use an existing session
793 session = osrfAppSessionClientInit(server); // open a session
794 session_is_temporary = 1; // just for this request
797 double start = get_timestamp_millis();
799 int req_id = osrfAppSessionSendRequest( session, params, method, 1 );
801 fprintf(stderr, "Unable to communicate with service %s\n", server);
802 osrfLogWarning( OSRF_LOG_MARK,
803 "Unable to communicate with remote service %s\n", server );
804 osrfAppSessionFree( session );
805 jsonObjectFree(params);
808 jsonObjectFree(params);
810 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
813 printf("\nReceived no data from server\n");
815 signal(SIGPIPE, SIG_IGN);
818 if(!is_from_script) less = popen( "less -EX", "w");
821 if( less == NULL ) { less = stdout; }
823 growing_buffer* resp_buffer = buffer_init(4096);
829 if(omsg->_result_content) {
831 osrfMessageFree(last_result);
837 char* j = jsonObjectToJSON(omsg->_result_content);
839 content = jsonFormatString(j);
842 content = strdup( "(null)" );
844 content = jsonObjectToJSON(omsg->_result_content);
846 content = strdup( "(null)" );
849 printf( "\nReceived Data: %s\n", content );
855 snprintf( code, sizeof(code), "%d", omsg->status_code );
856 buffer_add( resp_buffer, code );
858 printf( "\nReceived Exception:\nName: %s\nStatus: %s\nStatus: %s\n",
859 omsg->status_name, omsg->status_text, code );
862 osrfMessageFree(omsg);
867 if(omsg->_result_content) {
869 osrfMessageFree(last_result);
874 if( pretty_print && omsg->_result_content ) {
875 char* j = jsonObjectToJSON(omsg->_result_content);
877 content = jsonFormatString(j);
880 content = strdup( "(null)" );
882 content = jsonObjectToJSON(omsg->_result_content);
884 content = strdup( "(null)" );
887 buffer_add( resp_buffer, "\nReceived Data: " );
888 buffer_add( resp_buffer, content );
889 buffer_add_char( resp_buffer, '\n' );
894 buffer_add( resp_buffer, "\nReceived Exception:\nName: " );
895 buffer_add( resp_buffer, omsg->status_name );
896 buffer_add( resp_buffer, "\nStatus: " );
897 buffer_add( resp_buffer, omsg->status_text );
898 buffer_add( resp_buffer, "\nStatus: " );
900 snprintf( code, sizeof(code), "%d", omsg->status_code );
901 buffer_add( resp_buffer, code );
902 osrfMessageFree(omsg);
906 omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
910 double end = get_timestamp_millis();
912 fputs( resp_buffer->buf, less );
913 buffer_free( resp_buffer );
914 fputs("\n------------------------------------\n", less);
915 if( osrf_app_session_request_complete( session, req_id ))
916 fputs("Request Completed Successfully\n", less);
918 fprintf(less, "Request Time in seconds: %.6f\n", end - start );
919 fputs("------------------------------------\n", less);
921 if(!is_from_script) pclose(less);
923 osrf_app_session_request_finish( session, req_id );
925 if( session_is_temporary )
926 osrfAppSessionFree( session );
933 static int handle_time( const osrfStringArray* cmd_array ) {
935 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
937 printf("%f\n", get_timestamp_millis());
939 time_t epoch = (time_t) atoi( word_1 );
940 printf("%s", ctime(&epoch));
946 static int router_query_servers( const char* router_server ) {
948 if( ! router_server || strlen(router_server) == 0 )
951 const static char router_text[] = "router@%s/router";
952 size_t len = sizeof( router_text ) + strlen( router_server ) + 1;
954 snprintf(rbuf, sizeof(rbuf), router_text, router_server );
956 transport_message* send =
957 message_init( "servers", NULL, NULL, rbuf, NULL );
958 message_set_router_info( send, NULL, NULL, NULL, "query", 0 );
960 client_send_message( client, send );
961 message_free( send );
963 transport_message* recv = client_recv( client, -1 );
965 fprintf(stderr, "NULL message received from router\n");
970 "---------------------------------------------------------------------------------\n"
971 "Received from 'server' query on %s\n"
972 "---------------------------------------------------------------------------------\n"
973 "original reg time | latest reg time | last used time | class | server\n"
974 "---------------------------------------------------------------------------------\n"
976 "---------------------------------------------------------------------------------\n"
977 , router_server, recv->body );
979 message_free( recv );
984 static int print_help( void ) {
987 "---------------------------------------------------------------------------------\n"
988 "General commands:\n"
989 "---------------------------------------------------------------------------------\n"
990 "help - Display this message\n",
993 "!<command> [args] - Forks and runs the given command in the shell\n",
997 "time - Prints the current time\n"
998 "time <timestamp> - Formats seconds since epoch into readable format\n"
1000 "set <variable> <value> - Set a srfsh variable (e.g. set pretty_print true )\n"
1001 "print <variable> - Displays the value of a srfsh variable\n"
1003 "---------------------------------------------------------------------------------\n"
1005 "---------------------------------------------------------------------------------\n"
1006 "pretty_print - Display nicely formatted JSON results\n"
1007 " - Accepted values: true, false\n"
1008 " - Default value: true\n"
1010 "raw_print - Pass JSON results through 'less' paging command\n"
1011 " - Accepted values: true, false\n"
1012 " - Default value: false\n"
1014 "---------------------------------------------------------------------------------\n"
1015 "Commands for OpenSRF services and methods:\n"
1016 "---------------------------------------------------------------------------------\n"
1017 "introspect <service> [\"method-name\"]\n"
1018 " - Prints service API, limited to the methods that match the optional\n"
1019 " right-truncated method-name parameter\n"
1021 "request <service> <method> [ <JSON formatted string of params> ]\n"
1022 " - Anything passed in will be wrapped in a JSON array,\n"
1023 " so add commas if there is more than one param\n"
1025 "router query servers <server1 [, server2, ...]>\n"
1026 " - Returns stats on connected services\n"
1028 "relay <service> <method>\n"
1029 " - Performs the requested query using the last received result as the param\n"
1031 "math_bench <num_batches> [0|1|2]\n"
1032 " - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
1033 " 2 means reconnect after every request\n"
1035 "---------------------------------------------------------------------------------\n"
1036 " Commands for Evergreen\n"
1037 "---------------------------------------------------------------------------------\n"
1038 "login <username> <password> [type] [org_unit] [workstation]\n"
1039 " - Logs into the 'server' and displays the session id\n"
1040 " - To view the session id later, enter: print login\n"
1041 "---------------------------------------------------------------------------------\n"
1043 "Note: long output is piped through 'less' unless the 'raw_print' variable\n"
1044 "is true. To search in 'less', type: /<search>\n"
1045 "---------------------------------------------------------------------------------\n"
1054 static char* tabs(int count) {
1055 growing_buffer* buf = buffer_init(24);
1057 for(i=0;i!=count;i++)
1058 buffer_add(buf, " ");
1060 char* final = buffer_data( buf );
1067 @brief Execute the "math_bench" command.
1068 @param cmd_array A list of command arguments.
1069 @return 1 if successful, 0 if not.
1071 The first command argument is required. It is the number of iterations requested. If
1072 it is less than 1, it is coerced to 1.
1074 The second command argument is optional, with allowed values of 0 (the default), 1, or 2.
1075 It controls when and whether we call osrf_app_session_disconnect(). If this argument is
1076 out of range, it is coerced to a value of 0 or 2.
1078 static int handle_math( const osrfStringArray* cmd_array ) {
1079 const char* word = osrfStringArrayGetString( cmd_array, 1 );
1081 int count = atoi( word );
1086 const char* style_arg = osrfStringArrayGetString( cmd_array, 2 );
1088 style = atoi( style_arg );
1091 else if( style < 0 )
1095 return do_math( count, style );
1101 static int do_math( int count, int style ) {
1103 osrfAppSession* session = osrfAppSessionClientInit( "opensrf.math" );
1104 osrfAppSessionConnect(session);
1106 jsonObject* params = jsonNewObjectType( JSON_ARRAY );
1107 jsonObjectPush(params,jsonNewObject("1"));
1108 jsonObjectPush(params,jsonNewObject("2"));
1110 char* methods[] = { "add", "sub", "mult", "div" };
1111 char* answers[] = { "3", "-1", "2", "0.5" };
1113 // Initialize timings to zero. This shouldn't make a difference, because
1114 // we overwrite each timing anyway before reporting them.
1115 float times[ count * 4 ];
1117 for( fi = 0; fi < count; ++fi )
1121 for(k=0;k!=100;k++) {
1123 fprintf(stderr,"|");
1125 fprintf(stderr,".");
1128 fprintf(stderr,"\n\n");
1132 for(i=0; i!= count; i++) {
1135 for(j=0; j != 4; j++) {
1139 double start = get_timestamp_millis();
1140 int req_id = osrfAppSessionSendRequest( session, params, methods[j], 1 );
1141 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, 5 );
1142 double end = get_timestamp_millis();
1144 times[(4*i) + j] = end - start;
1148 if(omsg->_result_content) {
1149 char* jsn = jsonObjectToJSON(omsg->_result_content);
1150 if(!strcmp(jsn, answers[j]))
1151 fprintf(stderr, "+");
1153 fprintf(stderr, "\n![%s] - should be %s\n", jsn, answers[j] );
1158 osrfMessageFree(omsg);
1160 } else { fprintf( stderr, "\nempty message for tt: %d\n", req_id ); }
1162 osrf_app_session_request_finish( session, req_id );
1165 osrf_app_session_disconnect( session );
1168 fprintf(stderr,"\n");
1172 osrf_app_session_disconnect( session );
1175 osrfAppSessionFree( session );
1176 jsonObjectFree(params);
1180 for(c=0; c!= count*4; c++)
1183 float avg = total / (count*4);
1184 fprintf(stderr, "\n Average round trip time: %f\n", avg );
1190 @name Command line parser
1192 This group of functions parses the command line into a series of chunks, and stores
1193 the chunks in an osrfStringArray.
1195 A chunk may consist of a JSON string, complete with square brackets, curly braces, and
1196 embedded white space. It wouldn't work simply to break up the line into tokens
1197 separated by white space. Sometimes white space separates chunks, and sometimes it
1198 occurs within a chunk.
1200 When it sees a left square bracket or curly brace, the parser goes into JSON mode,
1201 collecting characters up to the corresponding right square bracket or curly brace.
1202 It also eliminates most kinds of unnecessary white space.
1204 The JSON parsing is rudimentary. It does not validate the syntax -- it merely looks
1205 for the end of the JSON string. Eventually the JSON string will be passed to a real
1206 JSON parser, which will detect and report syntax errors.
1208 When not in JSON mode, the parser collects tokens separated by white space. It also
1209 collects character strings in quotation marks, possibly including embedded white space.
1210 Within a quoted string, an embedded quotation mark does not terminate the string if it
1211 is escaped by a preceding backslash.
1215 @brief Collect a string literal enclosed by quotation marks.
1216 @param parser Pointer to an ArgParser
1218 A quotation mark serves as a terminator unless it is escaped by a preceding backslash.
1219 In the latter case, we collect both the backslash and the escaped quotation mark.
1221 static void get_string_literal( ArgParser* parser ) {
1222 // Collect character until the first unescaped quotation mark, or EOL
1224 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1226 // Don't stop at a quotation mark if it's escaped
1227 if( '\\' == *parser->itr && '\"' == *( parser->itr + 1 ) ) {
1228 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1233 } while( *parser->itr && *parser->itr != '\"' );
1235 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1240 @brief Collect a JSON array (enclosed by square brackets).
1241 @param parser Pointer to an ArgParser.
1243 Collect characters until you find the closing square bracket. Collect any intervening
1244 JSON arrays, JSON objects, or string literals recursively.
1246 static void get_json_array( ArgParser* parser ) {
1248 OSRF_BUFFER_ADD_CHAR( parser->buf, '[' );
1251 // Collect characters through the closing square bracket
1252 while( *parser->itr != ']' ) {
1254 if( '\"' == *parser->itr ) {
1255 get_string_literal( parser );
1256 } else if( '[' == *parser->itr ) {
1257 get_json_array( parser );
1258 } else if( '{' == *parser->itr ) {
1259 get_json_object( parser );
1260 } else if( isspace( (unsigned char) *parser->itr ) ) {
1261 ++parser->itr; // Ignore white space
1262 } else if ( '\0' == *parser->itr ) {
1263 return; // Ignore failure to close the object
1266 // make sure that bare words don't run together
1267 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1271 OSRF_BUFFER_ADD_CHAR( parser->buf, ']' );
1276 @brief Collect a JSON object (enclosed by curly braces).
1277 @param parser Pointer to an ArgParser.
1279 Collect characters until you find the closing curly brace. Collect any intervening
1280 JSON arrays, JSON objects, or string literals recursively.
1282 static void get_json_object( ArgParser* parser ) {
1284 OSRF_BUFFER_ADD_CHAR( parser->buf, '{' );
1287 // Collect characters through the closing curly brace
1288 while( *parser->itr != '}' ) {
1290 if( '\"' == *parser->itr ) {
1291 get_string_literal( parser );
1292 } else if( '[' == *parser->itr ) {
1293 get_json_array( parser );
1294 } else if( '{' == *parser->itr ) {
1295 get_json_object( parser );
1296 } else if( isspace( (unsigned char) *parser->itr ) ) {
1297 ++parser->itr; // Ignore white space
1298 } else if ( '\0' == *parser->itr ) {
1299 return; // Ignore failure to close the object
1302 // make sure that bare words don't run together
1303 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1307 OSRF_BUFFER_ADD_CHAR( parser->buf, '}' );
1312 @brief Collect a token terminated by white space or a ']' or '}' character.
1313 @param parser Pointer to an ArgParser
1315 For valid JSON, the chunk collected here would be either a number or one of the
1316 JSON key words "null", "true", or "false". However at this stage we're not finicky.
1317 We just collect whatever we see until we find a terminator.
1319 static void get_misc( ArgParser* parser ) {
1320 // Collect characters until we see one that doesn't belong
1322 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1324 char c = *parser->itr;
1325 if( '\0' == c || isspace( (unsigned char) c )
1326 || '{' == c || '}' == c || '[' == c || ']' == c || '\"' == c ) {
1333 @brief Parse the command line.
1334 @param request Pointer to the command line
1335 @param cmd_array Pointer to an osrfStringArray to hold the output of the parser.
1337 The parser operates by recursive descent. We build each chunk of command line in a
1338 growing_buffer belonging to an ArgParser, and then load the chunk into a slot in an
1339 osrfStringArray supplied by the calling code.
1341 static void parse_args( const char* request, osrfStringArray* cmd_array )
1345 // Initialize the ArgParser
1346 parser.itr = request;
1347 parser.buf = buffer_init( 128 );
1349 int done = 0; // boolean
1351 OSRF_BUFFER_RESET( parser.buf );
1353 // skip any white space or commas
1355 && ( isspace( (unsigned char) *parser.itr ) || ',' == *parser.itr ) )
1358 if( '\0' == *parser.itr )
1360 else if( '{' == *parser.itr ) {
1361 // Load a JSON object
1362 get_json_object( &parser );
1363 } else if( '[' == *parser.itr ) {
1364 // Load a JSON array
1365 get_json_array( &parser );
1366 } else if( '\"' == *parser.itr ) {
1367 // Load a string literal
1368 get_string_literal( &parser );
1370 // Anything else is delimited by white space
1372 OSRF_BUFFER_ADD_CHAR( parser.buf, *parser.itr );
1374 } while( *parser.itr && ! isspace( (unsigned char) *parser.itr ) );
1377 // Remove a trailing comma, if present
1378 char lastc = OSRF_BUFFER_C_STR( parser.buf )[
1379 strlen( OSRF_BUFFER_C_STR( parser.buf ) ) - 1 ];
1381 buffer_chomp( parser.buf );
1383 // Add the chunk to the osrfStringArray
1384 const char* s = OSRF_BUFFER_C_STR( parser.buf );
1386 osrfStringArrayAdd( cmd_array, s );
1390 buffer_free( parser.buf );