/* zentific-poll: statistics collection daemon for Zentific * Copyright (C) 2007, 2008 * Steven Maresca, Justin Demaris, * and Zentific LLC * * All rights reserved. * Use is subject to license terms. * * Please visit http://zentific.com for news and updates * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. */ //zentific includes #include "zentific-poll.h" //------------GLOBAL VARIABLES-------------- static struct timeval curtime, oldtime; static xenstat_handle *xhandle = NULL; static xenstat_node *prev_node = NULL; static xenstat_node *node = NULL; int RELOAD_CONFIG = 0; // flag to check if we need to reload the config. Triggered by SIGHUP int DEBUG = 0; int XML_DEBUG = 0; int REGISTERED = 0; int DELAY = DEFAULT_DELAY; int SEND_DATA = 1; int USE_SSL = 0; int PORT = 80; char XMLRPC_PATH[PATH_MAX+1] = DEFAULT_XMLRPC_PATH; char BACKEND_HOST[HOST_NAME_MAX+1] = DEFAULT_BACKEND_HOST; char CONFIG_FILE[PATH_MAX+1] = DEFAULT_CONFIG_FILE; char PID_FILE[PATH_MAX+1] = DEFAULT_PID_FILE; char LOG_FILE[PATH_MAX+1] = DEFAULT_LOG_FILE; char LOG_TYPE[128] = DEFAULT_LOG_TYPE; char *LOG_PREFIX = "Zentific-poll"; char *ZEROUUID = "00000000-0000-0000-0000-000000000000"; char *session_id = NULL; char *username = NULL; char *password = NULL; char *client_cert = NULL; char *NODE_UUID = NULL; int xmlrpc_err_cnt = 0; host thishost; //--------------FUNCTIONS------------------- int main(int argc, char **argv) { initialize(argc, argv); //begin session //NOTE this should go away when using client certificates // and/or be a fallback method? // and/or happen anyway. if nonssl, then pass is primary, // else just a formality to get the sessid, yet not // reached if client cert fails (e.g. it has been revoked) //FIXME implement //FIXME this should probably go into initialize (to go before daemonizing???) { char *params[3]; char **aparam; aparam = params; char *x_user = xmlrpc_string(username); char *x_pass = xmlrpc_string(password); char *v_user = xmlrpc_value(x_user); char *v_pass = xmlrpc_value(x_pass); *aparam++ = xmlrpc_param(v_user); *aparam++ = xmlrpc_param(v_pass); *aparam++ = NULL; free(x_user); free(v_user); free(x_pass); free(v_pass); char *stat = "Starting new session with Zentific backend.."; if (DEBUG) fprintf(stdout, "%s\n", stat); log_msg(stat); int resplen = 1024; char *resp = (char *)malloc(sizeof(char)*resplen); resp[0] = 0; char *method = "User.login"; if( xmlrpc_call(method, params, resplen, resp) != 0 ){ //FIXME apply this "Method Call 'x' failed" err style elsewhere char *msg = new_str("XMLRPC Method Call '%s' failed. Please check network connection, server status, and log file.", method); if(DEBUG || XML_DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); free(msg); cleanup_exit(1); } //fixme clean up char *returnedmsg = (char *)malloc(sizeof(char)*1024); returnedmsg[0] = 0; int check = xmlrpc_check_response(resp, returnedmsg, 1024); //1 if success //0 if failed if(check < 0 && SEND_DATA){ //error happened char *msg = new_str("XML-RPC ERROR: %s\n", returnedmsg); log_msg(msg); if(DEBUG) fprintf(stderr, "%s\n", msg); free(msg); cleanup_exit(1); } else if(check < 0 && !SEND_DATA){ char *msg = "Sending of data suppressed; session not established.\n"; if(DEBUG) fprintf(stderr, "%s\n", msg); } else if (check == 0){ char *msg = "Login failure: incorrect username and/or password."; log_msg(msg); if(DEBUG) fprintf(stderr, "%s\n", msg); cleanup_exit(1); } else if (check == 1){ char *msg = "SUCCESS. \n"; log_msg(msg); if(DEBUG) fprintf(stderr, "%s\n", msg); } else { //shouldnt happen, FIXME handle anyway } //FIXME this could probably be more graceful // Session id capture is in xmlrpc_call... free(returnedmsg); free(resp); char **freep = params; int pp = 0; for(;;freep++, pp++){ if(*freep == NULL) { break; } else { free(params[pp]); params[pp] = NULL; } } } while( !REGISTERED ){ register_node(); sleep(5); } log_msg("Now actively polling data."); //NOTE uncomment the lines below with cnt if // less frequent node updates are desired // instead of once every iteration //int cnt = 0; // Main operating loop while ( 1 ) { if ( RELOAD_CONFIG == 1 ) { log_msg("Reloading config."); parse_config(); //FIXME will probably need a reauth to backend here // (to refresh session id) register_node(); RELOAD_CONFIG = 0; } //if(!(cnt % 5)) update_node(); update_stats(); sleep(DELAY); //cnt++; } // If we get here, then exit successfully return 0; } void initialize(int argc, char **argv){ //Verify running as root; if not, exit if(geteuid() != 0){ fprintf(stderr, "\nERROR: This application requires execution with root privileges.\n"); usage(argv[0]); exit(1); } //picky beautification printf("\n"); // Validate configuration file and make sure that we CAN run here parse_config(); int i=0; int opt_ind=1; //opterr=0 means dont print error via getopt if option parameter is missing //TODO: this was commented to satisfy a -Wall compile time warning. revert or not? //it is an extern variable, default is nonzero. check manpage for getopt / getopt_long //int opterr=0; extern int opterr; opterr = 0; //TODO: add an option to pay attention to only vms named X, Y, Z and/or UUIDS P, Q, R and/or DOMIDs A, B, C // same goes for the *exclusion* of the same //TODO: above item should have config file respresentations; cmdline merely overrides them static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "delay", required_argument, NULL, 'd' }, { "destination", required_argument, NULL, 'D' }, { "pidfile", required_argument, NULL, 'p' }, { "verbose", no_argument, NULL, 'V' }, { "version", no_argument, 0, 'v' }, { "host", required_argument, NULL, 'H' }, { "port", required_argument, NULL, 'P' }, { "ssl", required_argument, NULL, 's' }, { "xmldebug", no_argument, NULL, 'X' }, { "nosend", no_argument, NULL, 'n' }, { "auth", no_argument, NULL, 'A' }, { 0,0,0,0 }, }; /* Get and process arguments */ while (i != -1) { i = getopt_long(argc, argv, ":hH:D:P:vp:nXVs:d:A:", long_options, &opt_ind); switch (i) { case 'A': sscanf(optarg, "%255[^:]:%255s", username, password); break; case 'H': strncpy(BACKEND_HOST, optarg, 64); BACKEND_HOST[255] = 0; break; case 'P': //FIXME: typechecking PORT = atoi(optarg); break; case 'd': DELAY = atoi(optarg); break; case 'D': strncpy(XMLRPC_PATH, optarg, PATH_MAX); XMLRPC_PATH[PATH_MAX+1] = '\0'; break; case 'n': SEND_DATA = 0; break; case 'p': strncpy(PID_FILE, optarg, PATH_MAX); //FIXME: is this needed? PID_FILE[PATH_MAX+1] = '\0'; break; case 'h': usage(argv[0]); exit(0); break; case 'V': DEBUG=1; break; case 'X': XML_DEBUG=1; break; case 's': if( strcmp( strlwr(optarg), "yes") == 0 ) { USE_SSL = 1; } else if( strcmp( strlwr(optarg), "no") == 0 ) { USE_SSL = 0; //FIXME what if no ssl but not port 80 and "-P ##" was on cmd line before "-s no" PORT = 80; } else { fprintf(stderr, "ERROR: SSL command line parameter '%s' is invalid. Terminating.\n", optarg); usage(argv[0]); exit(1); } break; case 'v': version(); exit(0); break; case ':': if(!optarg & (i!=-1) ){ fprintf(stderr, "ERROR: Option '%c' requires an argument.\n\n",optopt); usage(argv[0]); exit(1); } break; case '?': usage(argv[0]); exit(1); break; default: break; } } // Final loading step is to make sure all required values are set // FIXME:function to check options. if ( BACKEND_HOST[0] == '\0' ) { fprintf(stderr, "ERROR: Required option BACKEND_HOST is not in the config file nor provided via command line.\n"); usage(argv[0]); exit(1); } char *msg = "Initializing..\n"; log_msg(msg); //open up our pathway into xen and grab ahold // do before daemonizing, because if this isnt possible, its not worth going that far xhandle = xenstat_init(); // If it cannot load the library, leave if ( xhandle == NULL ){ char *error = "ERROR: Could not get xenstat handle. Xend and/or xenstored not running?"; log_msg(error); fprintf(stderr, "%s\n", error); cleanup_exit(1); } // After we're sure we can run, detach from the TTY int daemonized = 0; if ( !DEBUG ) daemonized = daemonize(1); if(daemonized == -1){ char *error = "ERROR: Could not daemonize."; log_msg(error); fprintf(stderr, "%s\n", error); cleanup_exit(1); } //We're may not be heading into the bg, but create the PID file regardless // need to do pidfile AFTER daemonizing to get right pid, but before closing descriptors (which would hide errors) int madepid = 0; madepid = make_pidfile(PID_FILE); if(madepid == -2){ char *error = "ERROR: Could not write pid file. Daemon already running or previously exited uncleanly."; log_msg(error); fprintf(stderr, "%s\n", error); xenstat_uninit(xhandle); exit(1); } else if (madepid == -1){ char *error = "ERROR: Could not write pid file. Check permissions and filesystem status."; log_msg(error); fprintf(stderr, "%s\n", error); xenstat_uninit(xhandle); exit(1); } int detached = 0; if ( !DEBUG ) detached = detach_daemon(); if(detached == -1 ){ char *error = "ERROR: Could not detach daemonize; daemon already running or previously exited uncleanly. Check pidfile."; log_msg(error); fprintf(stderr, "%s\n", error); exit(1); } if ( DEBUG ) { display_config(); } // Handle a SIGHUP by reloading the configuration file // If this fails, then the process will simply ignore the SIGHUPs if ( signal(SIGHUP, config_reload) == SIG_IGN ) { signal(SIGHUP, SIG_IGN); //fixme: is this true? if ( DEBUG == 1 ) fprintf(stderr, "Warning: Signal handling for SIGHUP not initialized in debug mode.\n"); } // Handle a SIGINT or SIGTERM by cleaning up the PID file and exitting if ( signal(SIGINT, cleanup_exit) == SIG_IGN || signal(SIGTERM, cleanup_exit) == SIG_IGN ) { signal(SIGINT, SIG_DFL); if ( DEBUG == 1 ) fprintf(stderr, "Warning: Signal handling for SIGINT not initialized in debug mode.\n"); } log_msg("Initialization complete."); } int log_msg(char *msg){ char datebuf[24]; int logerr = 0; int fd = 0; struct tm *date; time_t now; const char *const format = "%b %d %Y %H:%M:%S"; if(strstr( LOG_TYPE, "local") != 0){ //owner: rw, group: read, others: read mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; //write only, create if not exist, append only fd = open( LOG_FILE, O_WRONLY | O_CREAT | O_APPEND, mode); if (fd == -1) { fprintf(stderr, "ERROR: Could not open log for writing: %s.\n", strerror( errno ) ); logerr = 1; } else { now = time(NULL); date = localtime((const time_t *)&now); strftime(datebuf,sizeof(datebuf),format,date); //msglen = snprintf(buf, sizeof(buf), "%s - %s: %s\n", datebuf, LOG_PREFIX, msg); char *logmsg = new_str("%s - %s\n", datebuf, msg); int msglen = strlen(logmsg); if (lockf(fd, F_TLOCK, 0) == -1) logerr = 1; if (write(fd, logmsg, msglen) < 0) logerr = 1; int closed = close(fd); if(closed == -1) logerr = 1; free(logmsg); } } if(strstr( LOG_TYPE, "lsyslog") != 0){ //FIXME: defaults to LOG_INFO; what if fatal error results? perhaps take another param to log_msg //FIXME: any error checking with these? openlog(LOG_PREFIX, LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "%s", msg); closelog(); } if(logerr){ fprintf(stderr, "ERROR: Logging failed.\n"); return -1; } return 0; } int update_node() { //this function is semantically different from register_node. //this function collects dom0-specific stats and initializes the // node details (with the hardware specifications, etc) //register_node calls this function and then submits this information // to the backend if ( DEBUG ) { char *msg = "STATUS: Collecting statistics about the physical node."; if(DEBUG) fprintf(stderr, "%s\n", msg); //do not log to file here, it becomes excessive } //NOTE: dom0 IP implictly detected on receiving side //TODO: add dom0 IP anyway, in case it is being NATd xenstat_node *tempnode = xenstat_get_node(xhandle, XENSTAT_ALL); struct utsname uname_pointer; if(uname(&uname_pointer) == -1){ char *msg = "ERROR: Could not get uname data."; if(DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); return -1; } xc_physinfo_t physinfo = { 0 }; /* Get information about the physical system */ if (xc_physinfo(xhandle->xc_handle, &physinfo) < 0) { char *msg = "ERROR: Could not get system status data."; if(DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); return -1; } if( !REGISTERED ) { thishost.arch = strdup(uname_pointer.machine); thishost.kernel_ver = strdup(uname_pointer.release); thishost.os_ver = strdup(uname_pointer.sysname); thishost.xen_ver = strdup(xenstat_node_xen_version(tempnode)); if(NODE_UUID && strcmp(NODE_UUID, "detect") == 0){ char *hwuuid = (char *)malloc(sizeof(char)*36+1); //36=32 characters of uuid + 4 dashes hwuuid[0] = 0; if(get_hw_uuid(hwuuid) < 0){ char *msg = "ERROR: Could not get system hw UUID data."; if(DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); return -1; } free(thishost.hwuuid); thishost.hwuuid = strdup(hwuuid); free(hwuuid); } else if (NODE_UUID) { thishost.hwuuid = strdup(NODE_UUID); } else { char *msg = "ERROR: A node UUID is required for operation of the Zentific poller."; if(DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); cleanup_exit(1); } thishost.cores_per_socket = physinfo.cores_per_socket; thishost.threads_per_core = physinfo.threads_per_core; thishost.sockets_per_node = physinfo.nr_nodes; thishost.num_cpu_nodes = physinfo.nr_nodes; //NOTE: these two could also be outside of this !registered block // especially for computers with cpufreq throttling or hotpluggable cpus thishost.num_cpus = xenstat_node_num_cpus(tempnode); thishost.cpu_mhz = xenstat_node_cpu_hz(tempnode)/1024/1024; //FIXME do this every iteration rather than just at initial? thishost.default_vnc_passwd = get_default_vnc_passwd(); } if(gethostname(thishost.hostname, HOST_NAME_MAX) == -1){ char *msg = "ERROR: Could not get hostname data."; if(DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); return -1; } thishost.hostname[HOST_NAME_MAX+1] = 0; struct hostent *hp; /* for gethostbyname */ if ((hp = gethostbyname(thishost.hostname)) == NULL) { char *msg = "ERROR: Could not resolve hostname."; if(DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); return -1; } struct in_addr in; in.s_addr=(*(unsigned long *)hp->h_addr); char * nodeaddr = inet_ntoa(in); strncpy(thishost.address, nodeaddr, strlen(nodeaddr)); if(getdomainname(thishost.domainname, HOST_NAME_MAX) == -1){ char *msg = "ERROR: Could not get domain name data."; if(DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); return -1; } thishost.domainname[HOST_NAME_MAX+1] = 0; thishost.tot_mem = xenstat_node_tot_mem(tempnode)/1024/1024; thishost.free_mem = xenstat_node_free_mem(tempnode)/1024/1024; xenstat_domain *domain_zero = xenstat_node_domain_by_index(tempnode,0); thishost.cpu_pct = get_cpu_pct(domain_zero); /* Sysinfo data: All unsigned long longs * info.totalram; // Total usable main memory size * info.freeram; // Available memory size * info.sharedram; // Amount of shared memory * info.bufferram; // Memory used by buffers * info.totalswap; // Total swap space size * info.freeswap; // swap space still available * info.procs; // Number of current processes * info.totalhigh; // Total high memory size * info.freehigh; // Available high memory size * info.mem_unit; // Memory unit size in bytes */ struct sysinfo info; sysinfo(&info); //TODO/FIXME?: check return of this? // i.e.: int system_info = sysinfo(&info); long uptime = info.uptime; // Seconds since boot // 1, 5, and 15 minute load averages float one = info.loads[0] / 65536.0; float five = info.loads[1] / 65536.0; float fif = info.loads[2] / 65536.0; unsigned long long firstintr = get_intr(); usleep(2000000); unsigned long long secondintr = get_intr(); unsigned long long firstctxt = get_ctxt(); usleep(2000000); unsigned long long secondctxt = get_ctxt(); float iowait = get_iowait (); // cputime %user %nice %system %idle // IO transactions per second // stats for volume/disk usage // cache misses? //check the iostat tool to calculate await (average wait time for IO request to be handled) and svctm (average speed of disk response time). // iowait high wih svctm low == disk performing well, bottleneck elsewhere // both high == disk contention. bottleneck here unsigned long long div = 1024 * 1024; if( DEBUG ) { printf("-------------------dom0 stats-------------------\n"); printf("Memory: total %llu, used %llu, free %llu, shared %llu, buffered %llu\n", info.totalram / div, (info.totalram-info.freeram)/div, info.freeram/div, info.sharedram/div, info.bufferram/div); printf("Swap: total %llu, used %llu, free %llu\n", info.totalswap/div, (info.totalswap-info.freeswap)/div, info.freeswap/div); printf("num processes %i\n", info.procs); printf("interrupts/s=%llu, context switches/s=%llu\n", ((secondintr-firstintr)/2), (secondctxt-firstctxt)/2); printf("uptime: %li days, %li hours, %li minutes\n", uptime / 86400, uptime / 3600 % 24, uptime / 60 % 60); printf("1 %.2f 5 %.2f 15 %.2f\n", one , five , fif ); printf("iowait%% : %.2f\n", iowait); printf("cpu %% : %3.2f\n", thishost.cpu_pct); printf("------------------------------------------------\n\n"); display_node(); } if ( REGISTERED ) { //FIXME iowait, if possible, should not be a string here //loadavg will have to stay a str unless making it an array? char *c_iowait = new_str("%.2f", iowait); char *c_load_avg = new_str("%.2f %.2f %.2f", one, five, fif); char *x_iowait = xmlrpc_string(c_iowait); char *x_load_avg = xmlrpc_string(c_load_avg); char *x_uptime = xmlrpc_double((int)uptime); char *x_inter = xmlrpc_int((int)((secondintr-firstintr)/2)); char *x_ctxts = xmlrpc_int((int)((secondctxt-firstctxt)/2)); char *x_num_procs = xmlrpc_int(info.procs); char *x_mem_free = xmlrpc_int(info.freeram/div); char *x_mem_total = xmlrpc_int( info.totalram / div); char *x_mem_shared = xmlrpc_int(info.sharedram/div); char *x_mem_buffered = xmlrpc_int(info.bufferram/div); char *x_swap_total = xmlrpc_int(info.totalswap/div); char *x_swap_free = xmlrpc_int(info.freeswap/div); char *x_domainname = xmlrpc_string(thishost.domainname); char *x_address = xmlrpc_string(thishost.address); char *x_hostname = xmlrpc_string(thishost.hostname); char *x_total_phys_mem = xmlrpc_double(thishost.tot_mem); char *x_free_phys_mem = xmlrpc_double(thishost.free_mem); char *x_cpupct = xmlrpc_double(thishost.cpu_pct); char *xstructmem_cpupct = xmlrpc_struct_member("cpupct", x_cpupct); char *xstructmem_iowait = xmlrpc_struct_member("iowait", x_iowait); char *xstructmem_load = xmlrpc_struct_member("loadavg", x_load_avg); char *xstructmem_uptime = xmlrpc_struct_member("uptime", x_uptime); char *xstructmem_inter = xmlrpc_struct_member("intr", x_inter); char *xstructmem_ctxts = xmlrpc_struct_member("ctxts", x_ctxts); char *xstructmem_num_procs = xmlrpc_struct_member("num_procs", x_num_procs); char *xstructmem_mem_free = xmlrpc_struct_member("mem_free", x_mem_free); char *xstructmem_mem_total = xmlrpc_struct_member("mem_total", x_mem_total); char *xstructmem_mem_shared = xmlrpc_struct_member("mem_shared", x_mem_shared); char *xstructmem_mem_buffered = xmlrpc_struct_member("mem_buffered", x_mem_buffered); char *xstructmem_swap_total = xmlrpc_struct_member("swap_total", x_swap_total); char *xstructmem_swap_free = xmlrpc_struct_member("swap_free", x_swap_free); char *xstructmem_domainname = xmlrpc_struct_member("domainname", x_domainname); char *xstructmem_address = xmlrpc_struct_member("address", x_address); char *xstructmem_hostname = xmlrpc_struct_member("hostname", x_hostname); char *xstructmem_total_phys_mem = xmlrpc_struct_member("total_phys_mem", x_total_phys_mem); char *xstructmem_free_phys_mem = xmlrpc_struct_member("free_phys_mem", x_free_phys_mem); char *param = new_str("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", xstructmem_iowait, xstructmem_load, xstructmem_uptime, xstructmem_inter, xstructmem_ctxts, xstructmem_num_procs, xstructmem_mem_free, xstructmem_mem_total, xstructmem_mem_shared, xstructmem_mem_buffered, xstructmem_swap_total, xstructmem_swap_free, xstructmem_domainname, xstructmem_hostname, xstructmem_total_phys_mem, xstructmem_free_phys_mem, xstructmem_address, xstructmem_cpupct ); char *resultstruct = xmlrpc_struct(param); char *resultval = xmlrpc_value(resultstruct); char *resultparam = xmlrpc_param(resultval); char *x_hwuuid = xmlrpc_string(thishost.hwuuid); char *v_hwuuid = xmlrpc_value(x_hwuuid); char *p_hwuuid = xmlrpc_param(v_hwuuid); char *params[4]; char **aparam; aparam = params; *aparam++ = new_str("%s", p_hwuuid); *aparam++ = new_str("%s", resultparam); *aparam++ = NULL; int resplen = 4096; char *resp = (char *)malloc(sizeof(char)*resplen); resp[0] = 0; int callret = xmlrpc_call("Node.updateNodeStats", params, resplen, resp); char **freep = params; int pp = 0; for(;;freep++, pp++){ if(*freep == NULL) { break; } else { free(params[pp]); params[pp] = NULL; } } if( callret == -1){ char *msg = new_str("XMLRPC Method Call failed. Please check network connection, server status, and log file."); if(DEBUG || XML_DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); free(msg); cleanup_exit(1); } else { //fixme clean up char *returnedmsg = (char *)malloc(sizeof(char)*1024); returnedmsg[0] = 0; int check = xmlrpc_check_response(resp, returnedmsg, 1024); //FIXME: better error handling needed. at the moment, the log just gets // the raw error code if(check == -1 && SEND_DATA){ //error happened fprintf(stderr, "XML-RPC ERROR: %s\n", returnedmsg); log_msg(returnedmsg); //FIXME do we want to bail if update_node_stats fails? cleanup_exit(1); } else if(check == -1 && !SEND_DATA){ char *msg = new_str("Sending of data suppressed; node statistics not sent.\n"); if(DEBUG) fprintf(stderr, "%s\n", msg); free(msg); } else if(check == 1) { //success if(DEBUG) { fprintf(stdout, "SUCCESS. \n"); //log_msg(returnedmsg); //dont log, becomes excessive } } else if( check == 0){ //FIXME check==0 might not be an error or a failure, could be // just true or false if(DEBUG) { //char *msg = new_str("FAILURE: could not update Zentific backend with node statistics. %s.", returnedmsg); char *msg = new_str("FAILURE: could not update Zentific backend with node statistics."); fprintf(stdout, "%s\n\n", msg); log_msg(msg); free(msg); } } else { //FIXME this may be problematic. either an XMLRPC error (from // the application, rather than non-existent method // or demarshalization error) OR not an error, but status of some variety fprintf(stderr, "Unexpected method response code=%d \n", check); } free(returnedmsg); } free(param); free(resp); free(resultstruct); free(resultval); free(resultparam); free(c_iowait); free(c_load_avg); free(x_hwuuid); free(v_hwuuid); free(p_hwuuid); free(x_cpupct); free(x_iowait); free(x_load_avg); free(x_uptime); free(x_inter); free(x_ctxts); free(x_num_procs); free(x_mem_free); free(x_mem_total); free(x_mem_shared); free(x_mem_buffered); free(x_swap_total); free(x_swap_free); free(x_domainname); free(x_address); free(x_hostname); free(x_total_phys_mem); free(x_free_phys_mem); free(xstructmem_iowait); free(xstructmem_load); free(xstructmem_uptime); free(xstructmem_inter); free(xstructmem_ctxts); free(xstructmem_num_procs); free(xstructmem_mem_free); free(xstructmem_mem_total); free(xstructmem_mem_shared); free(xstructmem_mem_buffered); free(xstructmem_swap_total); free(xstructmem_swap_free); free(xstructmem_domainname); free(xstructmem_hostname); free(xstructmem_total_phys_mem); free(xstructmem_free_phys_mem); free(xstructmem_address); free(xstructmem_cpupct); } xenstat_free_node(tempnode); return 0; } // Reads the statistics from the libxenstat void update_stats() { int num_domains = 0; gettimeofday(&oldtime, NULL); prev_node = xenstat_get_node(xhandle, XENSTAT_ALL); if ( prev_node == NULL ) { char *msg = "ERROR: Failed to retrieve statistics from libxenstat."; log_msg(msg); if(DEBUG) fprintf(stderr, "%s\n", msg); cleanup_exit(1); } usleep(TIME_INTERVAL); gettimeofday(&curtime, NULL); node = xenstat_get_node(xhandle, XENSTAT_ALL); if ( node == NULL ) { char *msg = "ERROR: Failed to retrieve statistics from libxenstat."; log_msg(msg); if(DEBUG) fprintf(stderr, "%s\n", msg); cleanup_exit(1); } num_domains = xenstat_node_num_domains(node); guest *guests = (guest *)malloc(num_domains*sizeof(guest)); //XMLRPC setup char *params[num_domains+1]; char *vmsarray = NULL; vmsarray = str_append(vmsarray, "");//%s char **aparam; aparam = params; //Stats loop int j; for ( j=0; j < num_domains; j++ ) { xenstat_domain *cur_domain = xenstat_node_domain_by_index(node,j); guests[j].domid = cur_domain->id; guests[j].uuid = get_uuid(guests[j].domid); if(strcmp(guests[j].uuid, ZEROUUID) == 0){ //if we're dealing with the dom0 // we do some more statistics gathering // TODO: potentiall move this back from the while loop in main // also consider calling on a more periodic basis //update_node(); } else { //otherwise we're working with a domU //TODO:note migrating-$NAME guests should be ignored and/or trigger a 'migrating' flag in db? //FIXME for HVM hosts, need to check the following: //dmargs contains pae, acpi, apic, boot guests[j].puuid = strdup(thishost.hwuuid); guests[j].name = cur_domain->name; guests[j].cputime = cur_domain->cpu_ns/1000000000; guests[j].vcpus = cur_domain->num_vcpus; guests[j].mem = cur_domain->cur_mem; guests[j].maxmem = cur_domain->max_mem; guests[j].numnets = cur_domain->num_networks; guests[j].numvbds = cur_domain->num_vbds; guests[j].cpu_pct = get_cpu_pct(cur_domain); guests[j].vmtype = get_vm_type(guests[j].uuid); guests[j].ostype = get_os_type(guests[j].uuid); guests[j].kernel = get_kernel(guests[j].uuid); guests[j].ramdisk = get_ramdisk(guests[j].uuid); guests[j].cmdline = get_kernel_cmdline(guests[j].uuid); guests[j].on_poweroff = get_poweroff_action(guests[j].uuid); guests[j].on_reboot = get_reboot_action(guests[j].uuid); guests[j].on_crash = get_crash_action(guests[j].uuid); guests[j].uptime = get_uptime(guests[j].uuid); guests[j].vbds = (vbd *)malloc(sizeof(vbd)*cur_domain->num_vbds); get_vbd_stats(cur_domain->vbds, cur_domain->num_vbds, guests[j].domid, guests[j].vbds); guests[j].txbw = 0; guests[j].rxbw = 0; guests[j].vifs = (vif *)malloc(sizeof(vif)*cur_domain->num_networks); get_vif_stats(cur_domain->networks, cur_domain->num_networks, guests[j].domid, guests[j].vifs, &guests[j].rxbw, &guests[j].txbw, guests[j].vmtype); //FIXME when sdl=0 and vnc=0 in an HVM domain, we have problems because no port is present // go fix that guests[j].vnc_port = get_vnc_port(guests[j].domid); guests[j].vnc_passwd = get_vnc_passwd(guests[j].domid); guests[j].state = get_state(cur_domain); if ( DEBUG==1 ) { display_guest(&guests[j]); } //XMLRPC block begins char *xuuid = xmlrpc_string(guests[j].uuid); char *xpuuid = xmlrpc_string(guests[j].puuid); char *xdomid = xmlrpc_int(guests[j].domid); char *xname = xmlrpc_string(guests[j].name); char *xcputime = xmlrpc_double(guests[j].cputime); char *xvcpus = xmlrpc_int(guests[j].vcpus); char *xmem = xmlrpc_int(guests[j].mem/1024); char *xmaxmem = xmlrpc_int(guests[j].maxmem/1024); char *xnumnets = xmlrpc_int(guests[j].numnets); char *xcpupct = xmlrpc_double(guests[j].cpu_pct); char *xvmtype = xmlrpc_string(guests[j].vmtype); char *xostype = xmlrpc_string(guests[j].ostype); char *xvncport = xmlrpc_str_int(guests[j].vnc_port); char *xvncpasswd= xmlrpc_string(guests[j].vnc_passwd); char *xstate = xmlrpc_string(guests[j].state); char *xnumvbds = xmlrpc_int(guests[j].numvbds); char *xrxbw = xmlrpc_int(guests[j].rxbw/1024/1024); char *xtxbw = xmlrpc_int(guests[j].txbw/1024/1024); char *xramdisk = xmlrpc_string(guests[j].ramdisk); char *xcmdline = xmlrpc_string(guests[j].cmdline); char *xkernel = xmlrpc_string(guests[j].kernel); char *xon_poweroff = xmlrpc_string(guests[j].on_poweroff); char *xon_crash = xmlrpc_string(guests[j].on_crash); char *xon_reboot = xmlrpc_string(guests[j].on_reboot); char *xuptime = xmlrpc_int(guests[j].uptime); int x; char *vbdsblob = NULL; for(x=0;x"); char *resultvalue = xmlrpc_value(vmsarray); char *resultparam = xmlrpc_param(resultvalue); *aparam++ = new_str("%s", resultparam); free(vmsarray); free(resultvalue); free(resultparam); if ( DEBUG ) printf("------------------------------------------------\n\n"); //XMLRPC dispatch *aparam++ = NULL; //NOTE: send all in one batch or one at a time??? int resplen = 32768; char *resp = (char *)malloc(sizeof(char)*resplen); resp[0] = 0; int callret = -1; while(callret == -1 && xmlrpc_err_cnt < BAIL_THRESHOLD){ callret = xmlrpc_call("VM.updateVm", params, resplen, resp); if(callret == -1) xmlrpc_err_cnt++; } if( callret == -1){ char *msg = "XMLRPC Method Call failed. Please check network connection, server status, and log file."; if(DEBUG || XML_DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); } else { //start over xmlrpc_err_cnt = 0; } char **freep = params; int pp = 0; for(;;freep++, pp++){ if(*freep == NULL) { break; } else { free(params[pp]); params[pp] = NULL; } } //fixme clean up char *returnedmsg = (char *)malloc(sizeof(char)*1024+1); returnedmsg[0] = 0; if(callret >= 0){ int check = check = xmlrpc_check_response(resp, returnedmsg, 1024); if(XML_DEBUG) { log_msg(returnedmsg); } //FIXME: what if update stats fails ? if(check == 1){ //success char *msg = "SUCCESS.\n"; //log_msg(msg); dont log it, it becomes excessive if(DEBUG) fprintf(stderr, "%s\n", msg); } else if (check == 0){ //Failed to update stats char *msg = "FAILURE: could not update Zentific backend with vm statistics."; log_msg(msg); if(DEBUG) fprintf(stderr, "%s\n", msg); } else if(check == -1 && SEND_DATA){ //error happened fprintf(stderr, "XML-RPC ERROR: %s\n", returnedmsg); log_msg(returnedmsg); //FIXME do we want to bail if update_stats fails? //no we do not //cleanup_exit(1); //but we do increment the xmlrpc error count xmlrpc_err_cnt++; } else if(check == -1 && !SEND_DATA){ char *msg = new_str("Sending of data suppressed; statistics update not sent.\n"); if(DEBUG) fprintf(stderr, "%s\n", msg); free(msg); } } free(resp); free(guests); free(returnedmsg); xenstat_free_node(node); xenstat_free_node(prev_node); if(xmlrpc_err_cnt >= BAIL_THRESHOLD){ char *msg = new_str("XMLRPC Method Call has failed %d times. Please check network connection, server status, and log file.", xmlrpc_err_cnt); log_msg(msg); if(DEBUG) fprintf(stderr, "%s\n", msg); free(msg); } } int register_node(){ update_node(); char *status = new_str("Registering node with the Zentific management backend.."); if(DEBUG || XML_DEBUG) fprintf(stderr, "%s\n", status); log_msg(status); free(status); char *platform = "Xen"; char *x_platform = xmlrpc_string(platform); char *x_hwuuid = xmlrpc_string(thishost.hwuuid); char *x_kern = xmlrpc_string(thishost.kernel_ver); char *x_os_ver = xmlrpc_string(thishost.os_ver); char *x_arch = xmlrpc_string(thishost.arch); char *x_hostname = xmlrpc_string(thishost.hostname); char *x_address = xmlrpc_string(thishost.address); char *x_domainname = xmlrpc_string(thishost.domainname); char *x_xen_ver = xmlrpc_string((char*)thishost.xen_ver); char *x_num_cpus = xmlrpc_int(thishost.num_cpus); char *x_cores_per_socket = xmlrpc_int(thishost.cores_per_socket); char *x_threads_per_core = xmlrpc_int(thishost.threads_per_core); char *x_sockets_per_node = xmlrpc_int(thishost.sockets_per_node); char *x_num_cpu_nodes = xmlrpc_int(thishost.num_cpu_nodes); char *x_cpu_mhz = xmlrpc_double(thishost.cpu_mhz); char *x_total_mem = xmlrpc_double(thishost.tot_mem); //NOTE free_mem is omitted during node registration because it is variable //char *x_free_mem = xmlrpc_double(thishost.free_mem); char *x_default_vnc_pass = xmlrpc_string((char*)thishost.default_vnc_passwd); char *xstructmem_hwuuid = xmlrpc_struct_member("hwuuid", x_hwuuid); char *xstructmem_kernel = xmlrpc_struct_member("kernel", x_kern); char *xstructmem_os = xmlrpc_struct_member("os", x_os_ver); char *xstructmem_arch = xmlrpc_struct_member("architecture", x_arch); char *xstructmem_hostname = xmlrpc_struct_member("hostname", x_hostname); char *xstructmem_address = xmlrpc_struct_member("address", x_address); char *xstructmem_domainname = xmlrpc_struct_member("domainname", x_domainname); char *xstructmem_platform = xmlrpc_struct_member("platform", x_platform); char *xstructmem_xen_ver = xmlrpc_struct_member("platformver", x_xen_ver); char *xstructmem_num_cpus = xmlrpc_struct_member("num_cpus", x_num_cpus); char *xstructmem_cores_per_sock = xmlrpc_struct_member("cores_per_socket", x_cores_per_socket); char *xstructmem_thrd_per_core = xmlrpc_struct_member("threads_per_core", x_threads_per_core); char *xstructmem_num_cpu_nodes = xmlrpc_struct_member("num_cpu_nodes", x_num_cpu_nodes); char *xstructmem_sock_per_node = xmlrpc_struct_member("sockets_per_node", x_sockets_per_node); char *xstructmem_cpu_mhz = xmlrpc_struct_member("cpu_mhz", x_cpu_mhz); char *xstructmem_total_mem = xmlrpc_struct_member("total_memory", x_total_mem); char *xstructmem_default_vnc_pass = xmlrpc_struct_member("default_vnc_pass", x_default_vnc_pass); //NOTE free_mem is omitted during node registration because it is variable //char *xstructmem_free_mem = xmlrpc_struct_member("free_memory", x_free_mem); char *param = new_str("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", //%s", xstructmem_hwuuid, xstructmem_kernel, xstructmem_os, xstructmem_arch, xstructmem_hostname, xstructmem_domainname, xstructmem_platform, xstructmem_xen_ver, xstructmem_num_cpus, xstructmem_cores_per_sock, xstructmem_thrd_per_core, xstructmem_num_cpu_nodes, xstructmem_sock_per_node, xstructmem_cpu_mhz, xstructmem_total_mem, xstructmem_address, xstructmem_default_vnc_pass //, xstructmem_free_mem ); free(xstructmem_hwuuid); free(xstructmem_kernel); free(xstructmem_os); free(xstructmem_arch); free(xstructmem_hostname); free(xstructmem_address); free(xstructmem_domainname); free(xstructmem_xen_ver); free(xstructmem_platform); free(xstructmem_num_cpus); free(xstructmem_cores_per_sock); free(xstructmem_thrd_per_core); free(xstructmem_num_cpu_nodes); free(xstructmem_sock_per_node); free(xstructmem_cpu_mhz); free(xstructmem_total_mem); //NOTE free_mem is omitted during node registration because it is variable //free(xstructmem_free_mem); free(xstructmem_default_vnc_pass); free(x_hwuuid); free(x_kern); free(x_os_ver); free(x_arch); free(x_hostname); free(x_address); free(x_domainname); free(x_xen_ver); free(x_platform); free(x_num_cpus); free(x_cores_per_socket); free(x_threads_per_core); free(x_sockets_per_node); free(x_num_cpu_nodes); free(x_cpu_mhz); free(x_total_mem); //NOTE free_mem is omitted during node registration because it is variable //free(x_free_mem); free(x_default_vnc_pass); char *params[2]; char **aparam; aparam = params; //fixme leak here *aparam++ = xmlrpc_param(xmlrpc_value(xmlrpc_struct(param))); *aparam++ = NULL; int resplen = 8192; char *resp = (char *)malloc(sizeof(char)*resplen); resp[0] = 0; int callret = xmlrpc_call("Node.registerNode", params, resplen, resp); if( callret == -1){ char *msg = new_str("XMLRPC Method Call failed. Please check network connection, server status, and log file."); if(DEBUG || XML_DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); free(msg); cleanup_exit(1); } //fixme clean up char *returnedmsg = (char *)malloc(sizeof(char)*1024); returnedmsg[0] = 0; int check = xmlrpc_check_response(resp, returnedmsg, 1024); //FIXME -- check update_node for error handling // and update here //1 if success //0 if pending //2 if registration could not complete, check network, etc //3 if registration denied if(check == -1 && SEND_DATA){ //error happened fprintf(stderr, "XML-RPC ERROR: %s\n", returnedmsg); log_msg(returnedmsg); //FIXME do we want to bail if update_stats fails? cleanup_exit(1); } else if(check == -1 && !SEND_DATA){ char *msg = new_str("Sending of data suppressed; node registration not established.\n"); if(DEBUG) fprintf(stderr, "%s\n", msg); free(msg); REGISTERED = 1; } else if(check == 1) { //registration success REGISTERED = 1; if(XML_DEBUG) fprintf(stdout, "%s\n\n", returnedmsg); if(DEBUG) { fprintf(stdout, "SUCCESS. %s\n\n", returnedmsg); } log_msg(returnedmsg); } else if( check == 0){ //registration pending char *error = new_str("Registration of node with the Zentific management backend is pending."); if(DEBUG) { if(DEBUG || XML_DEBUG) fprintf(stderr, "%s\n", error); } log_msg(error); free(error); } else if(check > 1){ //fatal error, cant go any further if(returnedmsg != NULL){ if(DEBUG || XML_DEBUG) fprintf(stderr, "%s\n", returnedmsg); log_msg(returnedmsg); } else { char *error = new_str("ERROR: Could not register node with the Zentific management backend. Check configuration, network, and backend status. Additional information: %s", returnedmsg ); if(DEBUG || XML_DEBUG) fprintf(stderr, "%s\n", error); log_msg(error); free(error); } cleanup_exit(1); } else { //FIXME this may be problematic. either an XMLRPC error (from // the application, rather than non-existent method // or demarshalization error) OR not an error, but status of some variety fprintf(stderr, "register_node: Unexpected method response code=%d \n", check); } char **freep = params; int pp = 0; for(;;freep++, pp++){ if(*freep == NULL) { break; } else { free(params[pp]); } } free(param); free(resp); free(returnedmsg); return check; } void display_node(){ printf("----------------physical stats------------------\n"); printf("HW UUID: %s\n", thishost.hwuuid); printf("KERNEL VERSION: %s\n", thishost.kernel_ver); //kernel ver printf("OS NAME: %s\n", thishost.os_ver); //os name printf("ARCH: %s\n", thishost.arch); //arch printf("HOSTNAME: %s\n", thishost.hostname); //thishost name printf("DOMAINNAME: %s\n", thishost.domainname); //domain name printf("XEN VERSION: %s\n", thishost.xen_ver); printf("NUM CPUS: %li\n", thishost.num_cpus); printf("NUM CORES PER SOCKET: %d\n", thishost.cores_per_socket); printf("NUM THREADS PER CORE: %d\n", thishost.threads_per_core); printf("NUM SOCKETS PER NODE: %d\n", thishost.sockets_per_node); printf("NUM CPU NODES: %d\n", thishost.num_cpu_nodes); printf("CPU MHZ: %li\n", thishost.cpu_mhz); printf("TOTAL MEM: %li\n", thishost.tot_mem); printf("FREE MEM: %li\n", thishost.free_mem); printf("DEFAULT VNC PASS: %s\n", thishost.default_vnc_passwd); printf("------------------------------------------------\n"); } void display_config(){ //FIXME print host config variables printf("---------------config variables-----------------\n"); printf("BACKEND_HOST:\t%s.\n", BACKEND_HOST); printf("XMLRPC_PATH: \t%s.\n", XMLRPC_PATH); printf("LOG_TYPE: \t%s.\n", LOG_TYPE); printf("PID_FILE: \t%s.\n", PID_FILE); printf("USE_SSL: \t%i.\n", USE_SSL); printf("DELAY: \t%d.\n", DELAY); printf("PORT: \t%d.\n", PORT); printf("------------------------------------------------\n\n"); //others here } //NOTE: this function only displays when debugging mode is enabled // it would be entirely possible to revamp xentop to be more informational; // however, this program is not intended to be interactive. as such, display // of guest information, while useful, is going to be ugly unless there is a // real interest in making this program more extensible (possibly as a xentop // replacement/update). void display_guest(guest *aguest){ printf("------------------------------------------------\n\n"); printf("%s\n %4i\t %6s\n" "\tuuid \t%s\n" "\tkernel \t%s\n" "\tcmdline \t%s\n" "\tramdisk \t%s\n" "\tvcpus \t%i\n" "\tcpupct \t%.1f\n" "\tvnc-port\t%s\n" "\ttype \t%s\n" "\tcputime \t%llu\n" "\tos \t%s\n" "\tnumnets \t%d\n" "\tmem \t%llu\n" "\tmaxmem \t%llu\n" "\tvncpass \t%s\n" "\tnumvbds \t%d\n" "\tbw in \t%llu\n" "\tbw out \t%llu\n" "\ton_poweroff \t%s\n" "\ton_crash \t%s\n" "\ton_reboot \t%s\n" "\tuptime: \t%dd, %dh, %dm\n" "\n", aguest->name, aguest->domid, aguest->state, aguest->uuid, aguest->kernel, aguest->cmdline, aguest->ramdisk, aguest->vcpus, aguest->cpu_pct, aguest->vnc_port, aguest->vmtype, aguest->cputime, aguest->ostype, aguest->numnets, aguest->mem/1024/1024, aguest->maxmem/1024/1024, aguest->vnc_passwd, aguest->numvbds, aguest->rxbw/1024/1024, aguest->txbw/1024/1024, aguest->on_poweroff, aguest->on_crash, aguest->on_reboot, aguest->uptime / 86400, aguest->uptime / 3600 % 24, aguest->uptime / 60 % 60 ); int x; for(x=0;xnumvbds;x++){ printf("\tDisk %d:\n", x); printf("\t\tType: \t%s\n" "\t\tPath: \t%s\n" "\t\tDevice: \t%s\n" "\t\tMapped: \t%s\n" "\t\tMode: \t%s\n", aguest->vbds[x].type, aguest->vbds[x].external_dev, aguest->vbds[x].internal_dev, aguest->vbds[x].mapped_dev, aguest->vbds[x].mode); printf( "\t\tBlksz: \t%d\n", aguest->vbds[x].block_size); printf( "\t\tSt.Sect: \t%d\n", aguest->vbds[x].start_sector); printf( "\t\tPartition: \t%s\n", aguest->vbds[x].partition_type); printf("\t\tReads: %llu, Writes: %llu, Out of: %llu\n", aguest->vbds[x].rd_reqs, aguest->vbds[x].wr_reqs, aguest->vbds[x].oo_reqs); printf( "\t\tNum sectors of size %d bytes: %d\n" "\t\tCapacity: %.2lf GiB\n", aguest->vbds[x].size_sector, aguest->vbds[x].num_sectors, ((double)aguest->vbds[x].size_sector*(double)aguest->vbds[x].num_sectors)/1024/1024/1024 ); } for(x=0;xnumnets;x++){ printf("\tVif %d:\n", x); char *name = NULL; if(aguest->vifs[x].vifname == NULL) name = ""; else name = aguest->vifs[x].vifname; printf("\t\tType: \t%s\n" "\t\tBridge: \t%s\n" "\t\tMAC: \t%s\n" "\t\tScript: \t%s\n" "\t\tName: \t%s\n", aguest->vifs[x].type, aguest->vifs[x].bridge, aguest->vifs[x].mac, aguest->vifs[x].script, name); printf("\t\tRX -- pkts: %llu, bytes: %llu, drop: %llu, err: %llu\n", aguest->vifs[x].rpackets, aguest->vifs[x].rbytes, aguest->vifs[x].rdrop, aguest->vifs[x].rerrs); printf("\t\tTX -- pkts: %llu, bytes: %llu, drop: %llu, err: %llu\n", aguest->vifs[x].tpackets, aguest->vifs[x].tbytes, aguest->vifs[x].tdrop, aguest->vifs[x].terrs); } } char *get_state(xenstat_domain *domain){ //ugly but interesting struct { unsigned int (*get)(xenstat_domain *); char *ch; } states[] = { { xenstat_domain_dying, "d" }, { xenstat_domain_shutdown, "s" }, { xenstat_domain_blocked, "b" }, { xenstat_domain_crashed, "c" }, { xenstat_domain_paused, "p" }, { xenstat_domain_running, "r" } }; const unsigned int NUM_STATES = sizeof(states)/sizeof(*states); //FIXME does it make sense to have a one - to - two char // return val be as large as the whole set? char *state = (char *)malloc(sizeof(char)*NUM_STATES); memset(state, 0, NUM_STATES); // state[0] = 0; unsigned int i; for(i = 0; i < NUM_STATES; i++){ //state[i] = states[i].get(domain) ? states[i].ch : '-'; if(states[i].get(domain)) strcat(state, states[i].ch); } //FIXME what about state "" or " " return state; } int get_vbd_stats(xenstat_vbd *vbds, int numvbds, int domid, vbd *disks){ int i = 0; for(i=0; i < numvbds; i++){ disks[i].dev = vbds[i].dev; //major # disks[i].oo_reqs = vbds[i].oo_reqs; //out of requests disks[i].rd_reqs = vbds[i].rd_reqs; //read requests disks[i].wr_reqs = vbds[i].wr_reqs; //write requests #if XENMAJ > 3 && XENMIN > 2 // xen ver >= 3.2 //printf("VBD: dev %d t %d oo %d wr %d rd %d\n", vbds[i].dev, vbds[i].back_type, vbds[i].oo_reqs, vbds[i].wr_reqs, vbds[i].rd_reqs); #else // xen ver < 3.2 //printf("VBD: dev %d oo %d wr %d rd %d\n", vbds[i].dev, vbds[i].oo_reqs, vbds[i].wr_reqs, vbds[i].rd_reqs); #endif // VBD type, introduced http://xenbits.xensource.com/xen-3.2-testing.hg?rev/d47415adf384 // TODO consider for future use. // vbd.back_type = 1; //vbd type // vbd.back_type = 2; //tap type // this makes assumption that the driver domain is dom0; how to detect other driver domain? // will not support this for now char *backend = new_str("/local/domain/%d/device/vbd/%d/backend", domid, vbds[i].dev); char *base_path = xs_read(xhandle->xshandle, XBT_NULL, backend, NULL); char *dev_path = new_str("%s/dev", base_path); char *params_path = new_str("%s/params", base_path); char *mode_path = new_str("%s/mode", base_path); char *type_path = new_str("%s/type", base_path); char *node_path = new_str("%s/node", base_path); char *size_sector_path = new_str("%s/sector-size", base_path); //sectors in 3.0.4; check earlier and later (def. g. to xen hype) // says its size now char *num_sectors_path = new_str("%s/sectors", base_path); /* FIXME also present, if information is desired uuid = "de5009f7-c963-5569-7d10-4462fa15b560" physical-device = "7:2" online = "1" hotplug-status = "connected" info = "0" //flag indicates cdrom(1), removable(2), read only (4), 0=rw internal */ char *dev = xs_read(xhandle->xshandle, XBT_NULL, dev_path, NULL); char *params = xs_read(xhandle->xshandle, XBT_NULL, params_path, NULL); char *mode = xs_read(xhandle->xshandle, XBT_NULL, mode_path, NULL); char *type = xs_read(xhandle->xshandle, XBT_NULL, type_path, NULL); char *node = xs_read(xhandle->xshandle, XBT_NULL, node_path, NULL); char *size_sector = xs_read(xhandle->xshandle, XBT_NULL, size_sector_path, NULL); char *num_sectors = xs_read(xhandle->xshandle, XBT_NULL, num_sectors_path, NULL); if(type == NULL || mode == NULL || params == NULL || dev == NULL ){ //xenstore no longer reflects consistent domain definition //this is not a fatal error, it may be localized to one domain // (i.e. it rebooted or shut down, etc) return -1; } disks[i].internal_dev = strdup((char*)dev); disks[i].external_dev = strdup((char*)params); disks[i].mode = strdup((char*)mode); disks[i].type = strdup((char*)type); if(node) disks[i].mapped_dev = strdup((char*)node); else disks[i].mapped_dev = strdup("n/a"); // if (on older/misbehaving platforms) the data doesnt exist, use -1 as a DNE flag if(num_sectors) sscanf(num_sectors, "%d", &disks[i].num_sectors); else disks[i].num_sectors = -1; // same deal if(size_sector) sscanf(size_sector, "%d", &disks[i].size_sector); else disks[i].size_sector = -1; //sometimes param will have "aio:" prepending the image or similar // so try to get around that char *actualparams = strstr(params, ":"); //builds a device path pointer. // if node is defined, use it (for that is the mapped device for files, // e.g. /dev/loopX) // else use params, which is a path to a device /dev/sda, /dev/iscsi/blah, // /dev/someblockdevice accessed via phy: or tap:aio etc. // NOTE: if actualparams is defined, then the original params string // contained a colon, which often happens for tap:aio, whereby the // aio: prepends the actual device/file path. therefore, ++ the ptr // past the colon, and use it // else // just use params // char *device = (node) ? node : ( (actualparams) ? ++actualparams : params); int block_size = 0, start_sector = 0; //FIXME check return? get_device_data(device, &block_size, &start_sector); disks[i].start_sector = start_sector; disks[i].block_size = block_size; disks[i].partition_type = detect_partition_type(device); if(disks[i].partition_type == NULL) disks[i].partition_type = strdup("none"); free(dev_path); free(params_path); free(mode_path); free(type_path); free(node_path); free(num_sectors_path); free(size_sector_path); free(num_sectors); free(size_sector); free(params); free(mode); free(type); free(dev); free(node); free(base_path); free(backend); } return 0; } int get_vif_stats(xenstat_network *nets, int numnets, int domid, vif *vifs, unsigned long long *rxbw, unsigned long long *txbw, char *vmtype){ //network statistics //loop through xenstat_networks and collect statistics // using network.terr, txpackets, etc because it is // an abstraction above OS level specifics. // no need to dive down to /sys to get data; this is // instead handled by xenstat int i = 0; for(i=0; i < numnets; i++){ vifs[i].rbytes = nets[i].rbytes; vifs[i].rpackets = nets[i].rpackets; vifs[i].rerrs = nets[i].rerrs; vifs[i].rdrop = nets[i].rdrop; vifs[i].tbytes = nets[i].tbytes; vifs[i].tpackets = nets[i].tpackets; vifs[i].terrs = nets[i].terrs; vifs[i].tdrop = nets[i].tdrop; *rxbw += nets[i].rbytes; *txbw += nets[i].tbytes; //both hvm and pv //generic location; NETDEV is the # of the device being tested (0,1,2,3..) // if vifname exists, then the interface has that name. otherwise, // the assumption can be vif${DOMID}.${NETDEV} char *base_path = new_str("/local/domain/0/backend/vif/%d/%d", domid, nets[i].id); char *vifname_path = new_str("%s/vifname", base_path); //FIXME 3.0.2 lacks the bridge entry char *bridge_path = new_str("%s/bridge", base_path); char *script_path = new_str("%s/script", base_path); char *type_path = new_str("%s/type", base_path); char *mac_path = new_str("%s/mac", base_path); char *vifname = xs_read(xhandle->xshandle, XBT_NULL, vifname_path, NULL); char *bridge = xs_read(xhandle->xshandle, XBT_NULL, bridge_path, NULL); char *script = xs_read(xhandle->xshandle, XBT_NULL, script_path, NULL); char *type = xs_read(xhandle->xshandle, XBT_NULL, type_path, NULL); char *mac = xs_read(xhandle->xshandle, XBT_NULL, mac_path, NULL); if(vifname != NULL){ vifs[i].vifname = vifname; } else { #ifdef CONFIG_SunOS vifs[i].vifname = new_str("vnic%d.%d", domid, nets[i].id); #else vifs[i].vifname = new_str("vif%d.%d", domid, nets[i].id); #endif } if ( script != NULL ) { vifs[i].script = script; } else { vifs[i].script = new_str("%s", "default"); } if ( bridge != NULL ) { //vifs[i].bridge = strdup((char*)bridge); vifs[i].bridge = bridge; } else { //FIXME routed/custom is actually not determined by this //vifs[i].bridge = new_str("%s", "using routed or custom networking"); if(script && ( strstr(script, "vif-route") || strstr(script, "dummy")) ) vifs[i].bridge = new_str("%s", "using routed or custom networking"); else vifs[i].bridge = new_str("%s", "Using default bridge"); } if ( type != NULL ) { vifs[i].type = type; } else { vifs[i].type = new_str("%s", "netfront"); } if ( mac != NULL ) { vifs[i].mac = mac; } else { vifs[i].mac = new_str("%s", "unknown"); } free(vifname_path); free(bridge_path); free(script_path); free(type_path); free(mac_path); /* vifname, bridge, mac, type, script are NOT freed here because as * malloc'd char* they are used elsewhere. see vifs[i].bridge = bridge, etc free(vifname); free(bridge); free(script); free(type); free(mac); */ free(base_path); } // for ioemu devices of hvm domains (if recorded), we can inspect data here if( strcmp(vmtype, "hvm") == 0 ){ //FIXME hvm requires a bit of a hack to get network interface data. // xenstore lacks any represenation of the ioemu vif, and // thus we need to hook into the qemu-ifup script (for older systems), // or patch the qemu-dm code to otherwise add the dom->tap# correlation // to the domain's xenstore path. //FIXME accessing this data will get OS specific, seeing as we need to go direct // to the device to get stats. check libxenstat xenstat-${OS}.c code for // potential examples. //comma separated list of HVM tap devs //char *hvm_vif_hack = new_str("/local/domain/%d/device/vif-ioemu", domid); //if xs_read == NULL, this workaround is not in place } return 0; } // Cannot rely upon xenstat for determining UUID, so must access xenstore. // The xenstat handle contains a reference to the xenstore handle, and so // we can therefore read the uuid value. // // S. Maresca 1/5/08 char *get_uuid(unsigned int domain_id){ //FIXME to get domU path, this works just fine //printf("path %s\n", xs_get_domain_path(xhandle->xshandle, guests[j].domid)); //max 5 chars for the domid char path[43]; //32char + 4 hypens + null. char *uuid = (char *)malloc(sizeof(char)*37); uuid[0]=0; snprintf(path, sizeof(path),"/local/domain/%i/vm", domain_id); char *out = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); //skip the "/vm/" portion from the returned xenstore value strncpy(uuid, (out + 4), 36); uuid[36] = 0; free(out); return uuid; } char *get_vnc_port(unsigned int domain_id){ char path[43]; //max 5 chars for the domid char *none = new_str("N/A"); //domain 0 has no vnc port associated with it if(domain_id == 0) return none; //FIXME not seemingly a problem, but fyi: // if vnc port data is not being properly retrieved, // (probably just for pvfb devices), check the path // // /local/domain/0/backend/vfb/DOMID/0 -- port is in 'location' key // location may be worthwhile to note because it shows to what // interface the port is bound snprintf(path, sizeof(path),"/local/domain/%i/console/vnc-port", domain_id); char *out = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(out == NULL) return none; free(none); return out; } char *get_vnc_passwd(unsigned int domain_id){ /* FIXED * add compatibility code to this to support /local/domain/0/backend/vfb/${DOMID}/0/vncpasswd. note domid != uuid. * which appears to be the case in older 3.0.4 (vanilla) builds * that is the 'wrong' way to do it. divergence caused by the POC xenfb code of the time. * -moreover, 3.0.3-blah backports (and similar 3.0.4 backports in suse) probably do it the right way */ char *none = new_str("N/A"); char *blank = new_str("not set"); //domain 0 has no vnc port or passwd associated with it if( domain_id == 0) return none; char *path = new_str("/local/domain/0/backend/vfb/%d/0/vncpasswd", domain_id); char *out = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); free(path); if(out != NULL){ free(none); free(blank); //FIXME debuggging only: //printf("pass %s\n", out); return out; } else { free(none); free(out); return blank; } } char *get_vm_type(char *uuid){ char *host = "host"; char *pv = "para"; char *full = "full"; char *hybrid = "hybrid"; char *other = "other"; char *ret = NULL; if(strcmp(uuid, ZEROUUID) == 0){ ret = host; } else { char path[54]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/image/ostype' = 54 snprintf(path, sizeof(path), "/vm/%s/image/ostype", uuid); char *out = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); //FIXME THIS SHOULD NOT HAPPEN: XENSTORE ERROR // actually, it can happen between in the area between a domain restart and // revival. the old xenstore stuff is torn down and (until pages are unmapped and the domain destroyed) // the domid is still present. therefore we look into xenstore but nothing is present if( out == NULL){ char *msg = new_str("get_vm_type: output null and ostype path = %s .", path); if (DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); free(msg); ret = other; return strdup(other); } // ostype is ACTUALLY the vm type (hvm vs paravirt). // if the value of ostype is "linux" this indicates paravirt, so return that instead if(strcmp(out, "linux") == 0){ ret = pv; } else if(strcmp(out, "hvm") == 0){ ret = full; } else if(strcmp(out, "hvmwithpvdrivers")==0){ //FIXME assume we can detect this via // xenstore eventually ret = hybrid; } else { ret = other; } free(out); } return strdup(ret); } char *get_os_type(char *uuid){ char *unknown = strdup("Unknown"); char *type = NULL; if(strcmp(uuid, ZEROUUID) == 0){ struct utsname uname_pointer; if(uname(&uname_pointer) == -1){ return unknown; } type = uname_pointer.sysname; free(unknown); return type; } char path[47]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/image' = 47 snprintf(path, sizeof(path), "/vm/%s/image", uuid); char *image = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(image == NULL){ type = unknown; free(image); return type; } char *search = "GUEST_OS"; char *loc = NULL; loc = strstr(image, search); if(loc == 0){ type = unknown; } else { loc+=9; //advance loc ptr 9 spaces to bypass 'GUEST_OS ' int start = loc-image; int len = strlen(image); char *find = ")"; int pos; for(pos = loc-image; pos < len; pos++, loc++){ char *thischar = (char *)malloc(sizeof(char)*2); thischar[0] = 0; strncpy(thischar, loc, 1); thischar[1] = 0; if(strcmp(thischar, find) == 0){ //FIXME possible memory leak if we dont free before breaking loop free(thischar); break; } free(thischar); } int oslen = pos-start; //pos began == start, but has been advanced. type = (char *)malloc(sizeof(char)*oslen+1); //1 extra for null padding type[0] = 0; loc-=oslen;//back up loc ptr a bit and then copy the portion into type strncpy(type, loc, oslen); type[oslen] = 0; //null pad } free(image); return type; } char *get_kernel(char *uuid){ char path[54]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/image' + '/kernel' + null = 54 snprintf(path, sizeof(path), "/vm/%s/image/kernel", uuid); char * result = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(!result) return strdup(""); return result; } char *get_ramdisk(char *uuid){ char path[55]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/image' + '/ramdisk' + null = 55 snprintf(path, sizeof(path), "/vm/%s/image/ramdisk", uuid); char * result = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(!result) return strdup(""); return result; } char *get_kernel_cmdline(char *uuid){ char path[55]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/image' + '/cmdline' + null = 55 snprintf(path, sizeof(path), "/vm/%simage/cmdline", uuid); char * result = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(!result) return strdup(""); return result; } char *get_poweroff_action(char *uuid){ char path[53]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/on_poweroff' + null = 53 snprintf(path, sizeof(path), "/vm/%s/on_poweroff", uuid); char * result = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(!result) return strdup(""); return result; } char *get_crash_action(char *uuid){ char path[50]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/on_crash' + null = 50 snprintf(path, sizeof(path), "/vm/%s/on_crash", uuid); char * result = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(!result) return strdup(""); return result; } char *get_reboot_action(char *uuid){ char path[51]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/on_reboot' + null = 51 snprintf(path, sizeof(path), "/vm/%s/on_reboot", uuid); char * result = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(!result) return strdup(""); return result; } int get_uptime(char *uuid){ char path[52]; // '/vm/' = 4 + uuid (32 w/ 4 dashes) + '/start_time' + null = 52 snprintf(path, sizeof(path), "/vm/%s/start_time", uuid); char *start_time = xs_read(xhandle->xshandle, XBT_NULL, path, NULL); if(start_time){ double starttime = 0.0; sscanf(start_time, "%lf", &starttime); time_t now; now = time(NULL); free(start_time); return (int)now - (int)starttime; } return -1; } // Gets CPU percentage for a guest over time // via XMLPulse double get_cpu_pct(xenstat_domain *domain){ xenstat_domain *old_domain; double us_elapsed; // Can't calculate CPU percentage without a previous sample. if ( prev_node == NULL ) return 0.0; old_domain = xenstat_node_domain(prev_node, xenstat_domain_id(domain)); if ( old_domain == NULL ) return 0.0; // Calculate the time elapsed in microseconds us_elapsed = ((curtime.tv_sec-oldtime.tv_sec)*1000000.0 +(curtime.tv_usec - oldtime.tv_usec)); /* In the following, nanoseconds must be multiplied by 1000.0 to * convert to microseconds, then divided by 100.0 to get a percentage, * resulting in a multiplication by 10.0 */ return ((xenstat_domain_cpu_ns(domain)-xenstat_domain_cpu_ns(old_domain))/10.0)/us_elapsed; } // verify response from server // if error, return (as returncode) the faultcode from the method response // else if success, return the boolean value from the method response (0|1) // // NOTE: this function relies upon the assumption that the xmlrpc server returns // only TRUE/FALSE or fault for every methodcall int xmlrpc_check_response(char *response, char* returnmsg, int len){ //if no response data, fail if(response[0] == 0) return -1; int returncode = 0; if ( strstr(response, "faultCode") != NULL ){ //XMLRPC server error char *xmlintval = strstr(response, ""); char *xmlint = strstr(xmlintval, ""); if(sscanf(xmlint, "%d", &returncode) != 1) return -1; char *xmlfault = strstr(response, "faultString"); char *xmlstrval = strstr(xmlfault, ""); char *xmlstr = strstr(xmlstrval, ""); //NOTE %xxx must be <= len-1 so that we dont have an overflow in returnmsg! char *fmt = new_str("%%%d[^<]", len); if(sscanf(xmlstr, fmt, returnmsg) != 1) returnmsg = 0; free(fmt); return returncode; } char *xmlboolval = strstr(response, ""); char *xmlbool = strstr(xmlboolval, ""); if(sscanf(xmlbool, "%d", &returncode) != 1) return -1; return returncode; } //FIXME: this function may require some more dynamic memory allocations. defaults have been attempted that will be // useful, but further examination is necessary. perhaps use realloc ? int xmlrpc_call(char *method, char *params[], int len, char *resp){ int ret = 0; int count = 0; //FIXME use realloc instead of two step looping here char **aparam = params; for(;;aparam++){ if(*aparam == NULL) { break; } else { count += strlen(*aparam); } } char *temp = (char *)malloc(sizeof(char)*count+1024); temp[0] = 0; char **bparam = params; for(;;bparam++){ if(*bparam == NULL) { break; } else { strcat(temp, *bparam); } } //FIXME take note: this was the ~4 kbyte memory leak in this function //char *data = new_str("%s", xmlrpc_method_call(method, xmlrpc_params(temp))); char *theparams = xmlrpc_params(temp); char *thecall = xmlrpc_method_call(method, theparams); char *data = new_str("%s", thecall); /* POST %s HTTP/1.0 User-Agent: Zentific node polling daemon Host: %s Content-Type: text/xml Content-length: %d */ //char *header = new_str("POST %s HTTP/1.0\r\nUser-Agent: Zentific node polling daemon\r\nHost: %s\r\nContent-Type: text/xml\r\nContent-length: %d\r\n\r\n", XMLRPC_PATH, BACKEND_HOST, strlen(data)); char *header = NULL; if(session_id) header = new_str("POST %s HTTP/1.0\r\nUser-Agent: Zentific node polling daemon\r\nHost: %s\r\nCookie: sessid=%s;\r\nContent-Type: text/xml\r\nContent-length: %d\r\n\r\n", XMLRPC_PATH, BACKEND_HOST, session_id, strlen(data)); else header = new_str("POST %s HTTP/1.0\r\nUser-Agent: Zentific node polling daemon\r\nHost: %s\r\nContent-Type: text/xml\r\nContent-length: %d\r\n\r\n", XMLRPC_PATH, BACKEND_HOST, strlen(data)); char *fullmsg = new_str("%s%s\r\n\r\n", header, data); char *destination = new_str("%s:%d", BACKEND_HOST, PORT); char *response = (char *)malloc(sizeof(char)*len+1); //Just in case //FIXME: is there an overflow elsewhere that this bandaids? memset(response, 0, len+1); response[0]=0; if ( SEND_DATA ) if( USE_SSL ){ ret = ssl_send(fullmsg, destination, response, len, TRUST, IGNORE_TRUST_ERROR, 0); } else { ret = plaintext_send(fullmsg, destination, response, len); } else ret = 0; //ERROR testing for sending if(ret != 0){ char *msg = NULL; if(ret == 1){ msg = new_str("Error preparing client."); } else if(ret == 2){ msg = new_str( "Error loading trust store."); } else if(ret == 3){ msg = new_str( "Error connecting to host."); } else if(ret == 4){ msg = new_str( "Connection failed."); } else if(ret == 5){ msg = new_str( "FATAL ERROR: CERTIFICATE VALIDATION FAILED!"); } else if(ret == 6){ msg = new_str( "SSL Write failed."); } else if(ret == 7){ msg = new_str( "SSL Read failed."); } else { msg = new_str( "Unspecified error while sending XMLRPC call."); } if (DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); free(msg); return -1; } char *cookiestart = strstr(response, "Set-Cookie"); if(cookiestart){ char *sessid = malloc(sizeof(char)*37); memset(sessid, 0, 37); char *cookieend = strstr(cookiestart, "\r\n"); int cookielen = cookieend-cookiestart; char *cookie = (char *)malloc(sizeof(char)*cookielen+1); //1 extra for null padding cookie[0] = 0; strncpy(cookie, cookiestart, cookielen); cookie[cookielen] = 0; //null pad int howmany = sscanf(cookie, "Set-Cookie: sessid=%36s", sessid) ; if(cookie != NULL && howmany == 1) { if(DEBUG) fprintf(stderr, "Session id:%s\n", sessid); free(session_id); session_id = strdup(sessid); } else { //FIXME handling here? //if(DEBUG) fprintf(stderr, "\n\nSTARTOFCOOKIE%sENDOFCOOKIE\n\n howmany:%d\n", cookie, howmany); char *msg = "XML-RPC ERROR: session id not found in cookie"; if (DEBUG) fprintf(stderr, "%s\n", msg); log_msg(msg); } free(sessid); free(cookie); } char *methresp = NULL; if ( SEND_DATA ) methresp = strstr(response, ""); //DEBUGGING // FIXME possible corruption issue here when logging xml to file if ( XML_DEBUG ) { char *msg = NULL; if ( SEND_DATA ) msg = new_str( "Method call:\n----------------------\n%s\n\n" "Method Response:\n----------------------\n%s\n", fullmsg, methresp); else msg = new_str( "Method call:\n----------------------\n%s\n", fullmsg); fprintf(stdout, "%s\n", msg); log_msg(msg); free(msg); } //FIXME return specific error code for methresp // not found. fatal, causes abort if ( methresp == NULL && SEND_DATA ) return -1; if ( SEND_DATA ) { int pos = methresp-response; //FIXME this should probably be methresp copied to resp strncpy(resp, (response + pos), strlen(response)-1); if ( strlen(response) > len ) resp[len] = 0; else resp[strlen(response)] = 0; } free(temp); free(theparams); free(thecall); free(data); free(header); free(fullmsg); free(destination); free(response); // free(methresp); return 0; } // Display version information static void version(void) { printf("Zentific polling daemon - version %s - changeset %s\n" "\n%s\n\n""%s\n\n""%s\n\n", ZENTIFIC_POLL_VER, ZENTIFIC_CHANGESET, ZENTIFIC_COPYRIGHT, ZENTIFIC_DISCLAIMER, ZENTIFIC_CONTACT); } // Display usage syntax // FIXME: update to current static void usage(char *prog){ printf("\n\nZentific polling daemon\n" "----------------------------------------------------------------------\n" "Usage: %s [OPTION]\n\n" "-h(elp) : This message\n" "-H(ost) : Send node updates to host\n" "-P(ort) : Send node updates to host:port\n" "-D(estination) : Send node updates to /server/path\n" "-p(idfile) : Record PID in specified filename\n" "-s(sl) : Use SSL for transport of updates {yes|no}\n" "-d(elay) : Delay value in seconds between updates\n" "-V(erbose) : Do not daemonize, display debug output\n" "-v(ersion) : Info for this version, bugs, contact, etc.\n" "-n(osend) : Do not send XML updates to the backend.\n" "-X(MLdebug) : Display raw XML data for debugging.\n" "\n", prog ); printf("----------------------------------------------------------------------\n"); version(); } // Loads the config file /etc/zentific/node.conf and parses the value // assignments as configuration options int parse_config() { //FIXME: need more aggressive type checking FILE *h = fopen(CONFIG_FILE, "r"); if(h == NULL){ fprintf(stderr, "ERROR: %s does not exist. Please explicitly provide parameters on the command line.\n", CONFIG_FILE); return -1; } char line[1024]; int line_count = 0; while ( fgets(line, 1024, h) != NULL ) { // Keep track of line numbers for error reporting line_count++; // Remove all leading whitespace from the line char *ptr = line; // this is guaranteed to end at some point because fgets guarantees the last char is '\0' while ( ptr[0] == ' ' || ptr[0] == '\t' ) ptr++; // Ignore lines beginning with a #, as they are comments if ( ptr[0] != '#' && ptr[0] != '\n' && ptr[0] != '\r' && ptr[0] != '\0' ) { int c; // Make sure that there is an actual variable assignment in the line if ( strchr(line, '=') == NULL ) { fprintf(stderr, "ERROR: mal-formed line in config file on line [%d].\n", line_count); exit(1); } // Separate the assignment around the first equals sign char *name = ptr; char *value = ptr; for ( c = 0; c < strlen(ptr); c++ ) { if ( ptr[c] == '=' ) { ptr[c] = '\0'; if ( ptr[c+1] == '\"' || ptr[c+1] == '\'' ){ int chunklen = strrchr(&ptr[c+2], '\"') -&ptr[c+2]; char *out = (char *)malloc(sizeof(char)*chunklen+1); out[0] = 0; strncpy(out, &ptr[c+2], chunklen); out[chunklen] = 0; value = out; } else { value = &ptr[c+1]; } } else if ( ptr[c] == '\r' || ptr[c] == '\n' ) { ptr[c] = '\0'; } } // Handle a BACKEND_HOST assignment if ( strcmp(name, "BACKEND_HOST") == 0 ) { strncpy(BACKEND_HOST, value, HOST_NAME_MAX); BACKEND_HOST[HOST_NAME_MAX+1] = '\0'; } // Handle a PORT assignment else if ( strcmp(name, "PORT") == 0 ) { //FIXME typechecking PORT = atoi(value); } // Handle a NODE_UUID assignment else if ( strcmp(name, "NODE_UUID") == 0 ) { if(strcasecmp(value, "detect")){ NODE_UUID = strdup(value); } else if(is_uuid(value) == 0){ fprintf(stderr, "ERROR: NODE_UUID value %s on line [%d] is invalid. Terminating.\n", value, line_count); cleanup_exit(1); } NODE_UUID = strdup(value); } // Handle a DELAY assignment else if ( strcmp(name, "DELAY") == 0 ) { //FIXME typechecking DELAY = atoi(value); } // Handle a USE_SSL assignment // FIXME else if ( strcmp(name, "USE_SSL") == 0 ) { if( strcmp(strlwr(value), "yes") == 0 ) { USE_SSL = 1; } else if( strcmp(strlwr(value), "no") == 0 ) { USE_SSL = 0; } else { fprintf(stderr, "ERROR: USE_SSL config option %s on line [%d] is invalid. Terminating.\n", value, line_count); cleanup_exit(1); } } // Handle a PID_FILE assignment else if ( strcmp(name, "PID_FILE") == 0 ) { strncpy(PID_FILE, value, PATH_MAX); PID_FILE[PATH_MAX+1] = '\0'; } // Handle a username assignment else if ( strcmp(name, "XMLRPC_PATH") == 0 ) { strncpy(XMLRPC_PATH, value, PATH_MAX); XMLRPC_PATH[PATH_MAX+1] = '\0'; } // Handle a username assignment else if ( strcmp(name, "USERNAME") == 0 ) { username = strdup(value); } // Handle a password assignment else if ( strcmp(name, "PASSWORD") == 0 ) { password = strdup(value); } // Handle a LOG_TYPE assignment // FIXME:dont use contatns in strncpy, use variables assigned elsewhere else if ( strcmp(name, "LOG_TYPE") == 0 ) { strncpy(LOG_TYPE, value, 128); LOG_TYPE[127] = '\0'; } // Handle an unknown assignment else { fprintf(stderr, "ERROR: Unrecognized config option %s on line [%d].\n", value, line_count); } } } fclose(h); return 0; } // This action assumes that sending a SIGHUP will override all config options // with the values inside of the config file (if existing) void config_reload(int signum) { RELOAD_CONFIG = 1; } // Called on a SIGINT so we can clean up before exitting void cleanup_exit(int signum) { //close file descriptors and free the bits of the handle xenstat_uninit(xhandle); unlink(PID_FILE); if(signum == 1){ log_msg("Exiting due to errors."); } else { log_msg("Exiting cleanly."); } // Since we overrode the default SIGINT action, we have to force the exit on // our own now. exit(signum); } /*************************************/ /**********UTLITY FUNCTIONS***********/ /*************************************/ char *strlwr(char *s){ if (s != NULL) { char *p; for (p = s; *p; ++p) *p = tolower(*p); } return s; } char *xmlrpc_int(int value){ return new_str("%i", value); } char *xmlrpc_str_int(char *value){ return new_str("%s", value); } //FIXME this is a little overly constrained in terms of input type char *xmlrpc_double(unsigned long long value){ return new_str("%.2lf", (double)value); } char *xmlrpc_string(char *value){ return new_str("%s", value); } char *xmlrpc_struct_member(char *name, char *value){ return new_str("%s%s", name, value); } char *xmlrpc_param(char *value){ return new_str("%s", value); } char *xmlrpc_struct(char *value){ return new_str("%s", value); } char *xmlrpc_params(char *value){ return new_str("%s", value); } char *xmlrpc_method_call(char *value, char *params){ return new_str( "%s%s", value, params); } char *xmlrpc_array(char *value){ return new_str( "%s", value); } char *xmlrpc_value(char *value){ return new_str( "%s", value); } // // From the printf manpage // oh-so-convenient and probably slower than it could be, but // close enough for this application // char *new_str(const char *fmt, ...) { /* Guess we need no more than 100 bytes. */ int n, size = 100; char *p, *np; va_list ap; if ((p = malloc (size)) == NULL) return NULL; while (1) { /* Try to print in the allocated space. */ va_start(ap, fmt); n = vsnprintf (p, size, fmt, ap); va_end(ap); /* If that worked, return the string. */ if (n > -1 && n < size) return p; /* Else try again with more space. */ if (n > -1) /* glibc 2.1 */ size = n+1; /* precisely what is needed */ else /* glibc 2.0 */ size *= 2; /* twice the old size */ if ((np = realloc (p, size)) == NULL) { free(p); return NULL; } else { p = np; } } } unsigned long long get_intr () { char line[1024]; unsigned long long intr = 0; FILE *fp; if (!(fp = fopen("/proc/stat", "r"))){ char *msg = "Failed /proc/stat open"; log_msg(msg); fprintf(stderr, "%s\n", msg); free(msg); } while(!feof(fp)) { fgets(line,sizeof(line),fp); char *item = strtok(line, " "); if(strcmp(item, "intr") == 0){ char *this = strtok(NULL, " "); sscanf(this, "%llu", &intr); break; } } fclose(fp); return intr; } unsigned long long get_ctxt () { char line[1024]; unsigned long long ctxt = 0; FILE *fp; if (!(fp = fopen("/proc/stat", "r"))){ char *msg = "Failed /proc/stat open"; log_msg(msg); fprintf(stderr, "%s\n", msg); free(msg); } while(!feof(fp)) { fgets(line,sizeof(line),fp); char *item = strtok(line, " "); if(strcmp(item, "ctxt") == 0){ char *this = strtok(NULL, " "); sscanf(this, "%llu", &ctxt); break; } } fclose(fp); return ctxt; } float get_iowait () { char line[1024]; unsigned long long idle = 0; unsigned long long user = 0; unsigned long long system = 0; unsigned long long iowait = 0; unsigned long long userlow = 0; FILE *fp; if (!(fp = fopen("/proc/stat", "r"))){ char *msg = "Failed /proc/stat open"; log_msg(msg); fprintf(stderr, "%s\n", msg); free(msg); } while(!feof(fp)) { fgets(line,sizeof(line),fp); char *item = strtok(line, " "); if(strncmp(item, "cpu", 3) == 0){ char *this = strtok(NULL, ""); sscanf(this, "%llu %llu %llu %llu %llu", &user, &userlow, &system, &idle, &iowait); break; } } //printf(" user %llu, userlow %llu, system %llu, idle %llu, iowait %llu\n", user, userlow, system, idle, iowait); //pct = ((time waiting for io)/(total cputime))*100 float pct = (((float)iowait)/(float)(user+userlow+system+idle))*100; fclose(fp); return pct; } int match_regex(const char* str, const char* regex) { if (str == 0) //null input return -1; regex_t r; int rs; if ( regcomp(&r, regex, REG_EXTENDED|REG_NOSUB) ) //regex compilation failed return -2; rs = regexec(&r, str, 0, 0, 0); regfree(&r); if (rs != 0) //regex match failed return -3; //we have a match! return 0; } int is_uuid(char *uuid) { int match = match_regex(uuid, "(^[a-fA-F0-9]{32}$)|(^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$)"); if(match == 0) return 1; else //FIXME better error handling here fprintf(stderr, "match failed for %s, err %d\n", uuid, match); return 0; } char *str_append(char *dest, char *source){ char *new = NULL; if(dest && source) new = new_str("%s%s", dest, source); else if(source) new = strdup(source); else new = NULL; free(dest); dest = NULL; return new; } /* TODO / FIXME LIST * * - fix edge cases where a domU disappears AFTER update_stats() has * already started. fetching that domain fails because xenstore has * pruned it * * - fix xmlrpc_check_response return flow * * - dmidecode Not Present failsafe usage of 0-uuid for dom0 is stopgap only * need more uniqueness. use MAC? * * - update ssl_send.* * make sure BIO_read doesnt die. investigate causes. see acm_dom0 * * - linked list of observed domains? loop through to verify present or absent. * this fixes edge cases where last observed state was $BLAH (and then * crash occured or something). could also (alternatively) use last updated * timestamp in place of this * * - add authentication support (with handling to reauth if expired) * * - zlib compression of outgoing data to reduce overhead * (roughly 12:1) * * - xmlrpc_check_response may need to pad logged messages w/ newline * */