//Creates a xenstore battery dir with the specified index if it doesn't already exist. static void make_xenstore_battery_dir(unsigned int battery_index) { char xenstore_path[256]; char ** dir_entries; unsigned int num_entries, i; bool flag; dir_entries = xenstore_ls(&num_entries, "/pm"); if (!dir_entries) { xcpmd_log(LOG_WARNING, "Listing directory /pm failed with error `%s'\n", strerror(errno)); return; } snprintf(xenstore_path, 255, "%s%i", XS_BATTERY_PATH, battery_index); flag = false; for (i = 0; i < num_entries; ++i) { if (!strcmp(dir_entries[i], xenstore_path)) { flag = true; break; } } if (!flag) xenstore_mkdir(xenstore_path); //xenstore_ls() calls malloc(), so be sure to free(). free(dir_entries); }
//Finds the maximum battery index that exists in the sysfs. static int get_max_battery_index(void) { int max_index, index; DIR * dir; struct dirent * dp; dir = opendir(BATTERY_DIR_PATH); if (!dir) { xcpmd_log(LOG_ERR, "opendir in get_max_battery_index failed for directory %s with error %d\n", BATTERY_DIR_PATH, errno); return -1; } max_index = -1; while ((dp = readdir(dir)) != NULL) { if (!strncmp(dp->d_name, "BAT", 3)) { index = get_terminal_number(dp->d_name); if (index > -1 && index > max_index) { max_index = index; } } } closedir(dir); return max_index; }
//Allocates memory! Frees *str1 if it's not null! //Places a pointer to the concatenation of *str1 and the sprintf of the //remaining args into str1. void safe_str_append(char ** str1, char * format, ...) { if (str1 == NULL || format == NULL) return; int length; char *formatted, *concatted; va_list args; va_start(args, format); length = vsnprintf(NULL, 0, format, args) + 1; formatted = (char *)malloc(length * sizeof(char)); if (formatted == NULL) { xcpmd_log(LOG_ERR, "Couldn't allocate memory\n"); return; } vsnprintf(formatted, length, format, args); va_end(args); if (*str1 == NULL) { *str1 = formatted; } else { concatted = safe_sprintf("%s%s", *str1, formatted); free(*str1); free(formatted); *str1 = concatted; } }
//Retrieves a property over DBus. Returns 0 on failure or 1 on success. int dbus_get_property(xcdbus_conn_t * xc_conn, const char * service, const char * path, const char * interface, const char * property, GValue * outv) { GError *error = NULL; GValue var = G_VALUE_INIT; DBusGProxy *p = xcdbus_get_proxy(xc_conn, service, path, "org.freedesktop.DBus.Properties"); if (!p) { xcpmd_log(LOG_DEBUG, "Failed to get dbusgproxy"); return 0; } if (!dbus_g_proxy_call(p, "Get", &error, G_TYPE_STRING, interface, G_TYPE_STRING, property, G_TYPE_INVALID, G_TYPE_VALUE, &var, G_TYPE_INVALID )) { xcpmd_log(LOG_DEBUG, "proxy call failed: %s", error->message); return 0; } *outv = var; return 1; }
//Exactly what it says on the tin. void write_battery_info_to_xenstore(unsigned int battery_index) { if (battery_slot_exists(battery_index) == NO || battery_index >= num_battery_structs_allocd) { xcpmd_log(LOG_INFO, "Detected removal of battery slot %d in info.\n", battery_index); cleanup_removed_battery(battery_index); return; } struct battery_info * info; char bif[1024], string_info[256], xenstore_path[128]; info = &last_info[battery_index]; memset(bif, 0, 1024); memset(string_info, 0, 256); // write 9 dwords (so 9*4) + length of 4 strings + 4 null terminators snprintf(bif, 3, "%02x", (unsigned int)(9*4 + strlen(info->model_number) + strlen(info->serial_number) + strlen(info->battery_type) + strlen(info->oem_info) + 4)); write_ulong_lsb_first(bif+2, info->power_unit); write_ulong_lsb_first(bif+10, info->design_capacity); write_ulong_lsb_first(bif+18, info->last_full_capacity); write_ulong_lsb_first(bif+26, info->battery_technology); write_ulong_lsb_first(bif+34, info->design_voltage); write_ulong_lsb_first(bif+42, info->design_capacity_warning); write_ulong_lsb_first(bif+50, info->design_capacity_low); write_ulong_lsb_first(bif+58, info->capacity_granularity_1); write_ulong_lsb_first(bif+66, info->capacity_granularity_2); snprintf(string_info, 256, "%02x%s%02x%s%02x%s%02x%s", (unsigned int)strlen(info->model_number), info->model_number, (unsigned int)strlen(info->serial_number), info->serial_number, (unsigned int)strlen(info->battery_type), info->battery_type, (unsigned int)strlen(info->oem_info), info->oem_info); strncat(bif+73, string_info, 1024-73-1); //Ensure the directory exists before trying to write the leaves make_xenstore_battery_dir(battery_index); //Now write the leaves. snprintf(xenstore_path, 255, "%s%i/%s", XS_BATTERY_PATH, battery_index, XS_BIF_LEAF); xenstore_write(bif, xenstore_path); //Here for compatibility--will be removed eventually if (battery_index == 0) xenstore_write(bif, XS_BIF); else xenstore_write(bif, XS_BIF1); }
//Adds a DBus match for the specified string and registers a filter function, //with optional argument func_data and optional function free_func that will //be called on func_data when this match is removed. //Returns 0 on failure or 1 on success. //TODO: Cache DBus connection, since libxcdbus doesn't. int add_dbus_filter(char * match, DBusHandleMessageFunction filter_func, void * func_data, DBusFreeFunction free_func) { DBusConnection * connection; if (xcdbus_conn == NULL) { xcpmd_log(LOG_WARNING, "xcdbus connection is unavailable"); return 0; } connection = xcdbus_get_dbus_connection(xcdbus_conn); if (connection == NULL) { xcpmd_log(LOG_WARNING, "Unable to get DBus connection from xcdbus"); return 0; } dbus_bus_add_match(connection, match, NULL); if (!dbus_connection_add_filter(connection, filter_func, func_data, free_func)) { xcpmd_log(LOG_ERR, "Couldn't add DBus filter--not enough memory"); return 0; } return 1; }
//Allocates memory! //Deep clones a string. Don't forget to free() later. char * clone_string(char * string) { char * clone; int length; length = strlen(string) + 1; clone = (char *)malloc(length * sizeof(char)); if (clone == NULL) { xcpmd_log(LOG_ERR, "Couldn't allocate memory\n"); return NULL; } strcpy(clone, string); return clone; }
//Allocates memory! //Allocates an appropriately sized string and prints to it. char * safe_sprintf(char * format, ...) { int length; char * string; va_list args; va_start(args, format); length = vsnprintf(NULL, 0, format, args) + 1; string = (char *)malloc(length * sizeof(char)); if (string == NULL) { xcpmd_log(LOG_ERR, "Couldn't allocate memory\n"); return NULL; } vsnprintf(string, length, format, args); va_end(args); return string; }
//Removes a DBus filter previously added with add_dbus_filter. //Returns 0 on failure or 1 on success. //TODO: Cache DBus connection, since libxcdbus doesn't. int remove_dbus_filter(char * match, DBusHandleMessageFunction filter_func, void * func_data) { DBusConnection * connection; //Don't segfault if something else has already torn down the DBus connection. if (xcdbus_conn == NULL) { //xcpmd_log(LOG_WARNING, "Tried removing filter, but xcdbus connection is unavailable"); return 0; } connection = xcdbus_get_dbus_connection(xcdbus_conn); if (connection == NULL) { xcpmd_log(LOG_WARNING, "Unable to get dbus connection from xcdbus"); return 0; } dbus_bus_remove_match(connection, match, NULL); dbus_connection_remove_filter(connection, filter_func, func_data); return 1; }
//Counts the number of batteries present in the sysfs. int get_num_batteries_present(void) { int count; DIR * dir; struct dirent * dp; FILE * file; char data[128]; char filename[289]; dir = opendir(BATTERY_DIR_PATH); if (!dir) { xcpmd_log(LOG_ERR, "opendir in get_num_batteries_present() failed for directory %s with error %d\n", BATTERY_DIR_PATH, errno); return 0; } //Check all /sys/class/power_supply/BAT*/present. count = 0; while ((dp = readdir(dir)) != NULL) { if (!strncmp(dp->d_name, "BAT", 3)) { snprintf(filename, sizeof (filename), "%s/%s/present", BATTERY_DIR_PATH, dp->d_name); file = fopen(filename, "r"); if (file == NULL) continue; fgets(data, sizeof(data), file); fclose(file); if (strstr(data, "1")) { ++count; } } } closedir(dir); return count; }
//Counts the number of battery slots in the sysfs. int get_num_batteries(void) { int count = 0; DIR * dir; struct dirent * dp; dir = opendir(BATTERY_DIR_PATH); if (!dir) { xcpmd_log(LOG_ERR, "opendir in get_num_batteries failed for directory %s with error %d\n", BATTERY_DIR_PATH, errno); return 0; } //Count all entries whose names start with BAT. while ((dp = readdir(dir)) != NULL) { if (!strncmp(dp->d_name, "BAT", 3)) { ++count; } } closedir(dir); return count; }
//Updates status and info of all batteries locally and in the xenstore. void update_batteries(void) { struct battery_status *old_status = NULL; struct battery_info *old_info = NULL; char path[256]; unsigned int old_num_batteries = 0; unsigned int num_batteries = 0; unsigned int i, new_array_size, old_array_size, num_batteries_to_update; bool present_batteries_changed = false; if ( pm_specs & PM_SPEC_NO_BATTERIES ) return; //Keep a copy of what the battery status/info used to be. old_status = (struct battery_status *)malloc(num_battery_structs_allocd * sizeof(struct battery_status)); old_info = (struct battery_info *)malloc(num_battery_structs_allocd * sizeof(struct battery_info)); if (last_status != NULL) memcpy(old_status, last_status, num_battery_structs_allocd * sizeof(struct battery_status)); else memset(old_status, 0, num_battery_structs_allocd * sizeof(struct battery_status)); if (last_info != NULL) memcpy(old_info, last_info, num_battery_structs_allocd * sizeof(struct battery_info)); else memset(old_info, 0, num_battery_structs_allocd * sizeof(struct battery_info)); old_array_size = num_battery_structs_allocd; //Resize the arrays if necessary. new_array_size = (unsigned int)(get_max_battery_index() + 1); if (new_array_size != old_array_size) { if (new_array_size == 0) { xcpmd_log(LOG_INFO, "All batteries removed.\n"); free(last_info); free(last_status); } else { last_info = (struct battery_info *)realloc(last_info, new_array_size * sizeof(struct battery_info)); last_status = (struct battery_status *)realloc(last_status, new_array_size * sizeof(struct battery_status)); memset(last_info, 0, new_array_size * sizeof(struct battery_info)); memset(last_status, 0, new_array_size * sizeof(struct battery_status)); } num_battery_structs_allocd = new_array_size; } num_batteries_to_update = (new_array_size > old_array_size) ? new_array_size : old_array_size; //Updating all status/info before writing to the xenstore prevents bad //calculations of aggregate data (e.g., warning level). for (i=0; i < num_batteries_to_update; ++i) { update_battery_status(i); update_battery_info(i); } //Write back to the xenstore and only send notifications if things have changed. for (i=0; i < num_batteries_to_update; ++i) { //No need to update status/info in Xenstore if there was no battery to begin with. // On some latops, batteries index are not contiguous. It is not a big // deal to have one or two empty array slot, but it should not be // reported as a removed battery (OXT-614). if (last_status[i].present || old_status[i].present) { write_battery_status_to_xenstore(i); write_battery_info_to_xenstore(i); } if (i < old_array_size && i < new_array_size) { if (memcmp(&old_info[i], &last_info[i], sizeof(struct battery_info))) { snprintf(path, 255, "%s%i/%s", XS_BATTERY_EVENT_PATH, i, XS_BATTERY_INFO_EVENT_LEAF); xenstore_write("1", path); } if (memcmp(&old_status[i], &last_status[i], sizeof(struct battery_status))) { snprintf(path, 255, "%s%i/%s", XS_BATTERY_EVENT_PATH, i, XS_BATTERY_STATUS_EVENT_LEAF); xenstore_write("1", path); } if (old_status[i].present == YES) ++old_num_batteries; if (last_status[i].present == YES) ++num_batteries; if (old_status[i].present != last_status[i].present) present_batteries_changed = true; } else if (new_array_size > old_array_size) { //a battery has been added snprintf(path, 255, "%s%i/%s", XS_BATTERY_EVENT_PATH, i, XS_BATTERY_INFO_EVENT_LEAF); xenstore_write("1", path); snprintf(path, 255, "%s%i/%s", XS_BATTERY_EVENT_PATH, i, XS_BATTERY_STATUS_EVENT_LEAF); xenstore_write("1", path); if (last_status[i].present == YES) ++num_batteries; if (i < old_array_size) { if (old_status[i].present != last_status[i].present) present_batteries_changed = true; } else { if (last_status[i].present == YES) present_batteries_changed = true; } } else if (new_array_size < old_array_size) { //a battery has been removed snprintf(path, 255, "%s%i/%s", XS_BATTERY_EVENT_PATH, i, XS_BATTERY_INFO_EVENT_LEAF); xenstore_write("1", path); snprintf(path, 255, "%s%i/%s", XS_BATTERY_EVENT_PATH, i, XS_BATTERY_STATUS_EVENT_LEAF); xenstore_write("1", path); if (old_status[i].present == YES) ++old_num_batteries; if (i < new_array_size) { if (old_status[i].present != last_status[i].present) present_batteries_changed = true; } else { if (old_status[i].present == YES) present_batteries_changed = true; } } } if ((old_array_size != new_array_size) || (memcmp(old_info, last_info, new_array_size * sizeof(struct battery_info)))) { notify_com_citrix_xenclient_xcpmd_battery_info_changed(xcdbus_conn, XCPMD_SERVICE, XCPMD_PATH); } if ((old_array_size != new_array_size) || (memcmp(old_status, last_status, new_array_size * sizeof(struct battery_status)))) { //Here for compatibility--should eventually be removed xenstore_write("1", XS_BATTERY_STATUS_CHANGE_EVENT_PATH); notify_com_citrix_xenclient_xcpmd_battery_status_changed(xcdbus_conn, XCPMD_SERVICE, XCPMD_PATH); } if (present_batteries_changed) { notify_com_citrix_xenclient_xcpmd_num_batteries_changed(xcdbus_conn, XCPMD_SERVICE, XCPMD_PATH); } free(old_info); free(old_status); }
//Exactly what it says on the tin. void write_battery_status_to_xenstore(unsigned int battery_index) { struct battery_status * status; char bst[35], xenstore_path[128]; int num_batteries, current_battery_level; if (battery_index >= num_battery_structs_allocd) { cleanup_removed_battery(battery_index); } num_batteries = get_num_batteries_present(); if (num_batteries == 0) { xenstore_write("0", XS_BATTERY_PRESENT); return; } else { xenstore_write("1", XS_BATTERY_PRESENT); } status = &last_status[battery_index]; //Delete the BST and reset the "present" flag if the battery is not currently present. if (status->present != YES) { snprintf(xenstore_path, 255, "%s%i/%s", XS_BATTERY_PATH, battery_index, XS_BST_LEAF); xenstore_rm(xenstore_path); snprintf(xenstore_path, 255, "%s%i/%s", XS_BATTERY_PATH, battery_index, XS_BATTERY_PRESENT_LEAF); xenstore_write("0", xenstore_path); return; } //Build the BST structure. memset(bst, 0, 35); snprintf(bst, 3, "%02x", 16); write_ulong_lsb_first(bst+2, status->state); write_ulong_lsb_first(bst+10, status->present_rate); write_ulong_lsb_first(bst+18, status->remaining_capacity); write_ulong_lsb_first(bst+26, status->present_voltage); //Ensure the directory exists before trying to write the leaves make_xenstore_battery_dir(battery_index); //Now write the leaves. snprintf(xenstore_path, 255, XS_BATTERY_PATH "%i/" XS_BST_LEAF, battery_index); xenstore_write(bst, xenstore_path); snprintf(xenstore_path, 255, "%s%i/%s", XS_BATTERY_PATH, battery_index, XS_BATTERY_PRESENT_LEAF); xenstore_write("1", xenstore_path); //Here for compatibility--will be removed eventually if (battery_index == 0) xenstore_write(bst, XS_BST); else xenstore_write(bst, XS_BST1); current_battery_level = get_current_battery_level(); if (current_battery_level == NORMAL || get_ac_adapter_status() == ON_AC) xenstore_rm(XS_CURRENT_BATTERY_LEVEL); else { xenstore_write_int(current_battery_level, XS_CURRENT_BATTERY_LEVEL); notify_com_citrix_xenclient_xcpmd_battery_level_notification(xcdbus_conn, XCPMD_SERVICE, XCPMD_PATH); xcpmd_log(LOG_ALERT, "Battery level below normal - %d!\n", current_battery_level); } #ifdef XCPMD_DEBUG xcpmd_log(LOG_DEBUG, "~Updated battery information in xenstore\n"); #endif }
//Gets a battery's info from the sysfs and stores it in last_info. int update_battery_info(unsigned int battery_index) { DIR *battery_dir; struct dirent * dp; FILE *file; char filename[295]; char data[128]; char *ptr; struct battery_info info; memset(&info, 0, sizeof(struct battery_info)); if (battery_index >= num_battery_structs_allocd) return 0; if (battery_slot_exists(battery_index) == NO) { memcpy(&last_info[battery_index], &info, sizeof(struct battery_info)); return 0; } battery_dir = get_battery_dir(battery_index); if (!battery_dir) { xcpmd_log(LOG_ERR, "opendir in update_battery_info() for directory %s/BAT%d failed with error %d\n", BATTERY_DIR_PATH, battery_index, errno); return 0; } //Loop over the files in the directory. while ((dp = readdir(battery_dir)) != NULL) { //Convert from dirent to file and read out the data. if (dp->d_type == DT_REG) { memset(filename, 0, sizeof(filename)); snprintf(filename, sizeof (filename), "%s/BAT%u/%s", BATTERY_DIR_PATH, battery_index, dp->d_name); file = fopen(filename, "r"); if (file == NULL) continue; memset(data, 0, sizeof(data)); fgets(data, sizeof(data), file); fclose(file); //Trim off leading spaces. ptr = data; while(*ptr == ' ') ptr += sizeof(char); //Set the attribute represented by this file. set_battery_info_attribute(dp->d_name, ptr, &info); } } //In sysfs, the charge nodes are for batteries reporting in mA and //the energy nodes are for mW. if (info.charge_full_design != 0) { info.power_unit = mA; info.design_capacity = info.charge_full_design; info.last_full_capacity = info.charge_full; } else { info.power_unit = mW; info.design_capacity = info.energy_full_design; info.last_full_capacity = info.energy_full; } //Unlike the old procfs files, sysfs does not report some values like the //warn and low levels. These values are generally ignored anyway. The //various OS's decide what to do at different depletion levels through //their own policies. These are just some approximate values to pass. //TODO govern these by policy info.design_capacity_warning = info.last_full_capacity * (BATTERY_WARNING_PERCENT / 100); info.design_capacity_low = info.last_full_capacity * (BATTERY_LOW_PERCENT / 100); info.capacity_granularity_1 = 1; info.capacity_granularity_2 = 1; closedir(battery_dir); memcpy(&last_info[battery_index], &info, sizeof(struct battery_info)); return 1; }
//Allocates memory! //Creates a new VM identifier table from a GPtrArray of VMs as retrieved from //DBus. Does not modify the global vm identifier table. struct vm_identifier_table * new_vm_identifier_table(GPtrArray * vm_list) { struct vm_identifier_table * table; char * tmp; char * vm; unsigned int i; if (vm_list == NULL) { return NULL; } //Alloc the table itself. table = (struct vm_identifier_table *)calloc(1, sizeof(struct vm_identifier_table)); table->num_entries = vm_list->len; table->entries = (struct vm_identifier_table_row *)calloc(table->num_entries, sizeof(struct vm_identifier_table_row)); if ((table == NULL) || (table->entries == NULL)) { xcpmd_log(LOG_ERR, "Failed to allocate memory\n"); free_vm_identifier_table(table); return NULL; } //Create a table row for each entry in the GPtrArray of VM paths. for (i = 0; i < table->num_entries; ++i) { vm = g_ptr_array_index(vm_list, i); //Get the VM name. property_get_com_citrix_xenclient_xenmgr_vm_name_(xcdbus_conn, XENMGR_SERVICE, vm, &tmp); if(tmp == NULL) { xcpmd_log(LOG_ERR, "Error: Couldn't get name of %s.\n", vm); free_vm_identifier_table(table); return NULL; } table->entries[i].name = (char *)malloc(strlen(tmp) + 1); if (table->entries[i].name == NULL) { xcpmd_log(LOG_ERR, "Failed to allocate memory\n"); free_vm_identifier_table(table); return NULL; } strcpy(table->entries[i].name, tmp); //Copy the VM path. table->entries[i].path = (char *)malloc(VM_PATH_LEN + 1); //path_len = 40 = 32 path bytes + 4 underscores + "/vm/" (4), and 1 byte for \0 if (table->entries[i].path == NULL) { xcpmd_log(LOG_ERR, "Failed to allocate memory\n"); free_vm_identifier_table(table); return NULL; } strncpy(table->entries[i].path, vm, VM_PATH_LEN + 1); //Extract the VM UUID from the path. table->entries[i].uuid = (char *)malloc(VM_PATH_LEN - VM_PATH_UUID_PREFIX_LEN + 1); //path_len = 36 = 32 path bytes + 4 hyphens, and 1 byte for \0 if (table->entries[i].uuid == NULL) { xcpmd_log(LOG_ERR, "Failed to allocate memory\n"); free_vm_identifier_table(table); return NULL; } strncpy(table->entries[i].uuid, vm+VM_PATH_UUID_PREFIX_LEN, VM_PATH_LEN - VM_PATH_UUID_PREFIX_LEN + 1); //Convert _ to - in uuid //000000000011111111112222222222333333 //012345678901234567890123456789012345 //12345678-1234-1234-1234-123456789012 table->entries[i].uuid[8] = '-'; table->entries[i].uuid[13] = '-'; table->entries[i].uuid[18] = '-'; table->entries[i].uuid[23] = '-'; } return table; }
int main(int argc, char *argv[]) { int ret = 0; #ifndef RUN_STANDALONE openlog("xcpmd", 0, LOG_DAEMON); daemonize(); #endif xcpmd_log(LOG_INFO, "Starting XenClient power management daemon.\n"); //Initialize libevent library event_init(); //Initialize xenstore. if (xenstore_init() == -1) { xcpmd_log(LOG_ERR, "Unable to init xenstore\n"); return -1; } // Allow everyone to read from /pm/ in xenstore xenstore_rm("/pm"); xenstore_mkdir("/pm"); xenstore_chmod("r0", 1, "/pm"); initialize_platform_info(); xcpmd_log(LOG_INFO, "Starting DBUS server.\n"); if (xcpmd_dbus_initialize() == -1) { xcpmd_log(LOG_ERR, "Failed to initialize DBUS server\n"); goto xcpmd_err; } xcpmd_log(LOG_INFO, "Starting ACPI events monitor.\n"); if (acpi_events_initialize() == -1) { xcpmd_log(LOG_ERR, "Failed to initialize ACPI events monitor\n"); goto xcpmd_err; } // Load modules xcpmd_log(LOG_INFO, "Loading modules.\n"); if (init_modules() == -1) { xcpmd_log(LOG_ERR, "Failed to load all modules\n"); goto xcpmd_err; } //This relies on both acpi-events and acpi-module having been initialized xcpmd_log(LOG_INFO, "Initializing ACPI state.\n"); acpi_initialize_state(); // Load policy xcpmd_log(LOG_INFO, "Loading policy.\n"); if (load_policy_from_db() == -1) { xcpmd_log(LOG_WARNING, "Error loading policy from DB; continuing...\n"); } #ifdef POLICY_FILE_PATH if (load_policy_from_file(POLICY_FILE_PATH) == -1) { xcpmd_log(LOG_WARNING, "Error loading policy from file %s; continuing...\n", POLICY_FILE_PATH); } #endif #ifdef XCPMD_DEBUG xcpmd_log(LOG_DEBUG, "Rules loaded:\n"); print_rules(); #endif xcpmd_log(LOG_INFO, "Entering event loop.\n"); event_dispatch(); goto xcpmd_out; xcpmd_err: ret = -1; xcpmd_out: uninit_modules(); acpi_events_cleanup(); xcpmd_dbus_cleanup(); #ifndef RUN_STANDALONE closelog(); #endif return ret; }
//Gets a battery's status from the sysfs and stores it in last_status. int update_battery_status(unsigned int battery_index) { DIR *battery_dir; struct dirent * dp; FILE *file; char filename[295]; char data[128]; char *ptr; struct battery_status status; memset(&status, 0, sizeof(struct battery_status)); if (battery_index >= num_battery_structs_allocd) return -1; if (battery_slot_exists(battery_index) == NO) { status.present = NO; memcpy(&last_status[battery_index], &status, sizeof(struct battery_status)); return 1; } battery_dir = get_battery_dir(battery_index); if (!battery_dir) { //Battery directory does not exist--this normally occurs when a battery slot is removed if (errno == ENOENT) { status.present = NO; memcpy(&last_status[battery_index], &status, sizeof(struct battery_status)); return 1; } else { xcpmd_log(LOG_ERR, "opendir in update_battery_status for directory %s/BAT%d failed with error %d\n", BATTERY_DIR_PATH, battery_index, errno); return 0; } } //Loop over the files in the directory. while ((dp = readdir(battery_dir)) != NULL) { //Convert from dirent to file and read out the data. if (dp->d_type == DT_REG) { snprintf(filename, sizeof (filename), "%s/BAT%u/%s", BATTERY_DIR_PATH, battery_index, dp->d_name); file = fopen(filename, "r"); if (file == NULL) continue; memset(data, 0, sizeof(data)); fgets(data, sizeof(data), file); fclose(file); //Trim off leading spaces. ptr = data; while(*ptr == ' ') ptr += sizeof(char); //Set the attribute represented by this file. set_battery_status_attribute(dp->d_name, ptr, &status); } } // This check handles both cases for mA batteries: if are not charging // (current_now == 0) but have capacity or the battery is totally dead // (charge_now == 0) but it is charging. If both are zero, they will // both be zero in the end. if (status.charge_now != 0 || status.current_now != 0) { // Rate in mA, remaining in mAh status.present_rate = status.current_now; status.remaining_capacity = status.charge_now; } else { // Rate in mW, remaining in mWh status.present_rate = status.power_now; status.remaining_capacity = status.energy_now; } closedir(battery_dir); memcpy(&last_status[battery_index], &status, sizeof(struct battery_status)); #ifdef XCPMD_DEBUG print_battery_status(battery_index); #endif return 1; }