int master_workers_capacity(struct jx *j) { int capacity_tasks = jx_lookup_integer(j, "capacity_tasks"); int capacity_cores = jx_lookup_integer(j, "capacity_cores"); int capacity_memory = jx_lookup_integer(j, "capacity_memory"); int capacity_disk = jx_lookup_integer(j, "capacity_disk"); const int cores = resources->cores; const int memory = resources->memory; const int disk = resources->disk; // first, assume one task per worker int capacity = capacity_tasks; // then, enforce tasks per worker if(tasks_per_worker > 0) { capacity = DIV_INT_ROUND_UP(capacity, tasks_per_worker); } // then, enforce capacity per resource if(cores > 0 && capacity_cores > 0) { capacity = MIN(capacity, DIV_INT_ROUND_UP(capacity_cores, cores)); } if(memory > 0 && capacity_memory > 0) { capacity = MIN(capacity, DIV_INT_ROUND_UP(capacity_memory, memory)); } if(disk > 0 && capacity_disk > 0) { capacity = MIN(capacity, DIV_INT_ROUND_UP(capacity_disk, disk)); } return capacity; }
int master_workers_needed_by_resource(struct jx *j) { int tasks_total_cores = jx_lookup_integer(j, "tasks_total_cores"); int tasks_total_memory = jx_lookup_integer(j, "tasks_total_memory"); int tasks_total_disk = jx_lookup_integer(j, "tasks_total_disk"); const int cores = resources->cores; const int memory = resources->memory; const int disk = resources->disk; int needed = 0; if(cores > 0 && tasks_total_cores > 0) { needed = MAX(needed, DIV_INT_ROUND_UP(tasks_total_cores, cores)); } if(memory > 0 && tasks_total_memory > 0) { needed = MAX(needed, DIV_INT_ROUND_UP(tasks_total_memory, memory)); } if(disk > 0 && tasks_total_disk > 0) { needed = MAX(needed, DIV_INT_ROUND_UP(tasks_total_disk, disk)); } return needed; }
static void remove_expired_records() { struct jx *j; char *key; time_t current = time(0); // Only clean every clean_interval seconds. if((current-last_clean_time)<clean_interval) return; // After restarting, all records will have appear to be stale. // Run for a minimum of lifetime seconds before cleaning anything up. if((current-starttime)<lifetime ) return; jx_database_firstkey(table); while(jx_database_nextkey(table, &key, &j)) { time_t lastheardfrom = jx_lookup_integer(j,"lastheardfrom"); int this_lifetime = jx_lookup_integer(j,"lifetime"); if(this_lifetime>0) { this_lifetime = MIN(lifetime,this_lifetime); } else { this_lifetime = lifetime; } if( (current-lastheardfrom) > this_lifetime ) { j = jx_database_remove(table,key); if(j) jx_delete(j); } } last_clean_time = current; }
static void chirp_jx_to_stat(struct jx *j, struct chirp_stat *info) { memset(info, 0, sizeof(*info)); info->cst_atime = info->cst_mtime = info->cst_ctime = jx_lookup_integer(j, "lastheardfrom"); info->cst_size = jx_lookup_integer(j, "total") - jx_lookup_integer(j, "avail"); info->cst_size /= 1024 * 1024; info->cst_mode = S_IFDIR | 0555; }
static int count_workers_needed( struct list *masters_list, int only_waiting ) { int needed_workers=0; int masters=0; struct jx *j; if(!masters_list) { return needed_workers; } list_first_item(masters_list); while((j=list_next_item(masters_list))) { const char *project =jx_lookup_string(j,"project"); const char *host = jx_lookup_string(j,"name"); const int port = jx_lookup_integer(j,"port"); const char *owner = jx_lookup_string(j,"owner"); const int tr = jx_lookup_integer(j,"tasks_on_workers"); const int tw = jx_lookup_integer(j,"tasks_waiting"); const int tl = jx_lookup_integer(j,"tasks_left"); int tasks = tr+tw+tl; // first assume one task per worker int need; if(only_waiting) { need = tw; } else { need = tasks; } // enforce many tasks per worker if(tasks_per_worker > 0) { need = DIV_INT_ROUND_UP(need, tasks_per_worker); } // consider if tasks declared resources... need = MAX(need, master_workers_needed_by_resource(j)); int capacity = master_workers_capacity(j); if(consider_capacity && capacity > 0) { need = MIN(need, capacity); } debug(D_WQ,"%s %s:%d %s %d %d %d",project,host,port,owner,tasks,capacity,need); needed_workers += need; masters++; } return needed_workers; }
static int server_table_load(time_t stoptime) { struct catalog_query *q; struct jx *j; char *key; void *item; if((last_update + update_interval) > time(0)) { return 1; } if(!server_table) { server_table = hash_table_create(0, 0); if(!server_table) return 0; } if(inhibit_catalog_queries) { debug(D_CHIRP, "catalog queries disabled\n"); return 1; } hash_table_firstkey(server_table); while(hash_table_nextkey(server_table, &key, &item)) { hash_table_remove(server_table, key); jx_delete(item); } debug(D_CHIRP, "querying catalog at %s:%d", CATALOG_HOST, CATALOG_PORT); q = catalog_query_create(CATALOG_HOST, CATALOG_PORT, stoptime); if(!q) return 0; while((j = catalog_query_read(q, stoptime))) { char name[CHIRP_PATH_MAX]; const char *type, *hname; int port; type = jx_lookup_string(j, "type"); if(type && !strcmp(type, "chirp")) { hname = jx_lookup_string(j, "name"); if(hname) { port = jx_lookup_integer(j, "port"); if(!port) port = CHIRP_PORT; sprintf(name, "%s:%d", hname, port); hash_table_insert(server_table, name, j); } else { jx_delete(j); } } else { jx_delete(j); } } catalog_query_delete(q); last_update = time(0); return 1; }
static int describe_aws_job(char* aws_jobid, char* env_var){ char* cmd = string_format("aws batch describe-jobs --jobs %s",aws_jobid); struct jx* jx = run_command(cmd); free(cmd); int succeed = DESCRIBE_AWS_JOB_NON_FINAL; //default status struct jx* jobs_array = jx_lookup(jx,"jobs"); if(!jobs_array){ debug(D_BATCH,"Problem with given aws_jobid: %s",aws_jobid); return DESCRIBE_AWS_JOB_NON_EXIST; } struct jx* first_item = jx_array_index(jobs_array,0); if(!first_item){ debug(D_BATCH,"Problem with given aws_jobid: %s",aws_jobid); return DESCRIBE_AWS_JOB_NON_EXIST; } if(strstr((char*)jx_lookup_string(first_item,"status"),"SUCCEEDED")){ succeed = DESCRIBE_AWS_JOB_SUCCESS; } if(strstr((char*)jx_lookup_string(first_item,"status"),"FAILED")){ succeed = DESCRIBE_AWS_JOB_FAILED; } //start and stop if(succeed == DESCRIBE_AWS_JOB_SUCCESS || succeed == DESCRIBE_AWS_JOB_FAILED){ int64_t created_string = (int64_t) jx_lookup_integer(first_item,"createdAt"); int64_t start_string = (int64_t)jx_lookup_integer(first_item,"startedAt"); int64_t end_string = (int64_t)jx_lookup_integer(first_item,"stoppedAt"); if(created_string != 0 ){ debug(D_BATCH,"Job %s was created at: %"PRIi64"",aws_jobid,created_string); } if(start_string != 0 ){ debug(D_BATCH,"Job %s started at: %"PRIi64"",aws_jobid,start_string); } if(end_string != 0 ){ debug(D_BATCH,"Job %s ended at: %"PRIi64"",aws_jobid,end_string); } } jx_delete(jx); return succeed; }
static int count_workers_connected( struct list *masters_list ) { int connected_workers=0; struct jx *j; if(!masters_list) { return connected_workers; } list_first_item(masters_list); while((j=list_next_item(masters_list))) { const int workers = jx_lookup_integer(j,"workers"); connected_workers += workers; } return connected_workers; }
static void make_hash_key(struct jx *j, char *key) { const char *name, *addr; int port; addr = jx_lookup_string(j, "address"); if(!addr) addr = "unknown"; port = jx_lookup_integer(j, "port"); name = jx_lookup_string(j, "name"); if(!name) name = "unknown"; sprintf(key, "%s:%d:%s", addr, port, name); }
static int finished_aws_job_exit_code(char* aws_jobid, char* env_var){ char* cmd = string_format("aws batch describe-jobs --jobs %s",aws_jobid); struct jx* jx = run_command(cmd); free(cmd); struct jx* jobs_array = jx_lookup(jx,"jobs"); if(!jobs_array){ debug(D_BATCH,"Problem with given aws_jobid: %s",aws_jobid); return DESCRIBE_AWS_JOB_NON_EXIST; } struct jx* first_item = jx_array_index(jobs_array,0); if(!first_item){ debug(D_BATCH,"Problem with given aws_jobid: %s",aws_jobid); return DESCRIBE_AWS_JOB_NON_EXIST; } int ret = (int)jx_lookup_integer(first_item,"exitCode"); jx_delete(jx); return ret; }
int main( int argc, char *argv[] ) { int count =0; int first = 0; printf("{\n"); while(1) { struct nvpair *nv = nvpair_create(); int r = nvpair_parse_stream(nv,stdin); if(r) { struct jx *j = nvpair_to_jx(nv); const char *name = jx_lookup_string(j,"name"); const char *host = jx_lookup_string(j,"host"); int port = jx_lookup_integer(j,"port"); if(first) { first = 0; } else { printf(",\n"); } printf("\"%s:%s:%d\":",name,host,port); jx_print_stream(j,stdout); count++; } else if(r<0) { fprintf(stderr,"nvpair conversion error!\n"); } else { break; } } printf("\n}\n"); fprintf(stderr,"%d records converted.\n",count); return 0; }
int main(int argc, char *argv[]) { enum { LONGOPT_SERVER_LASTHEARDFROM = INT_MAX-0, LONGOPT_SERVER_PROJECT = INT_MAX-1, LONGOPT_WHERE = INT_MAX-2, }; static const struct option long_options[] = { {"all", no_argument, 0, 'a'}, {"brief", no_argument, 0, 's'}, {"catalog", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, {"debug-file", required_argument, 0, 'o'}, {"debug-rotate-max", required_argument, 0, 'O'}, {"help", no_argument, 0, 'h'}, {"server-lastheardfrom", required_argument, 0, LONGOPT_SERVER_LASTHEARDFROM}, {"server-project", required_argument, 0, LONGOPT_SERVER_PROJECT}, {"server-space", required_argument, 0, 'A'}, {"timeout", required_argument, 0, 't'}, {"totals", no_argument, 0, 'T'}, {"verbose", no_argument, 0, 'l'}, {"version", no_argument, 0, 'v'}, {"where", required_argument, 0, LONGOPT_WHERE }, {0, 0, 0, 0} }; struct catalog_query *q; struct jx *j; time_t timeout = 60, stoptime; const char *catalog_host = 0; int i; int c; int count = 0; int mode = MODE_TABLE; INT64_T sum_total = 0, sum_avail = 0; const char *filter_name = 0; const char *filter_value = 0; const char *where_expr = "true"; int show_all_types = 0; const char *server_project = NULL; time_t server_lastheardfrom = 0; uint64_t server_avail = 0; debug_config(argv[0]); while((c = getopt_long(argc, argv, "aA:c:d:t:o:O:sTlvh", long_options, NULL)) > -1) { switch (c) { case 'a': show_all_types = 1; break; case 'c': catalog_host = optarg; break; case 'd': debug_flags_set(optarg); break; case 't': timeout = string_time_parse(optarg); break; case 'A': server_avail = string_metric_parse(optarg); break; case 'o': debug_config_file(optarg); break; case 'O': debug_config_file_size(string_metric_parse(optarg)); break; case 'v': cctools_version_print(stdout, argv[0]); return 1; case 's': mode = MODE_SHORT; break; case 'l': mode = MODE_LONG; break; case 'T': mode = MODE_TOTAL; break; case LONGOPT_SERVER_LASTHEARDFROM: server_lastheardfrom = time(0)-string_time_parse(optarg); break; case LONGOPT_SERVER_PROJECT: server_project = xxstrdup(optarg); break; case LONGOPT_WHERE: where_expr = optarg; break; case 'h': default: show_help(argv[0]); return 1; } } cctools_version_debug(D_DEBUG, argv[0]); if(argc - optind == 0) { // fine, keep going } else if((argc - optind) == 1) { filter_name = "name"; filter_value = argv[optind]; } else if((argc - optind) == 2) { filter_name = argv[optind]; filter_value = argv[optind + 1]; } else { show_help(argv[0]); return 1; } stoptime = time(0) + timeout; const char *query_expr; if(show_all_types) { query_expr = where_expr; } else { query_expr = string_format("%s && (type==\"chirp\" || type==\"catalog\")",where_expr); } struct jx *jexpr = jx_parse_string(query_expr); if(!jexpr) { fprintf(stderr,"invalid expression: %s\n",query_expr); return 1; } q = catalog_query_create(catalog_host, 0, jexpr, stoptime); if(!q) { fprintf(stderr, "couldn't query catalog: %s\n", strerror(errno)); return 1; } if(mode == MODE_TABLE) { jx_table_print_header(headers,stdout); } else if(mode==MODE_LONG) { printf("[\n"); } while((j = catalog_query_read(q, stoptime))) { table[count++] = j; } catalog_query_delete(q); qsort(table, count, sizeof(*table), (int (*)(const void *, const void *)) compare_entries); for(i = 0; i < count; i++) { const char *lastheardfrom = jx_lookup_string(table[i], "lastheardfrom"); if (lastheardfrom && (time_t)strtoul(lastheardfrom, NULL, 10) < server_lastheardfrom) continue; const char *avail = jx_lookup_string(table[i], "avail"); if (avail && strtoul(avail, NULL, 10) < server_avail) continue; const char *project = jx_lookup_string(table[i], "project"); if (server_project && (project == NULL || !(strcmp(project, server_project) == 0))) continue; if(filter_name) { const char *v = jx_lookup_string(table[i], filter_name); if(!v || strcmp(filter_value, v)) continue; } if(mode == MODE_SHORT) { const char *t = jx_lookup_string(table[i], "type"); if(t && !strcmp(t, "chirp")) { printf("%s:%d\n", jx_lookup_string(table[i], "name"), (int) jx_lookup_integer(table[i], "port")); } } else if(mode == MODE_LONG) { if(i!=0) printf(",\n"); jx_print_stream(table[i],stdout); } else if(mode == MODE_TABLE) { jx_table_print(headers, table[i], stdout); } else if(mode == MODE_TOTAL) { sum_avail += jx_lookup_integer(table[i], "avail"); sum_total += jx_lookup_integer(table[i], "total"); } } for(i=0;i<count;i++) { jx_delete(table[i]); } if(mode == MODE_TOTAL) { printf("NODES: %4d\n", count); printf("TOTAL: %6sB\n", string_metric(sum_total, -1, 0)); printf("AVAIL: %6sB\n", string_metric(sum_avail, -1, 0)); printf("INUSE: %6sB\n", string_metric(sum_total - sum_avail, -1, 0)); } else if(mode == MODE_TABLE) { jx_table_print_footer(headers,stdout); } else if(mode==MODE_LONG) { printf("\n]\n"); } return 0; }
static void handle_query(struct link *query_link) { FILE *stream; char line[LINE_MAX]; char url[LINE_MAX]; char path[LINE_MAX]; char action[LINE_MAX]; char version[LINE_MAX]; char hostport[LINE_MAX]; char addr[LINK_ADDRESS_MAX]; char key[LINE_MAX]; int port; time_t current; char *hkey; struct jx *j; int i, n; link_address_remote(query_link, addr, &port); debug(D_DEBUG, "www query from %s:%d", addr, port); if(link_readline(query_link, line, LINE_MAX, time(0) + HANDLE_QUERY_TIMEOUT)) { string_chomp(line); if(sscanf(line, "%s %s %s", action, url, version) != 3) { return; } // Consume the rest of the query while(1) { if(!link_readline(query_link, line, LINE_MAX, time(0) + HANDLE_QUERY_TIMEOUT)) { return; } if(line[0] == 0) { break; } } } else { return; } // Output response stream = fdopen(link_fd(query_link), "w"); if(!stream) { return; } link_nonblocking(query_link, 0); current = time(0); fprintf(stream, "HTTP/1.1 200 OK\n"); fprintf(stream, "Date: %s", ctime(¤t)); fprintf(stream, "Server: catalog_server\n"); fprintf(stream, "Connection: close\n"); fprintf(stream, "Access-Control-Allow-Origin: *\n"); if(sscanf(url, "http://%[^/]%s", hostport, path) == 2) { // continue on } else { strcpy(path, url); } /* load the hash table entries into one big array */ n = 0; jx_database_firstkey(table); while(jx_database_nextkey(table, &hkey, &j)) { array[n] = j; n++; } /* sort the array by name before displaying */ qsort(array, n, sizeof(struct jx *), compare_jx); if(!strcmp(path, "/query.text")) { fprintf(stream, "Content-type: text/plain\n\n"); for(i = 0; i < n; i++) jx_export_nvpair(array[i], stream); } else if(!strcmp(path, "/query.json")) { fprintf(stream, "Content-type: text/plain\n\n"); fprintf(stream,"[\n"); for(i = 0; i < n; i++) { jx_print_stream(array[i],stream); if(i<(n-1)) fprintf(stream,",\n"); } fprintf(stream,"\n]\n"); } else if(!strcmp(path, "/query.oldclassads")) { fprintf(stream, "Content-type: text/plain\n\n"); for(i = 0; i < n; i++) jx_export_old_classads(array[i], stream); } else if(!strcmp(path, "/query.newclassads")) { fprintf(stream, "Content-type: text/plain\n\n"); for(i = 0; i < n; i++) jx_export_new_classads(array[i], stream); } else if(!strcmp(path, "/query.xml")) { fprintf(stream, "Content-type: text/xml\n\n"); fprintf(stream, "<?xml version=\"1.0\" standalone=\"yes\"?>\n"); fprintf(stream, "<catalog>\n"); for(i = 0; i < n; i++) jx_export_xml(array[i], stream); fprintf(stream, "</catalog>\n"); } else if(sscanf(path, "/detail/%s", key) == 1) { struct jx *j; fprintf(stream, "Content-type: text/html\n\n"); j = jx_database_lookup(table, key); if(j) { const char *name = jx_lookup_string(j, "name"); if(!name) name = "unknown"; fprintf(stream, "<title>%s catalog server: %s</title>\n", preferred_hostname, name); fprintf(stream, "<center>\n"); fprintf(stream, "<h1>%s catalog server</h1>\n", preferred_hostname); fprintf(stream, "<h2>%s</h2>\n", name); fprintf(stream, "<p><a href=/>return to catalog view</a><p>\n"); jx_export_html_solo(j, stream); fprintf(stream, "</center>\n"); } else { fprintf(stream, "<title>%s catalog server</title>\n", preferred_hostname); fprintf(stream, "<center>\n"); fprintf(stream, "<h1>%s catalog server</h1>\n", preferred_hostname); fprintf(stream, "<h2>Unknown Item!</h2>\n"); fprintf(stream, "</center>\n"); } } else { char avail_line[LINE_MAX]; char total_line[LINE_MAX]; INT64_T sum_total = 0; INT64_T sum_avail = 0; INT64_T sum_devices = 0; fprintf(stream, "Content-type: text/html\n\n"); fprintf(stream, "<title>%s catalog server</title>\n", preferred_hostname); fprintf(stream, "<center>\n"); fprintf(stream, "<h1>%s catalog server</h1>\n", preferred_hostname); fprintf(stream, "<a href=/query.text>text</a> - "); fprintf(stream, "<a href=/query.html>html</a> - "); fprintf(stream, "<a href=/query.xml>xml</a> - "); fprintf(stream, "<a href=/query.json>json</a> - "); fprintf(stream, "<a href=/query.oldclassads>oldclassads</a> - "); fprintf(stream, "<a href=/query.newclassads>newclassads</a>"); fprintf(stream, "<p>\n"); for(i = 0; i < n; i++) { j = array[i]; sum_total += jx_lookup_integer(j, "total"); sum_avail += jx_lookup_integer(j, "avail"); sum_devices++; } string_metric(sum_avail, -1, avail_line); string_metric(sum_total, -1, total_line); fprintf(stream, "<b>%sB available out of %sB on %d devices</b><p>\n", avail_line, total_line, (int) sum_devices); jx_export_html_header(stream, html_headers); for(i = 0; i < n; i++) { j = array[i]; make_hash_key(j, key); sprintf(url, "/detail/%s", key); jx_export_html_with_link(j, stream, html_headers, "name", url); } jx_export_html_footer(stream, html_headers); fprintf(stream, "</center>\n"); } fclose(stream); }
static void handle_update( const char *addr, int port, const char *raw_data, int raw_data_length, const char *protocol ) { char key[LINE_MAX]; unsigned long data_length; struct jx *j; // If the packet starts with Control-Z (0x1A), it is compressed, // so uncompress it to data[]. Otherwise just copy to data[];. if(raw_data[0]==0x1A) { data_length = sizeof(data); int success = uncompress((Bytef*)data,&data_length,(const Bytef*)&raw_data[1],raw_data_length-1); if(success!=Z_OK) { debug(D_DEBUG,"warning: %s:%d sent invalid compressed data (ignoring it)\n",addr,port); return; } } else { memcpy(data,raw_data,raw_data_length); data_length = raw_data_length; } // Make sure the string data is null terminated. data[data_length] = 0; // Once uncompressed, if it starts with a bracket, // then it is JX/JSON, otherwise it is the legacy nvpair format. if(data[0]=='{') { j = jx_parse_string(data); if(!j) { debug(D_DEBUG,"warning: %s:%d sent invalid JSON data (ignoring it)\n%s\n",addr,port,data); return; } if(!jx_is_constant(j)) { debug(D_DEBUG,"warning: %s:%d sent non-constant JX data (ignoring it)\n%s\n",addr,port,data); jx_delete(j); return; } } else { struct nvpair *nv = nvpair_create(); if(!nv) return; nvpair_parse(nv, data); j = nvpair_to_jx(nv); nvpair_delete(nv); } jx_insert_string(j, "address", addr); jx_insert_integer(j, "lastheardfrom", time(0)); /* If the server reports unbelievable numbers, simply reset them */ if(max_server_size > 0) { INT64_T total = jx_lookup_integer(j, "total"); INT64_T avail = jx_lookup_integer(j, "avail"); if(total > max_server_size || avail > max_server_size) { jx_insert_integer(j, "total", max_server_size); jx_insert_integer(j, "avail", max_server_size); } } /* Do not believe the server's reported name, just resolve it backwards. */ char name[DOMAIN_NAME_MAX]; if(domain_name_cache_lookup_reverse(addr, name)) { /* Special case: Prior bug resulted in multiple name entries in logged data. When removing the name property, keep looking until all items are removed. */ struct jx *jname = jx_string("name"); struct jx *n; while((n=jx_remove(j,jname))) { jx_delete(n); } jx_delete(jname); jx_insert_string(j,"name",name); } else if (jx_lookup_string(j, "name") == NULL) { /* If rDNS is unsuccessful, then we use the name reported if given. * This allows for hostnames that are only valid in the subnet of * the reporting server. Here we set the "name" field to the IP * Address, addr, because it was not set by the reporting server. */ jx_insert_string(j, "name", addr); } make_hash_key(j, key); if(logfile) { if(!jx_database_lookup(table,key)) { jx_print_stream(j,logfile); fprintf(logfile,"\n"); fflush(logfile); } } jx_database_insert(table, key, j); debug(D_DEBUG, "received %s update from %s",protocol,key); }
/* * Obtains information from the Catalog, format it, and make return it to user. */ int main(int argc, char** argv) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"project", required_argument, 0, 'N'}, {"server", required_argument, 0, 's'}, {"timeout", required_argument, 0, 't'}, {"username", required_argument, 0, 'u'}, {0, 0, 0, 0} }; struct catalog_query *q; struct jx *j; int c; unsigned int i; time_t timeout = 60; char* catalog_host = NULL; char* username = NULL; char* project = NULL; char* server = NULL; while ((c = getopt_long(argc, argv, "N:t:u:w:s:h", long_options, NULL)) > -1) { switch (c) { case 'N': project = xxstrdup(optarg); break; case 't': timeout = string_time_parse(optarg); break; case 'u': username = xxstrdup(optarg); break; case 's': server = xxstrdup(optarg); break; case 'h': default: show_help(argv[0]); return 1; } } //setup address if(server==NULL){ catalog_host = CATALOG_HOST; } //make query struct jx *jexpr = jx_operator( JX_OP_EQ, jx_symbol("type"), jx_string("makeflow") ); if (project) { jexpr = jx_operator( JX_OP_AND, jexpr, jx_operator( JX_OP_EQ, jx_symbol("project"), jx_string(project) ) ); } if (username) { jexpr = jx_operator( JX_OP_AND, jexpr, jx_operator( JX_OP_EQ, jx_symbol("username"), jx_string(username) ) ); } time_t stoptime = time(0) + timeout; unsigned int count = 0; //create catalog_query from jx q = catalog_query_create(catalog_host, jexpr, stoptime); if (!q) { fprintf(stderr, "couldn't query catalog: %s\n", strerror(errno)); return 1; } while ((j = catalog_query_read(q, stoptime))) { table[count++] = j; } catalog_query_delete(q);//all done with connection //sort qsort(table, count, sizeof(*table), (int (*)(const void *, const void *)) compare_entries); //print them out printf("%-10s %-18s %6s %6s %6s %6s %6s %6s %6s\n", "OWNER", "PROJECT", "JOBS", "WAIT", "RUN", "COMP", "ABRT", "FAIL", "TYPE"); for(i=0; i<count; i++){ printf("%-10s %-18s %6" PRId64 " %6" PRId64 " %6" PRId64 " %6" PRId64 " %6" PRId64 " %6" PRId64 " %6s\n", jx_lookup_string(table[i], "owner"), jx_lookup_string(table[i], "project"), jx_lookup_integer(table[i], "total"), jx_lookup_integer(table[i], "waiting"), jx_lookup_integer(table[i], "running"), jx_lookup_integer(table[i], "completed"), jx_lookup_integer(table[i], "aborted"), jx_lookup_integer(table[i], "failed"), jx_lookup_string(table[i], "batch_type") ); } printf("\n");//be polite //cleanup for(i=0;i<count;i++) { jx_delete(table[i]); } //done return (EXIT_SUCCESS); }