Patch from Scott McKellar to remove srfsh.h and push all relevant
[OpenSRF.git] / src / srfsh / srfsh.c
1 #include <opensrf/transport_client.h>
2 #include <opensrf/osrf_message.h>
3 #include <opensrf/osrf_app_session.h>
4 #include <time.h>
5 #include <sys/timeb.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8
9 #include <opensrf/utils.h>
10 #include <opensrf/log.h>
11
12 #include <signal.h>
13
14 #include <stdio.h>
15 #include <readline/readline.h>
16 #include <readline/history.h>
17
18 #define SRFSH_PORT 5222
19 #define COMMAND_BUFSIZE 4096
20
21
22 /* shell prompt */
23 static const char* prompt = "srfsh# ";
24
25 static char* history_file = NULL;
26
27 static int child_dead = 0;
28
29 static char* login_session = NULL;
30
31 /* true if we're pretty printing json results */
32 static int pretty_print = 1;
33 /* true if we're bypassing 'less' */
34 static int raw_print = 0;
35
36 /* our jabber connection */
37 static transport_client* client = NULL; 
38
39 /* the last result we received */
40 static osrf_message* last_result = NULL;
41
42 /* functions */
43 static int parse_request( char* request );
44
45 /* handles router requests */
46 static int handle_router( char* words[] );
47
48 /* utility method for print time data */
49 /* static int handle_time( char* words[] ); */
50
51 /* handles app level requests */
52 static int handle_request( char* words[], int relay );
53 static int handle_exec(char* words[], int new_shell);
54 static int handle_set( char* words[]);
55 static int handle_print( char* words[]);
56 static int send_request( char* server, 
57                                   char* method, growing_buffer* buffer, int relay );
58 static int parse_error( char* words[] );
59 static int router_query_servers( char* server );
60 //static int srfsh_client_connect();
61 static int print_help();
62 //static char* tabs(int count);
63 static void sig_child_handler( int s );
64 //static void sig_int_handler( int s );
65
66 static int load_history();
67 static int handle_math( char* words[] );
68 static int do_math( int count, int style );
69 static int handle_introspect(char* words[]);
70 static int handle_login( char* words[]);
71
72 static int recv_timeout = 120;
73 static int is_from_script = 0;
74 static FILE* shell_writer = NULL;
75 // static FILE* shell_reader = NULL;
76
77
78 int main( int argc, char* argv[] ) {
79
80         /* --------------------------------------------- */
81         /* see if they have a .srfsh.xml in their home directory */
82         char* home = getenv("HOME");
83         int l = strlen(home) + 36;
84         char fbuf[l];
85         memset(fbuf, 0, l);
86         sprintf(fbuf,"%s/.srfsh.xml",home);
87         
88         if(!access(fbuf, R_OK)) {
89                 if( ! osrf_system_bootstrap_client(fbuf, "srfsh") ) {
90                         fprintf(stderr,"Unable to bootstrap client for requests\n");
91                         osrfLogError( OSRF_LOG_MARK,  "Unable to bootstrap client for requests");
92                         return -1;
93                 }
94
95         } else {
96                 fprintf(stderr,"No Config file found at %s\n", fbuf ); 
97                 return -1;
98         }
99
100         if(argc > 1) {
101                 /* for now.. the first arg is used as a script file for processing */
102                 int f;
103                 if( (f = open(argv[1], O_RDONLY)) == -1 ) {
104                         osrfLogError( OSRF_LOG_MARK, "Unable to open file %s for reading, exiting...", argv[1]);
105                         return -1;
106                 }
107
108                 if(dup2(f, STDIN_FILENO) == -1) {
109                         osrfLogError( OSRF_LOG_MARK, "Unable to duplicate STDIN, exiting...");
110                         return -1;
111                 }
112
113                 close(f);
114                 is_from_script = 1;
115         }
116                 
117         /* --------------------------------------------- */
118         load_history();
119
120
121         client = osrf_system_get_transport_client();
122
123         /* open the shell handle */
124         shell_writer = popen( "bash", "w");
125         //shell_reader = popen( "bash", "r");
126
127         /* main process loop */
128         char* request;
129         while((request=readline(prompt))) {
130
131                 if( !strcasecmp(request, "exit") || !strcasecmp(request,"quit")) 
132                         break; 
133
134                 char* req_copy = strdup(request);
135
136                 parse_request( req_copy ); 
137                 if( request && strlen(request) > 1 ) {
138                         add_history(request);
139                 }
140
141                 free(request);
142                 free(req_copy);
143
144                 fflush(shell_writer);
145                 fflush(stderr);
146                 fflush(stdout);
147         }
148
149         if(history_file != NULL )
150                 write_history(history_file);
151
152         free(request);
153
154         osrf_system_shutdown();
155         return 0;
156 }
157
158 static void sig_child_handler( int s ) {
159         child_dead = 1;
160 }
161
162 /*
163 void sig_int_handler( int s ) {
164         printf("\n");
165         caught_sigint = 1;
166         signal(SIGINT,sig_int_handler);
167 }
168 */
169
170 static int load_history() {
171
172         char* home = getenv("HOME");
173         int l = strlen(home) + 24;
174         char fbuf[l];
175
176         memset(fbuf, 0, l);
177         sprintf(fbuf,"%s/.srfsh_history",home);
178         history_file = strdup(fbuf);
179
180         if(!access(history_file, W_OK | R_OK )) {
181                 history_length = 5000;
182                 read_history(history_file);
183         }
184         return 1;
185 }
186
187
188 static int parse_error( char* words[] ) {
189
190         if( ! words )
191                 return 0;
192
193
194         int i = 0;
195         char* current;
196         char buffer[256];
197         memset(buffer, 0, 256);
198         while( (current=words[i++]) ) {
199                 strcat(buffer, current);
200                 strcat(buffer, " ");
201         }
202         if( ! buffer || strlen(buffer) < 1 ) 
203                 printf("\n");
204
205         fprintf( stderr, "???: %s\n", buffer );
206         return 0;
207
208 }
209
210
211 static int parse_request( char* request ) {
212
213         if( request == NULL )
214                 return 0;
215
216         int ret_val = 0;
217         int i = 0;
218         char* words[COMMAND_BUFSIZE]; 
219         memset(words,0,COMMAND_BUFSIZE);
220         char* req = request;
221
222         char* cur_tok = strtok( req, " " );
223
224         if( cur_tok == NULL )
225                 return 0;
226
227         while(cur_tok != NULL) {
228                 words[i++] = cur_tok;
229                 cur_tok = strtok( NULL, " " );
230         }
231
232
233         // not sure why (strtok?), but this is necessary
234         memset( words + i, 0, COMMAND_BUFSIZE - i );
235
236         /* pass off to the top level command */
237         if( !strcmp(words[0],"router") ) 
238                 ret_val = handle_router( words );
239
240         /*
241         else if( !strcmp(words[0],"time") ) 
242                 ret_val = handle_time( words );
243                 */
244
245         else if (!strcmp(words[0],"request"))
246                 ret_val = handle_request( words, 0 );
247
248         else if (!strcmp(words[0],"relay"))
249                 ret_val = handle_request( words, 1 );
250
251         else if (!strcmp(words[0],"help"))
252                 ret_val = print_help();
253
254         else if (!strcmp(words[0],"set"))
255                 ret_val = handle_set(words);
256
257         else if (!strcmp(words[0],"print"))
258                 ret_val = handle_print(words);
259
260         else if (!strcmp(words[0],"math_bench"))
261                 ret_val = handle_math(words);
262
263         else if (!strcmp(words[0],"introspect"))
264                 ret_val = handle_introspect(words);
265
266         else if (!strcmp(words[0],"login"))
267                 ret_val = handle_login(words);
268
269         else if (words[0][0] == '!')
270                 ret_val = handle_exec( words, 1 );
271
272         if(!ret_val) {
273                 #ifdef EXEC_DEFAULT
274                         return handle_exec( words, 0 );
275                 #else
276                         return parse_error( words );
277                 #endif
278         }
279
280         return 1;
281
282 }
283
284
285 static int handle_introspect(char* words[]) {
286
287         if(words[1] && words[2]) {
288                 fprintf(stderr, "--> %s\n", words[1]);
289                 char buf[256];
290                 memset(buf,0,256);
291                 sprintf( buf, "request %s opensrf.system.method %s", words[1], words[2] );
292                 return parse_request( buf );
293
294         } else {
295         
296                 if(words[1]) {
297                         fprintf(stderr, "--> %s\n", words[1]);
298                         char buf[256];
299                         memset(buf,0,256);
300                         sprintf( buf, "request %s opensrf.system.method.all", words[1] );
301                         return parse_request( buf );
302                 }
303         }
304
305         return 0;
306 }
307
308
309 static int handle_login( char* words[]) {
310
311         if( words[1] && words[2]) {
312
313                 char* username          = words[1];
314                 char* password          = words[2];
315                 char* type                      = words[3];
316                 char* orgloc            = words[4];
317                 char* workstation       = words[5];
318                 int orgloci = (orgloc) ? atoi(orgloc) : 0;
319                 if(!type) type = "opac";
320
321                 char buf[256];
322                 memset(buf,0,256);
323
324                 char buf2[256];
325                 memset(buf2,0,256);
326
327                 sprintf( buf, 
328                                 "request open-ils.auth open-ils.auth.authenticate.init \"%s\"", username );
329                 parse_request(buf); 
330
331                 char* hash;
332                 if(last_result && last_result->_result_content) {
333                         jsonObject* r = last_result->_result_content;
334                         hash = jsonObjectGetString(r);
335                 } else return 0;
336
337
338                 char* pass_buf = md5sum(password);
339
340                 char both_buf[256];
341                 memset(both_buf,0,256);
342                 sprintf(both_buf,"%s%s",hash, pass_buf);
343
344                 char* mess_buf = md5sum(both_buf);
345
346                 /*
347                 sprintf( buf2, "request open-ils.auth open-ils.auth.authenticate.complete "
348                                 "{ \"username\" : \"%s\", \"password\" : \"%s\", "
349                                 "\"type\" : \"%s\", \"org\" : %d, \"workstation\": \"%s\"}", 
350                                 username, mess_buf, type, orgloci, workstation );
351                                 */
352
353                 growing_buffer* argbuf = buffer_init(64);
354                 buffer_fadd(argbuf, 
355                                 "request open-ils.auth open-ils.auth.authenticate.complete "
356                                 "{ \"username\" : \"%s\", \"password\" : \"%s\"", username, mess_buf );
357
358                 if(type) buffer_fadd( argbuf, ", \"type\" : \"%s\"", type );
359                 if(orgloci) buffer_fadd( argbuf, ", \"org\" : %d", orgloci );
360                 if(workstation) buffer_fadd( argbuf, ", \"workstation\" : \"%s\"", workstation);
361                 buffer_add(argbuf, "}");
362
363                 free(pass_buf);
364                 free(mess_buf);
365
366                 parse_request( argbuf->buf );
367                 buffer_free(argbuf);
368
369                 jsonObject* x = last_result->_result_content;
370                 double authtime = 0;
371                 if(x) {
372                         char* authtoken = jsonObjectGetString(
373                                         jsonObjectGetKey(jsonObjectGetKey(x,"payload"), "authtoken"));
374                         authtime  = jsonObjectGetNumber(
375                                         jsonObjectGetKey(jsonObjectGetKey(x,"payload"), "authtime"));
376                         if(authtoken) login_session = strdup(authtoken);
377                         else login_session = NULL;
378                 }
379                 else login_session = NULL;
380
381                 printf("Login Session: %s.  Session timeout: %f\n", login_session, authtime );
382                 
383                 return 1;
384
385         }
386
387         return 0;
388 }
389
390 static int handle_set( char* words[]) {
391
392         char* variable;
393         if( (variable=words[1]) ) {
394
395                 char* val;
396                 if( (val=words[2]) ) {
397
398                         if(!strcmp(variable,"pretty_print")) {
399                                 if(!strcmp(val,"true")) {
400                                         pretty_print = 1;
401                                         printf("pretty_print = true\n");
402                                         return 1;
403                                 } 
404                                 if(!strcmp(val,"false")) {
405                                         pretty_print = 0;
406                                         printf("pretty_print = false\n");
407                                         return 1;
408                                 } 
409                         }
410
411                         if(!strcmp(variable,"raw_print")) {
412                                 if(!strcmp(val,"true")) {
413                                         raw_print = 1;
414                                         printf("raw_print = true\n");
415                                         return 1;
416                                 } 
417                                 if(!strcmp(val,"false")) {
418                                         raw_print = 0;
419                                         printf("raw_print = false\n");
420                                         return 1;
421                                 } 
422                         }
423
424                 }
425         }
426
427         return 0;
428 }
429
430
431 static int handle_print( char* words[]) {
432
433         char* variable;
434         if( (variable=words[1]) ) {
435                 if(!strcmp(variable,"pretty_print")) {
436                         if(pretty_print) {
437                                 printf("pretty_print = true\n");
438                                 return 1;
439                         } else {
440                                 printf("pretty_print = false\n");
441                                 return 1;
442                         }
443                 }
444
445                 if(!strcmp(variable,"login")) {
446                         printf("login session = %s\n", login_session );
447                         return 1;
448                 }
449
450         }
451         return 0;
452 }
453
454 static int handle_router( char* words[] ) {
455
456         if(!client)
457                 return 1;
458
459         int i;
460
461         if( words[1] ) { 
462                 if( !strcmp(words[1],"query") ) {
463                         
464                         if( words[2] && !strcmp(words[2],"servers") ) {
465                                 for(i=3; i < COMMAND_BUFSIZE - 3 && words[i]; i++ ) {   
466                                         router_query_servers( words[i] );
467                                 }
468                                 return 1;
469                         }
470                         return 0;
471                 }
472                 return 0;
473         }
474         return 0;
475 }
476
477
478 /* if new shell, spawn a new child and subshell to do the work,
479         otherwise pipe the request to the currently open (piped) shell */
480 static int handle_exec(char* words[], int new_shell) {
481
482         if(!words[0]) return 0;
483
484         if( words[0] && words[0][0] == '!') {
485                 int len = strlen(words[0]);
486                 char command[len];
487                 memset(command,0,len);
488         
489                 int i; /* chop out the ! */
490                 for( i=1; i!= len; i++) {
491                         command[i-1] = words[0][i];
492                 }
493         
494                 free(words[0]);
495                 words[0] = strdup(command);
496         }
497
498         if(new_shell) {
499                 signal(SIGCHLD, sig_child_handler);
500
501                 if(fork()) {
502         
503                         waitpid(-1, 0, 0);
504                         if(child_dead) {
505                                 signal(SIGCHLD,sig_child_handler);
506                                 child_dead = 0;
507                         }
508         
509                 } else {
510                         execvp( words[0], words );
511                         exit(0);
512                 }
513
514         } else {
515
516
517                 growing_buffer* b = buffer_init(64);
518                 int i = 0;
519                 while(words[i]) 
520                         buffer_fadd( b, "%s ", words[i++] );
521         
522                 buffer_add( b, "\n");
523         
524                 //int reader;
525                 //int reader = dup2(STDOUT_FILENO, reader);
526                 //int reader = dup(STDOUT_FILENO);
527                 //close(STDOUT_FILENO);
528
529                 fprintf( shell_writer, b->buf );
530                 buffer_free(b);
531         
532                 fflush(shell_writer);
533                 usleep(1000);
534
535                 /*
536                 char c[4096];
537                 bzero(c, 4096);
538                 read( reader, c, 4095 );
539                 fprintf(stderr, "read %s", c);
540                 dup2(reader, STDOUT_FILENO);
541                 */
542
543         }
544
545         
546         return 1;
547 }
548
549
550 static int handle_request( char* words[], int relay ) {
551
552         if(!client)
553                 return 1;
554
555         if(words[1]) {
556                 char* server = words[1];
557                 char* method = words[2];
558                 int i;
559                 growing_buffer* buffer = NULL;
560                 if(!relay) {
561                         buffer = buffer_init(128);
562                         buffer_add(buffer, "[");
563                         for(i = 3; words[i] != NULL; i++ ) {
564                                 /* removes trailing semicolon if user accidentally enters it */
565                                 if( words[i][strlen(words[i])-1] == ';' )
566                                         words[i][strlen(words[i])-1] = '\0';
567                                 buffer_add( buffer, words[i] );
568                                 buffer_add(buffer, " ");
569                         }
570                         buffer_add(buffer, "]");
571                 }
572
573                 return send_request( server, method, buffer, relay );
574         } 
575
576         return 0;
577 }
578
579 int send_request( char* server, 
580                 char* method, growing_buffer* buffer, int relay ) {
581         if( server == NULL || method == NULL )
582                 return 0;
583
584         jsonObject* params = NULL;
585         if( !relay ) {
586                 if( buffer != NULL && buffer->n_used > 0 ) 
587                         params = json_parse_string(buffer->buf);
588         } else {
589                 if(!last_result || ! last_result->_result_content) { 
590                         printf("We're not going to call 'relay' with no result params\n");
591                         return 1;
592                 }
593                 else {
594                         jsonObject* o = jsonNewObject(NULL);
595                         jsonObjectPush(o, last_result->_result_content );
596                         params = o;
597                 }
598         }
599
600
601         if(buffer->n_used > 0 && params == NULL) {
602                 fprintf(stderr, "JSON error detected, not executing\n");
603                 return 1;
604         }
605
606         osrf_app_session* session = osrf_app_client_session_init(server);
607
608         if(!osrf_app_session_connect(session)) {
609                 osrfLogWarning( OSRF_LOG_MARK,  "Unable to connect to remote service %s\n", server );
610                 return 1;
611         }
612
613         double start = get_timestamp_millis();
614         //int req_id = osrf_app_session_make_request( session, params, method, 1, NULL );
615         int req_id = osrf_app_session_make_req( session, params, method, 1, NULL );
616
617
618         osrf_message* omsg = osrf_app_session_request_recv( session, req_id, recv_timeout );
619
620         if(!omsg) 
621                 printf("\nReceived no data from server\n");
622         
623         
624         signal(SIGPIPE, SIG_IGN);
625
626         FILE* less; 
627         if(!is_from_script) less = popen( "less -EX", "w");
628         else less = stdout;
629
630         if( less == NULL ) { less = stdout; }
631
632         growing_buffer* resp_buffer = buffer_init(4096);
633
634         while(omsg) {
635
636                 if(raw_print) {
637
638                         if(omsg->_result_content) {
639         
640                                 osrf_message_free(last_result);
641                                 last_result = omsg;
642         
643                                 char* content;
644         
645                                 if( pretty_print && omsg->_result_content ) {
646                                         char* j = jsonObjectToJSON(omsg->_result_content);
647                                         //content = json_printer(j); 
648                                         content = jsonFormatString(j);
649                                         free(j);
650                                 } else
651                                         content = jsonObjectGetString(omsg->_result_content);
652         
653                                 printf( "\nReceived Data: %s\n", content ); 
654                                 free(content);
655         
656                         } else {
657
658                                 char code[16];
659                                 memset(code, 0, 16);
660                                 sprintf( code, "%d", omsg->status_code );
661                                 buffer_add( resp_buffer, code );
662
663                                 printf( "\nReceived Exception:\nName: %s\nStatus: %s\nStatus: %s\n", 
664                                                 omsg->status_name, omsg->status_text, code );
665
666                                 fflush(stdout);
667                         }
668
669                 } else {
670
671                         if(omsg->_result_content) {
672         
673                                 osrf_message_free(last_result);
674                                 last_result = omsg;
675         
676                                 char* content;
677         
678                                 if( pretty_print && omsg->_result_content ) {
679                                         char* j = jsonObjectToJSON(omsg->_result_content);
680                                         //content = json_printer(j); 
681                                         content = jsonFormatString(j);
682                                         free(j);
683                                 } else
684                                         content = jsonObjectGetString(omsg->_result_content);
685         
686                                 buffer_add( resp_buffer, "\nReceived Data: " ); 
687                                 buffer_add( resp_buffer, content );
688                                 buffer_add( resp_buffer, "\n" );
689                                 free(content);
690         
691                         } else {
692         
693                                 buffer_add( resp_buffer, "\nReceived Exception:\nName: " );
694                                 buffer_add( resp_buffer, omsg->status_name );
695                                 buffer_add( resp_buffer, "\nStatus: " );
696                                 buffer_add( resp_buffer, omsg->status_text );
697                                 buffer_add( resp_buffer, "\nStatus: " );
698                                 char code[16];
699                                 memset(code, 0, 16);
700                                 sprintf( code, "%d", omsg->status_code );
701                                 buffer_add( resp_buffer, code );
702                         }
703                 }
704
705
706                 omsg = osrf_app_session_request_recv( session, req_id, recv_timeout );
707
708         }
709
710         double end = get_timestamp_millis();
711
712         fprintf( less, resp_buffer->buf );
713         buffer_free( resp_buffer );
714         fprintf( less, "\n------------------------------------\n");
715         if( osrf_app_session_request_complete( session, req_id ))
716                 fprintf(less, "Request Completed Successfully\n");
717
718
719         fprintf(less, "Request Time in seconds: %.6f\n", end - start );
720         fprintf(less, "------------------------------------\n");
721
722         pclose(less); 
723
724         osrf_app_session_request_finish( session, req_id );
725         osrf_app_session_disconnect( session );
726         osrf_app_session_destroy( session );
727
728
729         return 1;
730
731
732 }
733
734 /*
735 static int handle_time( char* words[] ) {
736
737         if( ! words[1] ) {
738
739                 char buf[36];
740                 memset(buf,0,36);
741                 get_timestamp(buf);
742                 printf( "%s\n", buf );
743                 return 1;
744         }
745
746         if( words[1] ) {
747                 time_t epoch = (time_t)atoi( words[1] );
748                 char* localtime = strdup( ctime( &epoch ) );
749                 printf( "%s => %s", words[1], localtime );
750                 free(localtime);
751                 return 1;
752         }
753
754         return 0;
755
756 }
757 */
758
759                 
760
761 static int router_query_servers( char* router_server ) {
762
763         if( ! router_server || strlen(router_server) == 0 ) 
764                 return 0;
765
766         char rbuf[256];
767         memset(rbuf,0,256);
768         sprintf(rbuf,"router@%s/router", router_server );
769                 
770         transport_message* send = 
771                 message_init( "servers", NULL, NULL, rbuf, NULL );
772         message_set_router_info( send, NULL, NULL, NULL, "query", 0 );
773
774         client_send_message( client, send );
775         message_free( send );
776
777         transport_message* recv = client_recv( client, -1 );
778         if( recv == NULL ) {
779                 fprintf(stderr, "NULL message received from router\n");
780                 return 1;
781         }
782         
783         printf( 
784                         "---------------------------------------------------------------------------------\n"
785                         "Received from 'server' query on %s\n"
786                         "---------------------------------------------------------------------------------\n"
787                         "original reg time | latest reg time | last used time | class | server\n"
788                         "---------------------------------------------------------------------------------\n"
789                         "%s"
790                         "---------------------------------------------------------------------------------\n"
791                         , router_server, recv->body );
792
793         message_free( recv );
794         
795         return 1;
796 }
797
798 static int print_help() {
799
800         printf(
801                         "---------------------------------------------------------------------------------\n"
802                         "Commands:\n"
803                         "---------------------------------------------------------------------------------\n"
804                         "help                   - Display this message\n"
805                         "!<command> [args] - Forks and runs the given command in the shell\n"
806                 /*
807                         "time                   - Prints the current time\n"
808                         "time <timestamp>       - Formats seconds since epoch into readable format\n"   
809                 */
810                         "set <variable> <value> - set a srfsh variable (e.g. set pretty_print true )\n"
811                         "print <variable>               - Displays the value of a srfsh variable\n"
812                         "---------------------------------------------------------------------------------\n"
813
814                         "router query servers <server1 [, server2, ...]>\n"
815                         "       - Returns stats on connected services\n"
816                         "\n"
817                         "\n"
818                         "request <service> <method> [ <json formatted string of params> ]\n"
819                         "       - Anything passed in will be wrapped in a json array,\n"
820                         "               so add commas if there is more than one param\n"
821                         "\n"
822                         "\n"
823                         "relay <service> <method>\n"
824                         "       - Performs the requested query using the last received result as the param\n"
825                         "\n"
826                         "\n"
827                         "math_bench <num_batches> [0|1|2]\n"
828                         "       - 0 means don't reconnect, 1 means reconnect after each batch of 4, and\n"
829                         "                2 means reconnect after every request\n"
830                         "\n"
831                         "introspect <service>\n"
832                         "       - prints the API for the service\n"
833                         "\n"
834                         "\n"
835                         "---------------------------------------------------------------------------------\n"
836                         " Commands for Open-ILS\n"
837                         "---------------------------------------------------------------------------------\n"
838                         "login <username> <password>\n"
839                         "       -       Logs into the 'server' and displays the session id\n"
840                         "       - To view the session id later, enter: print login\n"
841                         "---------------------------------------------------------------------------------\n"
842                         "\n"
843                         "\n"
844                         "Note: long output is piped through 'less'.  To search in 'less', type: /<search>\n"
845                         "---------------------------------------------------------------------------------\n"
846                         "\n"
847                         );
848
849         return 1;
850 }
851
852
853 /*
854 static char* tabs(int count) {
855         growing_buffer* buf = buffer_init(24);
856         int i;
857         for(i=0;i!=count;i++)
858                 buffer_add(buf, "  ");
859
860         char* final = buffer_data( buf );
861         buffer_free( buf );
862         return final;
863 }
864 */
865
866
867 static int handle_math( char* words[] ) {
868         if( words[1] )
869                 return do_math( atoi(words[1]), 0 );
870         return 0;
871 }
872
873
874 static int do_math( int count, int style ) {
875
876         osrf_app_session* session = osrf_app_client_session_init(  "opensrf.math" );
877         osrf_app_session_connect(session);
878
879         jsonObject* params = json_parse_string("[]");
880         jsonObjectPush(params,jsonNewObject("1"));
881         jsonObjectPush(params,jsonNewObject("2"));
882
883         char* methods[] = { "add", "sub", "mult", "div" };
884         char* answers[] = { "3", "-1", "2", "0.500000" };
885
886         float times[ count * 4 ];
887         memset(times,0,count*4);
888
889         int k;
890         for(k=0;k!=100;k++) {
891                 if(!(k%10)) 
892                         fprintf(stderr,"|");
893                 else
894                         fprintf(stderr,".");
895         }
896
897         fprintf(stderr,"\n\n");
898
899         int running = 0;
900         int i;
901         for(i=0; i!= count; i++) {
902
903                 int j;
904                 for(j=0; j != 4; j++) {
905
906                         ++running;
907
908                         double start = get_timestamp_millis();
909                         int req_id = osrf_app_session_make_req( session, params, methods[j], 1, NULL );
910                         osrf_message* omsg = osrf_app_session_request_recv( session, req_id, 5 );
911                         double end = get_timestamp_millis();
912
913                         times[(4*i) + j] = end - start;
914
915                         if(omsg) {
916         
917                                 if(omsg->_result_content) {
918                                         char* jsn = jsonObjectToJSON(omsg->_result_content);
919                                         if(!strcmp(jsn, answers[j]))
920                                                 fprintf(stderr, "+");
921                                         else
922                                                 fprintf(stderr, "\n![%s] - should be %s\n", jsn, answers[j] );
923                                         free(jsn);
924                                 }
925
926
927                                 osrf_message_free(omsg);
928                 
929                         } else { fprintf( stderr, "\nempty message for tt: %d\n", req_id ); }
930
931                         osrf_app_session_request_finish( session, req_id );
932
933                         if(style == 2)
934                                 osrf_app_session_disconnect( session );
935
936                         if(!(running%100))
937                                 fprintf(stderr,"\n");
938                 }
939
940                 if(style==1)
941                         osrf_app_session_disconnect( session );
942         }
943
944         osrf_app_session_destroy( session );
945         jsonObjectFree(params);
946
947         int c;
948         float total = 0;
949         for(c=0; c!= count*4; c++) 
950                 total += times[c];
951
952         float avg = total / (count*4); 
953         fprintf(stderr, "\n      Average round trip time: %f\n", avg );
954
955         return 1;
956 }