/* 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
*
*/