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] == '!') {
379 if (!no_bang) system( request + 1 );
383 osrfStringArrayFree( cmd_array );
386 return parse_error( request );
392 static int handle_introspect( const osrfStringArray* cmd_array ) {
394 const char* service = osrfStringArrayGetString( cmd_array, 1 );
398 fprintf(stderr, "--> %s\n", service );
400 // Build a command in a suitably-sized
401 // buffer and then parse it
404 const char* method = osrfStringArrayGetString( cmd_array, 2 );
406 static const char text[] = "request %s opensrf.system.method %s";
407 len = sizeof( text ) + strlen( service ) + strlen( method );
409 snprintf( buf, sizeof(buf), text, service, method );
410 return process_request( buf );
413 static const char text[] = "request %s opensrf.system.method.all";
414 len = sizeof( text ) + strlen( service );
416 snprintf( buf, sizeof(buf), text, service );
417 return process_request( buf );
423 static int handle_login( const osrfStringArray* cmd_array ) {
425 const char* username = osrfStringArrayGetString( cmd_array, 1 );
426 const char* password = osrfStringArrayGetString( cmd_array, 2 );
428 if( username && password ) {
430 const char* type = osrfStringArrayGetString( cmd_array, 3 );
431 const char* orgloc = osrfStringArrayGetString( cmd_array, 4 );
432 const char* workstation = osrfStringArrayGetString( cmd_array, 5 );
433 int orgloci = (orgloc) ? atoi(orgloc) : 0;
434 if(!type) type = "opac";
436 char login_text[] = "request open-ils.auth open-ils.auth.authenticate.init \"%s\"";
437 size_t len = sizeof( login_text ) + strlen(username) + 1;
440 snprintf( buf, sizeof(buf), login_text, username );
441 process_request(buf);
444 if(last_result && last_result->_result_content) {
445 jsonObject* r = last_result->_result_content;
446 hash = jsonObjectGetString(r);
449 char* pass_buf = md5sum(password);
451 size_t both_len = strlen( hash ) + strlen( pass_buf ) + 1;
452 char both_buf[both_len];
453 snprintf(both_buf, sizeof(both_buf), "%s%s", hash, pass_buf);
455 char* mess_buf = md5sum(both_buf);
457 growing_buffer* argbuf = buffer_init(64);
459 "request open-ils.auth open-ils.auth.authenticate.complete "
460 "{ \"username\" : \"%s\", \"password\" : \"%s\"", username, mess_buf );
462 if(type) buffer_fadd( argbuf, ", \"type\" : \"%s\"", type );
463 if(orgloci) buffer_fadd( argbuf, ", \"org\" : %d", orgloci );
464 if(workstation) buffer_fadd( argbuf, ", \"workstation\" : \"%s\"", workstation);
465 buffer_add_char( argbuf, '}' );
470 process_request( argbuf->buf );
473 if( login_session != NULL )
474 free( login_session );
476 const jsonObject* x = last_result->_result_content;
479 const char* authtoken = jsonObjectGetString(
480 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtoken"));
481 authtime = jsonObjectGetNumber(
482 jsonObjectGetKeyConst(jsonObjectGetKeyConst(x,"payload"), "authtime"));
485 login_session = strdup(authtoken);
487 login_session = NULL;
489 else login_session = NULL;
491 printf("Login Session: %s. Session timeout: %f\n",
492 (login_session ? login_session : "(none)"), authtime );
502 @brief Open connections to one or more specified services.
503 @param cmd_array Pointer to a list of command line chunks.
504 @return 1 in all cases.
506 The first chunk of the command line is the "open" command. Subsequent chunks, if any,
509 Try to open all specified servers. If no servers are specified, report what servers are
512 static int handle_open( const osrfStringArray* cmd_array ) {
513 if( NULL == osrfStringArrayGetString( cmd_array, 1 ) ) {
514 if( ! server_hash || osrfHashGetCount( server_hash ) == 0 ) {
515 printf( "No services are currently open\n" );
519 printf( "Service(s) currently open:\n" );
521 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
522 while( osrfHashIteratorNext( itr ) ) {
523 printf( "\t%s\n", osrfHashIteratorKey( itr ) );
525 osrfHashIteratorFree( itr );
530 server_hash = osrfNewHash( 6 );
533 for( i = 1; ; ++i ) { // for each requested service
534 const char* server = osrfStringArrayGetString( cmd_array, i );
538 if( osrfHashGet( server_hash, server ) ) {
539 printf( "Service %s is already open\n", server );
543 // Try to open a session with the current specified server
544 osrfAppSession* session = osrfAppSessionClientInit(server);
546 if(!osrfAppSessionConnect(session)) {
547 fprintf(stderr, "Unable to open service %s\n", server);
548 osrfLogWarning( OSRF_LOG_MARK, "Unable to open remote service %s\n", server );
549 osrfAppSessionFree( session );
551 osrfHashSet( server_hash, session, server );
552 printf( "Service %s opened\n", server );
560 @brief Close connections to one or more specified services.
561 @param cmd_array Pointer to a list of command line chunks.
562 @return 1 if any services were closed, or 0 if there were none to close.
564 The first chunk of the command line is the "close" command. Subsequent chunks, if any,
567 static int handle_close( const osrfStringArray* cmd_array ) {
568 if( cmd_array->size < 2 ) {
569 fprintf( stderr, "No service specified for close\n" );
574 for( i = 1; ; ++i ) {
575 const char* server = osrfStringArrayGetString( cmd_array, i );
579 osrfAppSession* session = osrfHashRemove( server_hash, server );
581 printf( "Service \"%s\" is not open\n", server );
585 osrf_app_session_disconnect( session );
586 osrfAppSessionFree( session );
587 printf( "Service \"%s\" closed\n", server );
594 @brief Close all currently open connections to services.
596 static void close_all_sessions( void ) {
598 osrfAppSession* session;
599 osrfHashIterator* itr = osrfNewHashIterator( server_hash );
601 while(( session = osrfHashIteratorNext( itr ) )) {
602 osrf_app_session_disconnect( session );
603 osrfAppSessionFree( session );
606 osrfHashIteratorFree( itr );
609 static int handle_set( const osrfStringArray* cmd_array ) {
611 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
614 const char* val = osrfStringArrayGetString( cmd_array, 2 );
617 if(!strcmp(variable,"pretty_print")) {
618 if(!strcmp(val,"true")) {
620 printf("pretty_print = true\n");
622 } else if(!strcmp(val,"false")) {
624 printf("pretty_print = false\n");
629 if(!strcmp(variable,"raw_print")) {
630 if(!strcmp(val,"true")) {
632 printf("raw_print = true\n");
634 } else if(!strcmp(val,"false")) {
636 printf("raw_print = false\n");
648 static int handle_print( const osrfStringArray* cmd_array ) {
650 const char* variable = osrfStringArrayGetString( cmd_array, 1 );
653 if(!strcmp(variable,"pretty_print")) {
655 printf("pretty_print = true\n");
658 printf("pretty_print = false\n");
663 if(!strcmp(variable,"raw_print")) {
665 printf("raw_print = true\n");
668 printf("raw_print = false\n");
673 if(!strcmp(variable,"login")) {
674 printf("login session = %s\n",
675 login_session ? login_session : "(none)" );
683 static int handle_router( const osrfStringArray* cmd_array ) {
690 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
691 const char* word_2 = osrfStringArrayGetString( cmd_array, 2 );
693 if( !strcmp( word_1,"query") ) {
695 if( word_2 && !strcmp( word_2, "servers" ) ) {
696 for( i=3; i < COMMAND_BUFSIZE - 3; i++ ) {
697 const char* word = osrfStringArrayGetString( cmd_array, i );
699 router_query_servers( word );
714 static int handle_request( const osrfStringArray* cmd_array, int relay ) {
719 const char* server = osrfStringArrayGetString( cmd_array, 1 );
720 const char* method = osrfStringArrayGetString( cmd_array, 2 );
724 growing_buffer* buffer = NULL;
726 int first = 1; // boolean
727 buffer = buffer_init( 128 );
728 buffer_add_char( buffer, '[' );
730 const char* word = osrfStringArrayGetString( cmd_array, i );
737 buffer_add( buffer, ", " );
739 buffer_add( buffer, word );
741 /* remove trailing semicolon if user accidentally entered it */
742 if( word[ strlen( word ) - 1 ] == ';' )
743 buffer_chomp( buffer );
745 buffer_add_char( buffer, ']' );
748 int rc = send_request( server, method, buffer, relay );
749 buffer_free( buffer );
756 int send_request( const char* server,
757 const char* method, growing_buffer* buffer, int relay ) {
758 if( server == NULL || method == NULL )
761 jsonObject* params = NULL;
763 if( buffer != NULL && buffer->n_used > 0 ) {
764 // Temporarily redirect parsing error messages to stderr
766 params = jsonParse( OSRF_BUFFER_C_STR( buffer ) );
767 osrfRestoreLogType();
770 if(!last_result || ! last_result->_result_content) {
771 printf("We're not going to call 'relay' with no result params\n");
775 params = jsonNewObject(NULL);
776 jsonObjectPush(params, last_result->_result_content );
780 if(buffer->n_used > 0 && params == NULL) {
781 fprintf(stderr, "JSON error detected, not executing\n");
782 jsonObjectFree(params);
786 int session_is_temporary; // boolean
787 osrfAppSession* session = osrfHashGet( server_hash, server );
789 session_is_temporary = 0; // use an existing session
791 session = osrfAppSessionClientInit(server); // open a session
792 session_is_temporary = 1; // just for this request
795 double start = get_timestamp_millis();
797 int req_id = osrfAppSessionSendRequest( session, params, method, 1 );
799 fprintf(stderr, "Unable to communicate with service %s\n", server);
800 osrfLogWarning( OSRF_LOG_MARK,
801 "Unable to communicate with remote service %s\n", server );
802 osrfAppSessionFree( session );
803 jsonObjectFree(params);
806 jsonObjectFree(params);
808 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
811 printf("\nReceived no data from server\n");
813 signal(SIGPIPE, SIG_IGN);
816 if(!is_from_script) less = popen( "less -EX", "w");
819 if( less == NULL ) { less = stdout; }
821 growing_buffer* resp_buffer = buffer_init(4096);
827 if(omsg->_result_content) {
829 osrfMessageFree(last_result);
835 char* j = jsonObjectToJSON(omsg->_result_content);
837 content = jsonFormatString(j);
840 content = strdup( "(null)" );
842 content = jsonObjectToJSON(omsg->_result_content);
844 content = strdup( "(null)" );
847 printf( "\nReceived Data: %s\n", content );
853 snprintf( code, sizeof(code), "%d", omsg->status_code );
854 buffer_add( resp_buffer, code );
856 printf( "\nReceived Exception:\nName: %s\nStatus: %s\nStatus: %s\n",
857 omsg->status_name, omsg->status_text, code );
860 osrfMessageFree(omsg);
865 if(omsg->_result_content) {
867 osrfMessageFree(last_result);
872 if( pretty_print && omsg->_result_content ) {
873 char* j = jsonObjectToJSON(omsg->_result_content);
875 content = jsonFormatString(j);
878 content = strdup( "(null)" );
880 content = jsonObjectToJSON(omsg->_result_content);
882 content = strdup( "(null)" );
885 buffer_add( resp_buffer, "\nReceived Data: " );
886 buffer_add( resp_buffer, content );
887 buffer_add_char( resp_buffer, '\n' );
892 buffer_add( resp_buffer, "\nReceived Exception:\nName: " );
893 buffer_add( resp_buffer, omsg->status_name );
894 buffer_add( resp_buffer, "\nStatus: " );
895 buffer_add( resp_buffer, omsg->status_text );
896 buffer_add( resp_buffer, "\nStatus: " );
898 snprintf( code, sizeof(code), "%d", omsg->status_code );
899 buffer_add( resp_buffer, code );
900 osrfMessageFree(omsg);
904 omsg = osrfAppSessionRequestRecv( session, req_id, recv_timeout );
908 double end = get_timestamp_millis();
910 fputs( resp_buffer->buf, less );
911 buffer_free( resp_buffer );
912 fputs("\n------------------------------------\n", less);
913 if( osrf_app_session_request_complete( session, req_id ))
914 fputs("Request Completed Successfully\n", less);
916 fprintf(less, "Request Time in seconds: %.6f\n", end - start );
917 fputs("------------------------------------\n", less);
921 osrf_app_session_request_finish( session, req_id );
923 if( session_is_temporary )
924 osrfAppSessionFree( session );
931 static int handle_time( const osrfStringArray* cmd_array ) {
933 const char* word_1 = osrfStringArrayGetString( cmd_array, 1 );
935 printf("%f\n", get_timestamp_millis());
937 time_t epoch = (time_t) atoi( word_1 );
938 printf("%s", ctime(&epoch));
944 static int router_query_servers( const char* router_server ) {
946 if( ! router_server || strlen(router_server) == 0 )
949 const static char router_text[] = "router@%s/router";
950 size_t len = sizeof( router_text ) + strlen( router_server ) + 1;
952 snprintf(rbuf, sizeof(rbuf), router_text, router_server );
954 transport_message* send =
955 message_init( "servers", NULL, NULL, rbuf, NULL );
956 message_set_router_info( send, NULL, NULL, NULL, "query", 0 );
958 client_send_message( client, send );
959 message_free( send );
961 transport_message* recv = client_recv( client, -1 );
963 fprintf(stderr, "NULL message received from router\n");
968 "---------------------------------------------------------------------------------\n"
969 "Received from 'server' query on %s\n"
970 "---------------------------------------------------------------------------------\n"
971 "original reg time | latest reg time | last used time | class | server\n"
972 "---------------------------------------------------------------------------------\n"
974 "---------------------------------------------------------------------------------\n"
975 , router_server, recv->body );
977 message_free( recv );
982 static int print_help( void ) {
985 "---------------------------------------------------------------------------------\n"
986 "General commands:\n"
987 "---------------------------------------------------------------------------------\n"
988 "help - Display this message\n"
989 "!<command> [args] - Forks and runs the given command in the shell\n"
991 "time - Prints the current time\n"
992 "time <timestamp> - Formats seconds since epoch into readable format\n"
994 "set <variable> <value> - Set a srfsh variable (e.g. set pretty_print true )\n"
995 "print <variable> - Displays the value of a srfsh variable\n"
997 "---------------------------------------------------------------------------------\n"
999 "---------------------------------------------------------------------------------\n"
1000 "pretty_print - Display nicely formatted JSON results\n"
1001 " - Accepted values: true, false\n"
1002 " - Default value: true\n"
1004 "raw_print - Pass JSON results through 'less' paging command\n"
1005 " - Accepted values: true, false\n"
1006 " - Default value: false\n"
1008 "---------------------------------------------------------------------------------\n"
1009 "Commands for OpenSRF services and methods:\n"
1010 "---------------------------------------------------------------------------------\n"
1011 "introspect <service> [\"method-name\"]\n"
1012 " - Prints service API, limited to the methods that match the optional\n"
1013 " right-truncated method-name parameter\n"
1015 "request <service> <method> [ <JSON formatted string of params> ]\n"
1016 " - Anything passed in will be wrapped in a JSON array,\n"
1017 " so add commas if there is more than one param\n"
1019 "router query servers <server1 [, server2, ...]>\n"
1020 " - Returns stats on connected services\n"
1022 "relay <service> <method>\n"
1023 " - Performs the requested query using the last received result as the param\n"
1025 "math_bench <num_batches> [0|1|2]\n"
1026 " - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
1027 " 2 means reconnect after every request\n"
1029 "---------------------------------------------------------------------------------\n"
1030 " Commands for Evergreen\n"
1031 "---------------------------------------------------------------------------------\n"
1032 "login <username> <password> [type] [org_unit] [workstation]\n"
1033 " - Logs into the 'server' and displays the session id\n"
1034 " - To view the session id later, enter: print login\n"
1035 "---------------------------------------------------------------------------------\n"
1037 "Note: long output is piped through 'less' unless the 'raw_print' variable\n"
1038 "is true. To search in 'less', type: /<search>\n"
1039 "---------------------------------------------------------------------------------\n"
1048 static char* tabs(int count) {
1049 growing_buffer* buf = buffer_init(24);
1051 for(i=0;i!=count;i++)
1052 buffer_add(buf, " ");
1054 char* final = buffer_data( buf );
1061 @brief Execute the "math_bench" command.
1062 @param cmd_array A list of command arguments.
1063 @return 1 if successful, 0 if not.
1065 The first command argument is required. It is the number of iterations requested. If
1066 it is less than 1, it is coerced to 1.
1068 The second command argument is optional, with allowed values of 0 (the default), 1, or 2.
1069 It controls when and whether we call osrf_app_session_disconnect(). If this argument is
1070 out of range, it is coerced to a value of 0 or 2.
1072 static int handle_math( const osrfStringArray* cmd_array ) {
1073 const char* word = osrfStringArrayGetString( cmd_array, 1 );
1075 int count = atoi( word );
1080 const char* style_arg = osrfStringArrayGetString( cmd_array, 2 );
1082 style = atoi( style_arg );
1085 else if( style < 0 )
1089 return do_math( count, style );
1095 static int do_math( int count, int style ) {
1097 osrfAppSession* session = osrfAppSessionClientInit( "opensrf.math" );
1098 osrfAppSessionConnect(session);
1100 jsonObject* params = jsonNewObjectType( JSON_ARRAY );
1101 jsonObjectPush(params,jsonNewObject("1"));
1102 jsonObjectPush(params,jsonNewObject("2"));
1104 char* methods[] = { "add", "sub", "mult", "div" };
1105 char* answers[] = { "3", "-1", "2", "0.5" };
1107 // Initialize timings to zero. This shouldn't make a difference, because
1108 // we overwrite each timing anyway before reporting them.
1109 float times[ count * 4 ];
1111 for( fi = 0; fi < count; ++fi )
1115 for(k=0;k!=100;k++) {
1117 fprintf(stderr,"|");
1119 fprintf(stderr,".");
1122 fprintf(stderr,"\n\n");
1126 for(i=0; i!= count; i++) {
1129 for(j=0; j != 4; j++) {
1133 double start = get_timestamp_millis();
1134 int req_id = osrfAppSessionSendRequest( session, params, methods[j], 1 );
1135 osrfMessage* omsg = osrfAppSessionRequestRecv( session, req_id, 5 );
1136 double end = get_timestamp_millis();
1138 times[(4*i) + j] = end - start;
1142 if(omsg->_result_content) {
1143 char* jsn = jsonObjectToJSON(omsg->_result_content);
1144 if(!strcmp(jsn, answers[j]))
1145 fprintf(stderr, "+");
1147 fprintf(stderr, "\n![%s] - should be %s\n", jsn, answers[j] );
1152 osrfMessageFree(omsg);
1154 } else { fprintf( stderr, "\nempty message for tt: %d\n", req_id ); }
1156 osrf_app_session_request_finish( session, req_id );
1159 osrf_app_session_disconnect( session );
1162 fprintf(stderr,"\n");
1166 osrf_app_session_disconnect( session );
1169 osrfAppSessionFree( session );
1170 jsonObjectFree(params);
1174 for(c=0; c!= count*4; c++)
1177 float avg = total / (count*4);
1178 fprintf(stderr, "\n Average round trip time: %f\n", avg );
1184 @name Command line parser
1186 This group of functions parses the command line into a series of chunks, and stores
1187 the chunks in an osrfStringArray.
1189 A chunk may consist of a JSON string, complete with square brackets, curly braces, and
1190 embedded white space. It wouldn't work simply to break up the line into tokens
1191 separated by white space. Sometimes white space separates chunks, and sometimes it
1192 occurs within a chunk.
1194 When it sees a left square bracket or curly brace, the parser goes into JSON mode,
1195 collecting characters up to the corresponding right square bracket or curly brace.
1196 It also eliminates most kinds of unnecessary white space.
1198 The JSON parsing is rudimentary. It does not validate the syntax -- it merely looks
1199 for the end of the JSON string. Eventually the JSON string will be passed to a real
1200 JSON parser, which will detect and report syntax errors.
1202 When not in JSON mode, the parser collects tokens separated by white space. It also
1203 collects character strings in quotation marks, possibly including embedded white space.
1204 Within a quoted string, an embedded quotation mark does not terminate the string if it
1205 is escaped by a preceding backslash.
1209 @brief Collect a string literal enclosed by quotation marks.
1210 @param parser Pointer to an ArgParser
1212 A quotation mark serves as a terminator unless it is escaped by a preceding backslash.
1213 In the latter case, we collect both the backslash and the escaped quotation mark.
1215 static void get_string_literal( ArgParser* parser ) {
1216 // Collect character until the first unescaped quotation mark, or EOL
1218 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1220 // Don't stop at a quotation mark if it's escaped
1221 if( '\\' == *parser->itr && '\"' == *( parser->itr + 1 ) ) {
1222 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1227 } while( *parser->itr && *parser->itr != '\"' );
1229 OSRF_BUFFER_ADD_CHAR( parser->buf, '\"' );
1234 @brief Collect a JSON array (enclosed by square brackets).
1235 @param parser Pointer to an ArgParser.
1237 Collect characters until you find the closing square bracket. Collect any intervening
1238 JSON arrays, JSON objects, or string literals recursively.
1240 static void get_json_array( ArgParser* parser ) {
1242 OSRF_BUFFER_ADD_CHAR( parser->buf, '[' );
1245 // Collect characters through the closing square bracket
1246 while( *parser->itr != ']' ) {
1248 if( '\"' == *parser->itr ) {
1249 get_string_literal( parser );
1250 } else if( '[' == *parser->itr ) {
1251 get_json_array( parser );
1252 } else if( '{' == *parser->itr ) {
1253 get_json_object( parser );
1254 } else if( isspace( (unsigned char) *parser->itr ) ) {
1255 ++parser->itr; // Ignore white space
1256 } else if ( '\0' == *parser->itr ) {
1257 return; // Ignore failure to close the object
1260 // make sure that bare words don't run together
1261 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1265 OSRF_BUFFER_ADD_CHAR( parser->buf, ']' );
1270 @brief Collect a JSON object (enclosed by curly braces).
1271 @param parser Pointer to an ArgParser.
1273 Collect characters until you find the closing curly brace. Collect any intervening
1274 JSON arrays, JSON objects, or string literals recursively.
1276 static void get_json_object( ArgParser* parser ) {
1278 OSRF_BUFFER_ADD_CHAR( parser->buf, '{' );
1281 // Collect characters through the closing curly brace
1282 while( *parser->itr != '}' ) {
1284 if( '\"' == *parser->itr ) {
1285 get_string_literal( parser );
1286 } else if( '[' == *parser->itr ) {
1287 get_json_array( parser );
1288 } else if( '{' == *parser->itr ) {
1289 get_json_object( parser );
1290 } else if( isspace( (unsigned char) *parser->itr ) ) {
1291 ++parser->itr; // Ignore white space
1292 } else if ( '\0' == *parser->itr ) {
1293 return; // Ignore failure to close the object
1296 // make sure that bare words don't run together
1297 OSRF_BUFFER_ADD_CHAR( parser->buf, ' ' );
1301 OSRF_BUFFER_ADD_CHAR( parser->buf, '}' );
1306 @brief Collect a token terminated by white space or a ']' or '}' character.
1307 @param parser Pointer to an ArgParser
1309 For valid JSON, the chunk collected here would be either a number or one of the
1310 JSON key words "null", "true", or "false". However at this stage we're not finicky.
1311 We just collect whatever we see until we find a terminator.
1313 static void get_misc( ArgParser* parser ) {
1314 // Collect characters until we see one that doesn't belong
1316 OSRF_BUFFER_ADD_CHAR( parser->buf, *parser->itr );
1318 char c = *parser->itr;
1319 if( '\0' == c || isspace( (unsigned char) c )
1320 || '{' == c || '}' == c || '[' == c || ']' == c || '\"' == c ) {
1327 @brief Parse the command line.
1328 @param request Pointer to the command line
1329 @param cmd_array Pointer to an osrfStringArray to hold the output of the parser.
1331 The parser operates by recursive descent. We build each chunk of command line in a
1332 growing_buffer belonging to an ArgParser, and then load the chunk into a slot in an
1333 osrfStringArray supplied by the calling code.
1335 static void parse_args( const char* request, osrfStringArray* cmd_array )
1339 // Initialize the ArgParser
1340 parser.itr = request;
1341 parser.buf = buffer_init( 128 );
1343 int done = 0; // boolean
1345 OSRF_BUFFER_RESET( parser.buf );
1347 // skip any white space or commas
1349 && ( isspace( (unsigned char) *parser.itr ) || ',' == *parser.itr ) )
1352 if( '\0' == *parser.itr )
1354 else if( '{' == *parser.itr ) {
1355 // Load a JSON object
1356 get_json_object( &parser );
1357 } else if( '[' == *parser.itr ) {
1358 // Load a JSON array
1359 get_json_array( &parser );
1360 } else if( '\"' == *parser.itr ) {
1361 // Load a string literal
1362 get_string_literal( &parser );
1364 // Anything else is delimited by white space
1366 OSRF_BUFFER_ADD_CHAR( parser.buf, *parser.itr );
1368 } while( *parser.itr && ! isspace( (unsigned char) *parser.itr ) );
1371 // Remove a trailing comma, if present
1372 char lastc = OSRF_BUFFER_C_STR( parser.buf )[
1373 strlen( OSRF_BUFFER_C_STR( parser.buf ) ) - 1 ];
1375 buffer_chomp( parser.buf );
1377 // Add the chunk to the osrfStringArray
1378 const char* s = OSRF_BUFFER_C_STR( parser.buf );
1380 osrfStringArrayAdd( cmd_array, s );
1384 buffer_free( parser.buf );