diff --git a/clientside/protogeni/geni-get b/clientside/protogeni/geni-get index a9660821bc5993b9b8a3e84a76359cd36c7c9161..e9af82ada8292403d1b3cce29334aa52e7890ac4 100755 --- a/clientside/protogeni/geni-get +++ b/clientside/protogeni/geni-get @@ -47,6 +47,17 @@ def usage(): print " -n, --no-cache disable reading cached results" print " -p, --port specify server port" print " -v, --version display server version" + print "" + print "Try \"" + sys.argv[ 0 ] + " commands\" for a list of supported commands." + +def dump( sock ): + while True: + buf = sock.recv( 0x10000 ) + if not buf: + break + sys.stdout.write( buf ) + + sock.close() try: opts, args = getopt.getopt( sys.argv[ 1: ], "achnp:v", [ "all", "client-version", "help", "no-cache", "port=", "version" ] ) @@ -62,7 +73,7 @@ for opt, param in opts: if opt in ( "-a", "--all" ): command = "all" elif opt in ( "-c", "--client-version" ): - print "1" + print "1.1" sys.exit( 0 ) elif opt in ( "-h", "--help" ): usage() @@ -99,10 +110,32 @@ sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) sock.connect( (server, port) ) sock.send( "geni_" + command ) -while True: - buf = sock.recv( 0x10000 ) - if not buf: - break - print buf, +firstchar = sock.recv( 1 ) +if not firstchar: + print >> sys.stderr, sys.argv[ 0 ] + ": unexpected EOF from server" + sys.exit( 1 ) + +if firstchar != "\x00": + # old protocol -- just dump everything + sys.stdout.write( firstchar ) + dump( sock ) + sys.exit( 0 ) + +nextchar = sock.recv( 1 ) +if not nextchar: + print >> sys.stderr, sys.argv[ 0 ] + ": unexpected EOF from server" + sys.exit( 1 ) + +if nextchar == "\x00": + # error from server + sys.stderr.write( sys.argv[ 0 ] + ": " + command + ": " ) + while True: + buf = sock.recv( 0x10000 ) + if not buf: + sock.close() + sys.exit( 1 ) + sys.stderr.write( buf ) -sock.close() +# new protocol, success +sys.stdout.write( nextchar ) +dump( sock ) diff --git a/tmcd/tmcd.c b/tmcd/tmcd.c index 190fd1b9b69dddda641bf96c5fe6a1de05661810..4991e3f5a94bbeb330552039d76305abc35d0517 100644 --- a/tmcd/tmcd.c +++ b/tmcd/tmcd.c @@ -365,7 +365,10 @@ COMMAND_PROTOTYPE(dogenicontrolmac); COMMAND_PROTOTYPE(dogeniversion); COMMAND_PROTOTYPE(dogenigetversion); COMMAND_PROTOTYPE(dogenisliverstatus); +COMMAND_PROTOTYPE(dogenistatus); +COMMAND_PROTOTYPE(dogenicommands); COMMAND_PROTOTYPE(dogeniall); +COMMAND_PROTOTYPE(dogeniinvalid); #endif /* @@ -493,7 +496,12 @@ struct command { { "geni_version", FULLCONFIG_NONE, 0, dogeniversion }, { "geni_getversion", FULLCONFIG_NONE, 0, dogenigetversion }, { "geni_sliverstatus", FULLCONFIG_NONE, 0, dogenisliverstatus }, + { "geni_status", FULLCONFIG_NONE, 0, dogenistatus }, + { "geni_commands", FULLCONFIG_NONE, 0, dogenicommands }, { "geni_all", FULLCONFIG_NONE, 0, dogeniall }, + /* A rather ugly hack to avoid making error handling a special case. + THIS MUST BE THE LAST ENTRY IN THE ARRAY! */ + { "geni_invalid", FULLCONFIG_NONE, 0, dogeniinvalid } #endif }; static int numcommands = sizeof(command_array)/sizeof(struct command); @@ -1358,8 +1366,14 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp) break; if (i == numcommands) { - info("%s: INVALID REQUEST: %.8s\n", reqp->nodeid, bp); - goto skipit; + if( !strncmp( bp, "geni_", 5 ) ) + /* Any invalid command with a GENI prefix is treated + as geni_invalid. */ + i = numcommands - 1; + else { + info("%s: INVALID REQUEST: %.8s\n", reqp->nodeid, bp); + goto skipit; + } } /* @@ -11454,13 +11468,19 @@ static int dogeni( int sock, tmcdreq_t *reqp, int tcp, char *result = func( reqp ); if( result ) { + client_writeback( sock, "", 1, tcp ); /* single NUL */ client_writeback( sock, result, strlen( result ), tcp ); client_writeback( sock, "\n", 1, tcp ); free( result ); return 0; - } else + } else { + static char error_msg[] = "\0\0internal error handling request\n"; + + client_writeback( sock, error_msg, sizeof error_msg - 1, tcp ); + return 1; + } } static char *geni_append( char *buf, char *buf_end, char *p ) { @@ -11869,15 +11889,16 @@ static char *getgenigetversion( tmcdreq_t *reqp ) { if( mysql_num_rows( res ) ) { MYSQL_ROW row = mysql_fetch_row( res ); - GOUTPUT( buf, sizeof buf, "{'code_tag':'%s'," - "'urn':'urn:publicid:IDN+" OURDOMAIN "+authority+cm'," - "'url':'" TBBASE ":12369/protogeni/xmlrpc/am'," - "'geni_am_type':'protogeni'," - "'geni_single_allocation':true," - "'geni_allocate':'geni_disjoint'," - "'geni_credential_types':{" - "'geni_type':'geni_sfa','geni_version':'2'," - "'geni_type':'geni_sfa','geni_version':'3'}}", + GOUTPUT( buf, sizeof buf, "{\"code_tag\":\"%s\"," + "\"urn\":\"urn:publicid:IDN+" OURDOMAIN + "+authority+cm\"," + "\"url\":\"" TBBASE ":12369/protogeni/xmlrpc/am\"," + "\"geni_am_type\":\"protogeni\"," + "\"geni_single_allocation\":true," + "\"geni_allocate\":\"geni_disjoint\"," + "\"geni_credential_types\":{" + "\"geni_type\":\"geni_sfa\",\"geni_version\":\"2\"," + "\"geni_type\":\"geni_sfa\",\"geni_version\":\"3\"}}", row[ 0 ] ); } @@ -11913,9 +11934,9 @@ static char *getgenisliverstatus( tmcdreq_t *reqp ) { status = strcmp( row[ 1 ], "ready" ) && strcmp( row[ 1 ], "failed" ) ? "unknown" : row[ 1 ]; - p = buf + snprintf( buf, sizeof buf, "{'geni_urn':'urn:publicid:IDN+" - OURDOMAIN "+sliver+%s','geni_status':'%s'," - "'geni_resources':[", row[ 0 ], status ); + p = buf + snprintf( buf, sizeof buf, "{\"geni_urn\":\"urn:publicid:" + "IDN+" OURDOMAIN "+sliver+%s\",\"geni_status\"" + ":\"%s\",\"geni_resources\":[", row[ 0 ], status ); mysql_free_result( res ); @@ -11932,9 +11953,9 @@ static char *getgenisliverstatus( tmcdreq_t *reqp ) { } while( ( row = mysql_fetch_row( res ) ) ) { - p += snprintf( p, buf + sizeof buf - p, "%s{'geni_urn':" - "'urn:publicid:IDN+" OURDOMAIN "+sliver+%s'," - "'geni_status':'%s','geni_error':'%s'}", + p += snprintf( p, buf + sizeof buf - p, "%s{\"geni_urn\":" + "\"urn:publicid:IDN+" OURDOMAIN "+sliver+%s\"," + "\"geni_status\":\"%s\",\"geni_error\":\"%s\"}", first ? "" : ",", row[ 0 ], row[ 1 ], row[ 2 ] ? row[ 2 ] : "" ); first = 0; @@ -11950,6 +11971,73 @@ static char *getgenisliverstatus( tmcdreq_t *reqp ) { return strdup( buf ); } +/* This is "status", which returns the status of the sliver. Not to be + confused with "sliverstatus", which returns the status of the sliver + slightly differently. Hands up if you like design by committee! */ +static char *getgenistatus( tmcdreq_t *reqp ) { + + MYSQL_RES *res; + MYSQL_ROW row; + char buf[ 0x4000 ], *p, expires[ 0x40 ]; + int first = 1; + + res = mydb_query( "SELECT c.urn, s.expires FROM " + "`geni-cm`.geni_certificates AS c, " + "`geni-cm`.geni_slices AS s, " + "`geni-cm`.geni_slivers AS l WHERE " + "l.resource_uuid = '%s' AND " + "c.uuid = l.slice_uuid AND " + "s.uuid = l.slice_uuid", 2, reqp->nodeuuid ); + + if( !res || !mysql_num_rows( res ) ) { + error( "geni_sliverstatus: %s: DB error getting aggregate!\n", + reqp->nodeid ); + return NULL; + } + + row = mysql_fetch_row( res ); + + p = buf + snprintf( buf, sizeof buf, "{\"geni_urn\":\"%s\"," + "geni_slivers\":[", row[ 0 ] ); + strcpy( expires, row[ 1 ] ); + + mysql_free_result( res ); + + res = mydb_query( "SELECT x.idx, x.status, x.errorlog FROM " + "`geni-cm`.geni_slivers AS x, " + "`geni-cm`.geni_slivers AS y WHERE " + "x.aggregate_uuid = y.aggregate_uuid AND " + "y.resource_uuid = '%s'", 3, reqp->nodeuuid ); + + if( !res || !mysql_num_rows( res ) ) { + error( "geni_sliverstatus: %s: DB error getting sliver!\n", + reqp->nodeid ); + return NULL; + } + + while( ( row = mysql_fetch_row( res ) ) ) { + p += snprintf( p, buf + sizeof buf - p, "%s{\"geni_urn\":" + "\"urn:publicid:IDN+" OURDOMAIN "+sliver+%s\"," + "\"geni_expires\":\"%s\"," + "\"geni_allocation_status\":" + "\"geni_provisioned\"," + "\"geni_operational_status\":\"geni_%s\"," + "\"geni_error\":\"%s\"}", + first ? "" : ",", row[ 0 ], expires, + row[ 1 ], row[ 2 ] ? row[ 2 ] : "" ); + first = 0; + } + + mysql_free_result( res ); + + geni_append( p, buf + sizeof buf, "]}" ); + + if( verbose ) + info( "%s: geni_sliverstatus: %s", reqp->nodeid, buf ); + + return strdup( buf ); +} + #define MAKEGENICOMMAND( cmd ) \ COMMAND_PROTOTYPE( dogeni ## cmd ) { \ return dogeni( sock, reqp, tcp, getgeni ## cmd ); \ @@ -11966,55 +12054,125 @@ MAKEGENICOMMAND(controlmac) MAKEGENICOMMAND(version) MAKEGENICOMMAND(getversion) MAKEGENICOMMAND(sliverstatus) +MAKEGENICOMMAND(status) + +struct genicommand { + char *tag; + char *( *func )( tmcdreq_t * ); + int quote; + char *desc; +} genicommands[] = { + { "all", NULL, 0, NULL }, + { "client_id", getgeniclientid, 1, "Return the client-specified " + "sliver ID" }, + { "commands", NULL, 0, "Show all available commands" }, + { "control_mac", getgenicontrolmac, 1, "Show the MAC address of the " + "control interface" }, + { "geni_user", getgenigeniuser, 0, "Show user accounts and public keys" }, + { "getversion", getgenigetversion, 0, "Report the AM version" }, + { "manifest", getgenimanifest, 1, "Show the sliver manifest" }, + { "slice_email", getgenisliceemail, 1, "Retrieve the e-mail address from " + "the slice certificate" }, + { "slice_urn", getgenisliceurn, 1, "Show the URN of the slice" }, + { "sliverstatus", getgenisliverstatus, 0, "Give the current status of " + "the sliver (AM API v2)" }, + { "status", getgenistatus, 0, "Give the current status of " + "the sliver (AM API v3)" }, + { "user_email", getgeniuseremail, 1, "Show the e-mail address of the " + "sliver creator" }, + { "user_urn", getgeniuserurn, 1, "Show the URN of the sliver creator" }, + { "version", getgeniversion, 1, NULL } +}; + +COMMAND_PROTOTYPE(dogenicommands) +{ + char buf[ 0x4000 ], *p; + int i, maxlen, first = 1; + + buf[ 0 ] = 0; /* NUL */ + buf[ 1 ] = '{'; + buf[ 2 ] = '\n'; + p = buf + 3; + + for( i = 0, maxlen = 0; i < sizeof genicommands / sizeof *genicommands; + i++ ) { + char *tag = genicommands[ i ].tag; + int len; + + if( tag && ( len = strlen( tag ) ) > maxlen ) + maxlen = len; + } + + for( i = 0; i < sizeof genicommands / sizeof *genicommands; i++ ) { + char *desc = genicommands[ i ].desc; + + if( desc ) { + if( first ) + first = 0; + else + p = geni_append( p, buf + sizeof buf, ",\n" ); + + p += snprintf( p, buf + sizeof buf - p, + " \"%s\":%*s\"%s\"", + genicommands[ i ].tag, + maxlen + 1 - strlen( genicommands[ i ].tag ), "", + genicommands[ i ].desc ); + } + } + + geni_append( p, buf + sizeof buf, "\n}\n" ); + + client_writeback( sock, buf, 1 + strlen( buf + 1 ), tcp ); + + return 0; +} COMMAND_PROTOTYPE(dogeniall) { /* Glob all the other stuff into a JSON structure. Hey, at least it's not XML! */ char buf[ 0x4000 ], *p; - struct work { - char *tag; - char *( *func )( tmcdreq_t * ); - int quote; - } work[] = { - { "client_id", getgeniclientid, 1 }, - { "slice_urn", getgenisliceurn, 1 }, - { "slice_email", getgenisliceemail, 1 }, - { "user_urn", getgeniuserurn, 1 }, - { "user_email", getgeniuseremail, 1 }, - { "geni_user", getgenigeniuser, 0 }, - { "manifest", getgenimanifest, 1 }, - { "control_mac", getgenicontrolmac, 1 }, - { "version", getgeniversion, 1 }, - { "getversion", getgenigetversion, 0 }, - { "sliverstatus", getgenisliverstatus, 0 } - }; - int i; - - p = geni_append( buf, buf + sizeof buf, "{" ); + int i, first = 1; + + buf[ 0 ] = 0; /* NUL */ + buf[ 1 ] = '{'; + p = buf + 2; - for( i = 0; i < sizeof work / sizeof *work; i++ ) { - char *val = work[ i ].func( reqp ); + for( i = 0; i < sizeof genicommands / sizeof *genicommands; i++ ) { + char *( *f )( tmcdreq_t * ) = genicommands[ i ].func; + char *val; - if( !val ) + if( !f || !( val = f( reqp ) ) ) continue; - if( i ) + if( first ) + first = 0; + else p = geni_append( p, buf + sizeof buf, "," ); - p = geni_quote( p, buf + sizeof buf, work[ i ].tag ); + p = geni_quote( p, buf + sizeof buf, genicommands[ i ].tag ); p = geni_append( p, buf + sizeof buf, ":" ); - p = ( work[ i ].quote ? geni_quote : geni_append )( p, buf + sizeof - buf, val ); + p = ( genicommands[ i ].quote ? geni_quote : + geni_append )( p, buf + sizeof buf, val ); + free( val ); } geni_append( p, buf + sizeof buf, "}\n" ); - client_writeback( sock, buf, strlen( buf ), tcp ); + client_writeback( sock, buf, 1 + strlen( buf + 1 ), tcp ); return 0; } + +COMMAND_PROTOTYPE(dogeniinvalid) +{ + static char error_msg[] = "\0\0unknown request\n"; + + client_writeback( sock, error_msg, sizeof error_msg - 1, tcp ); + + return 1; +} #endif /*