#include #include #include #include /* #------------------------------------------------------------------------# # Copyright 2021, Jesse Gardner # #------------------------------------------------------------------------# # This file is part of qq2clone. # # # # qq2clone is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 2 of the License, or # # (at your option) any later version. # # # # qq2clone is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with qq2clone. If not, see . # #------------------------------------------------------------------------# */ void pr_bad(); void pr_conn_bad(); void pr_conn_good(); void pr_fatal_and_exit(int e); void pr_good(); void pr_no_match(); int get_domain(char *uuid, virDomainPtr *domain); char * get_state_string(virDomainPtr dom, int state); void lv_get_name(char *uuid); void lv_get_state(char *uuid); void lv_get_xml(char *uuid); void lv_list_all_name(); void lv_list_all_uuid(); const int E_LIBVIRT=16; virConnectPtr conn; /*One of these is displayed on startup. If CONN_BAD, program exits*/ const char CONN_BAD[]= "# No Connection"; const char CONN_GOOD[]="# Connected"; /*After a command is issued, one of these displays. Only GOOD is followed by the requested information, then EOF */ const char BAD_REQ[]="# Bad Request"; const char FATAL[]= "# Fatal error"; const char GOOD[]= "# Making API Request"; const char NOMATCH[]="# No matching domain"; /************************************************************************/ /* Possible commands: * list: list UUIDs of all domains that are defined and/or running * list_name: list names of all domains that are defined and/or running * get_name : get name of domain * get_state : get state of domain as defined in man qq2clone. It * is possible, though unlikely for this to return an empty string * even when referring to a valid uuid * get_xml : print xml file describing domain (prints current * config of a running machine, rather than inactive config) */ int main () { setlinebuf(stdout); /* Make sure output is always line buffered */ conn=virConnectOpen(NULL); /* Connect to libvirt API */ if ( conn == NULL) { pr_conn_bad(); exit(E_LIBVIRT); } else { pr_conn_good(); } char *lineptr, *token, **str_arr; const char delim[]="\t\n "; unsigned int count; int i; size_t n; /* Get input from stdin and execute commands in a loop */ while(1) { str_arr=NULL; lineptr=NULL; n=0; count=0; getline(&lineptr,&n,stdin); token=strtok(lineptr,delim); if (token == NULL) { free(lineptr); pr_bad(); continue; } while (token != NULL) { str_arr=realloc(str_arr, sizeof(char*) * (count + 1) ); if (str_arr == NULL){ pr_fatal_and_exit(1); } str_arr[count]=token; count++; token=strtok(NULL,delim); } if ( count > 2 || count == 0) { free(lineptr); pr_bad(); continue; } if ( strcmp( str_arr[0], "exit" ) == 0 ) { virConnectClose(conn); exit(0); } else if (strcmp( str_arr[0], "list" ) == 0 ) { if (count==1){ lv_list_all_uuid(); } else { pr_bad(); } } else if (strcmp( str_arr[0], "list_name" ) == 0 ) { if (count==1){ lv_list_all_name(); } else { pr_bad(); } } else if (strcmp( str_arr[0], "get_name" ) == 0 ) { if (count==2){ lv_get_name(str_arr[1]); } else { pr_bad(); } } else if (strcmp( str_arr[0], "get_state" ) == 0 ) { if (count==2){ lv_get_state(str_arr[1]); } else { pr_bad(); } } else if (strcmp( str_arr[0], "get_xml" ) == 0 ) { if (count==2){ lv_get_xml(str_arr[1]); } else { pr_bad(); } } else { pr_bad(); } free(lineptr); if (str_arr != NULL){ free(str_arr); } } exit(0); } /************************************************************************/ /*********************************** * Basic, repeated output sequences * ***********************************/ void pr_bad() { printf("%s\n",BAD_REQ); } void pr_conn_bad() { printf("%s\n", CONN_BAD); } void pr_conn_good() { printf("%s\n",CONN_GOOD); } /* Okay, this one provides a basic output sequence *and* quits */ void pr_fatal_and_exit(int e) { printf("%s\n",FATAL); virConnectClose; exit(e); } void pr_good() { printf("%s\n",GOOD); } void pr_no_match() { printf("%s\n",NOMATCH); } /***************************************** * Helper functions for working with API * *****************************************/ int get_domain(char *uuid, virDomainPtr *domain){ *domain=virDomainLookupByUUIDString(conn, (const char *) uuid); if (domain == NULL){ return 1; } return 0; } char * get_state_string(virDomainPtr dom, int state) { if (state == VIR_DOMAIN_RUNNING) { return ("running"); } else if (state == VIR_DOMAIN_BLOCKED) { return ("idle"); } else if ( state == VIR_DOMAIN_PAUSED ) { return ("paused"); } else if ( state == VIR_DOMAIN_SHUTDOWN ) { return("shutting-down"); } else if ( state == VIR_DOMAIN_SHUTOFF ) { int saved; saved=virDomainHasManagedSaveImage(dom,0); if( saved == 1 ) { return("saved"); } else if ( saved == 0 ) { return("off"); } else { return(NULL); } } else if ( state == VIR_DOMAIN_CRASHED ) { return("crashed"); } else if ( state == VIR_DOMAIN_PMSUSPENDED ) { return ("pmsuspended"); } else { return(""); } } /******************************************************* * Get information from libvirt API and print to stdout * *******************************************************/ void lv_get_name (char *uuid){ virDomainPtr domain; if (get_domain(uuid, &domain) == 0){ pr_good(); const char *name; name=virDomainGetName(domain); if (name!=NULL){ printf("+%s\n",name); } else { pr_fatal_and_exit(E_LIBVIRT); } virDomainFree(domain); printf("EOF\n"); }else{ pr_no_match(); } } void lv_get_state(char *uuid){ virDomainPtr domain; if (get_domain(uuid, &domain) == 0){ pr_good(); int *state=malloc(sizeof(state)), *reason=malloc(sizeof(reason)), ret; ret=virDomainGetState(domain,state,reason,0); if ( ret==0 ){ const char *state_str; state_str=get_state_string(domain, *state); printf("+%s\n",state_str); } else { printf("+\n"); } virDomainFree(domain); free(state); free(reason); printf("EOF\n"); }else{ pr_no_match(); } } void lv_get_xml(char *uuid){ virDomainPtr domain; if (get_domain(uuid, &domain) == 0){ pr_good(); char *xml; xml=virDomainGetXMLDesc(domain, 0); if ( xml!=NULL ){ char *token; token=strtok(xml, "\n"); if(token==NULL) { free(xml); virDomainFree(domain); return; } while (token != NULL) { printf ("+%s\n",token); token=strtok(NULL, "\n"); } free(xml); } else { pr_fatal_and_exit(E_LIBVIRT); } virDomainFree(domain); printf("EOF\n"); } else { pr_no_match(); } } void lv_list_all_uuid() { virDomainPtr *domains; int ret_list, ret_uuid, i; ret_list=virConnectListAllDomains(conn,&domains,0); if(ret_list<0){ virConnectClose(conn); pr_fatal_and_exit(E_LIBVIRT); }; pr_good(); char *uuid=malloc(VIR_UUID_STRING_BUFLEN); for(i=0;i