"Fossies" - the Fresh Open Source Software Archive

Member "procmeter3-3.6+svn387/modules/battery.c" (6 Apr 2012, 25559 Bytes) of package /linux/misc/procmeter3-3.6+svn387.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "battery.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3-3.6_vs_3-3.6+svn387.

    1 /***************************************
    2   ProcMeter - A system monitoring program for Linux
    3 
    4   battery values from /sys/ (for example new-style ACPI)
    5 
    6   This file Copyright 2011, 2012 Bernhard R. Link
    7   Modified 2012 by Andrew M. Bishop.
    8 
    9   It may be distributed under the GNU Public License, version 2, or
   10   any higher version.  See section COPYING of the GNU Public license
   11   for conditions under which this file may be redistributed.
   12   ***************************************/
   13 
   14 #define _GNU_SOURCE 1
   15 #include <unistd.h>
   16 #include <stdio.h>
   17 #include <stdlib.h>
   18 #include <string.h>
   19 #include <dirent.h>
   20 #include <stdbool.h>
   21 #include <fcntl.h>
   22 #include <assert.h>
   23 #include <errno.h>
   24 
   25 #include "procmeter.h"
   26 
   27 static bool debug_battery = false;
   28 
   29 static ProcMeterModule module = {
   30  /* char name[];       */ "Battery",
   31  /* char *description; */ "battery information from /sys/class/power_supply/",
   32 };
   33 
   34 ProcMeterModule *Load(void) {
   35         return(&module);
   36 }
   37 
   38 #ifndef __USE_ATFILE
   39 #warning sysbattery module not available, as most likely not supported (__USE_ATFILE not defined)
   40 
   41 void Unload(void) {
   42 }
   43 
   44 ProcMeterOutput **Initialise(char *options) {
   45         static ProcMeterOutput *empty_list[] = { NULL };
   46 
   47         return empty_list;
   48 }
   49 
   50 int Update(time_t now,ProcMeterOutput *output) {
   51         return -1;
   52 }
   53 #else
   54 
   55 const static struct battery_field {
   56         const char *name;
   57         enum batteryfieldtype {
   58                 bft_yesno, bft_string, bft_count,
   59                 bft_muA, bft_muAh, bft_v }
   60                         fieldtype;
   61         bool variable, required;
   62         const char *description, *name_template;
   63 } fields[] = {
   64 #define FIELD_OFS_STATUS 0
   65         { "status", bft_string, true, true,
   66                 "Status (charging/discharging).", "%s_state"},
   67 #define FIELD_OFS_CURRENT 1
   68         { "current_now", bft_muA, true, true,
   69                 "Current charging/discharging rate.", "%s_rate"},
   70 #define FIELD_OFS_CHARGE 2
   71         { "charge_now", bft_muAh, true, true,
   72                 "Current charge of the battery.", "%s_charge"},
   73 #define FIELD_OFS_FULL 3
   74         { "charge_full", bft_muAh, false, true,
   75                 "Last full charge of the battery.", "%s_last full"},
   76 #define FIELD_OFS_DESIGN_FULL 4
   77         { "charge_full_design", bft_muAh, false, false,
   78                 "Designed full charge of the battery.", "%s_design full"},
   79 //      { "cycle_count", bft_count, false, false,
   80 //              "Content of the 'cycle_count' file.", "%s_cycle count"},
   81         { "manufacturer", bft_string, false, false,
   82                 "Manufacturer.", "%s_manufacturer"},
   83         { "model_name", bft_string, false, false,
   84                 "Model name.", "%s_model name"},
   85         { "serial_number", bft_string, false, false,
   86                 "Serial number.", "%s_serial number"},
   87         { "technology", bft_string, false, false,
   88                 "Technology.", "%s_technology"},
   89         { "voltage_min_design", bft_v, false, false,
   90                 "Minimum design voltage.", "%s_min voltage"},
   91         { "voltage_now", bft_v, true, false,
   92                 "Current voltage.", "%s_voltage"},
   93         { NULL, 0}
   94 };
   95 
   96 #define BAT_OUTPUT_COUNT 15
   97 
   98 static struct battery {
   99         struct battery *next;
  100         char *name;
  101         char *directory;
  102         int fd;
  103         int output_count;
  104         enum battery_state {
  105                 /* not present (or can no longer be read) */
  106                 battery_unavailable,
  107                 /* present, but unread since available */
  108                 battery_unread,
  109                 /* present and charging */
  110                 battery_charging,
  111                 /* present and discharging */
  112                 battery_discharging,
  113                 /* present but strange as neither of the above */
  114                 battery_unknown
  115         } state;
  116         time_t lastupdated;
  117         long long lastcurrent, lastcharge, lastfull, designfull;
  118         struct field {
  119                 struct battery *parent;
  120                 const struct battery_field *field;
  121                 time_t lastupdated;
  122                 ProcMeterOutput output;
  123         } fields[BAT_OUTPUT_COUNT];
  124 } *batteries = NULL;
  125 
  126 static void free_batteries(struct battery *b) {
  127         while (b != NULL) {
  128                 struct battery *h = b;
  129                 b = h->next;
  130 
  131                 close(h->fd);
  132                 free(h->directory);
  133                 free(h);
  134         }
  135 }
  136 
  137 static char *dirconcat(const char *dir, const char *name) {
  138         size_t dl, nl;
  139         char *r;
  140 
  141         dl = strlen(dir);
  142         nl = strlen(name);
  143         if (dl > 0 && dir[dl-1] == '/')
  144                 dl--;
  145         r = malloc(dl + nl + 2);
  146         if (r == NULL) {
  147                 fputs("Out of Memory\n", stderr);
  148                 return NULL;
  149         }
  150         memcpy(r, dir, dl);
  151         r[dl] = '/';
  152         memcpy(r + dl + 1, name, nl+1);
  153         return r;
  154 }
  155 
  156 /* this assumes there are no other symlinks than the given one
  157  * (otherwise the ../ shortening does not work) */
  158 static char *canonify_link(const char *basedir, int dirfd, const char *linkname) {
  159         char *r, *buffer = 0;
  160         size_t bufsize = 512;
  161         ssize_t got;
  162         const char *e, *s;
  163 
  164         do {
  165                 char *h;
  166                 h = realloc(buffer, bufsize);
  167                 if (h == NULL) {
  168                         free(buffer);
  169                         return dirconcat(basedir, linkname);
  170                         fputs("Out of Memory\n", stderr);
  171                 }
  172                 buffer = h;
  173                 memset(buffer, 0, bufsize);
  174 
  175                 got = readlinkat(dirfd, linkname, buffer, bufsize - 1);
  176                 if (got < 0) {
  177                         if (debug_battery) {
  178                                 fprintf(stderr, "Error %d reading link '%s' in '%s': %s\n",
  179                                                 errno, linkname, basedir,
  180                                                 strerror(errno));
  181                         }
  182                         free(buffer);
  183                         return dirconcat(basedir, linkname);
  184                 }
  185                 if (got >= bufsize) {
  186                         bufsize = got + 2;
  187                         continue;
  188                 }
  189         } while (false);
  190         e = basedir + strlen(basedir);
  191         if (e <= basedir) {
  192                 free(buffer);
  193                 if (debug_battery) {
  194                         fputs("Confused in pathname canonisation!\n", stderr);
  195                 }
  196                 return dirconcat(basedir, linkname);
  197         }
  198         e--;
  199         while (e > basedir && *e == '/')
  200                 e--;
  201         s = buffer;
  202         while (s[0] == '.') {
  203                 if (s[1] == '/') {
  204                         s += 2;
  205                         continue;
  206                 }
  207                 if (s[1] != '.' || s[2] != '/')
  208                         break;
  209                 s += 3;
  210                 while (e > basedir && *e != '/')
  211                         e--;
  212                 if (*e != '/') {
  213                         free(buffer);
  214                         if (debug_battery) {
  215                                 fputs("Confused in pathname canonisation!\n", stderr);
  216                         }
  217                         return dirconcat(basedir, linkname);
  218                 }
  219                 while (e > basedir && *e == '/')
  220                         e--;
  221         }
  222         e++;
  223         r = malloc((e-basedir) + strlen(s) + 2);
  224         if (r == NULL) {
  225                 free(buffer);
  226                 fputs("Out of Memory\n", stderr);
  227                 return dirconcat(basedir, linkname);
  228         }
  229         memcpy(r, basedir, e-basedir);
  230         r[e-basedir] = '/';
  231         strcpy(r + (e-basedir) + 1, s);
  232         free(buffer);
  233         return r;
  234 }
  235 
  236 static bool read_file(int dfd, const char *dirname, const char *filename, char *buffer, size_t buflen) {
  237         int fd;
  238         ssize_t got;
  239 
  240         fd = openat(dfd, filename, O_NOFOLLOW|O_RDONLY);
  241         if (fd < 0) {
  242                 if (debug_battery) {
  243                         fprintf(stderr, "Error %d opening file '%s' in '%s': %s\n",
  244                                         errno, filename, dirname,
  245                                         strerror(errno));
  246                 }
  247                 return false;
  248         }
  249         memset(buffer, 0, buflen);
  250         /* TODO: are /sys file garanteed to be read in one read? */
  251         got = read(fd, buffer, buflen-1);
  252         close(fd);
  253         if (got < 0 || got >= buflen) {
  254                 if (debug_battery) {
  255                         fprintf(stderr, "Error %d reading file '%s' in '%s': %s\n",
  256                                         errno, filename, dirname,
  257                                         strerror(errno));
  258                 }
  259                 return false;
  260         }
  261         while (got > 0 && buffer[got-1] == '\n')
  262                 buffer[--got] = '\0';
  263         return true;
  264 }
  265 
  266 
  267 
  268 static bool is_battery(int dirfd, const char *dirname) {
  269         char value[9];
  270 
  271         if (!read_file(dirfd, dirname, "type", value, sizeof(value)))
  272                 return false;
  273         return memcmp(value, "Battery", 8) == 0;
  274 }
  275 
  276 static struct field *new_output(struct battery *bat, const char *name, const char *description, char type, int interval) {
  277         struct field *out;
  278 
  279         out = &bat->fields[bat->output_count++];
  280         assert ( bat->output_count <= BAT_OUTPUT_COUNT);
  281         memset(out, 0, sizeof(ProcMeterOutput));
  282 
  283         out->parent = bat;
  284         snprintf(out->output.name, PROCMETER_NAME_LEN, name, bat->name);
  285         out->output.description = (char *)description;
  286         out->output.type = type;
  287         out->output.interval = interval;
  288         out->output.graph_scale = 1;
  289         strcpy(out->output.text_value, "not available");
  290         return out;
  291 }
  292 
  293 static bool fill_outputs(struct battery *bat, int dirfd) {
  294         struct field *f;
  295         const struct battery_field *bf;
  296 
  297         memset(bat->fields, 0, sizeof(bat->fields));
  298         bat->output_count = 0;
  299 
  300 #define OUTPUT_OFS_PERCENT 0
  301         f = new_output(bat, "%s_percent", "The percentage of design charge in the battery.", PROCMETER_TEXT|PROCMETER_BAR|PROCMETER_GRAPH, 10);
  302         if (f == NULL)
  303                 return false;
  304         strcpy(f->output.text_value, "??%");
  305         strcpy(f->output.graph_units, "(%d%)");
  306         f->output.graph_scale = 20;
  307 #define OUTPUT_OFS_TIMELEFT 1
  308         f = new_output(bat, "%s_remaining", "Time left till charged or discharged.", PROCMETER_TEXT, 10);
  309         if (f == NULL)
  310                 return false;
  311         strcpy(f->output.text_value, "??:??:??");
  312 
  313 #define OUTPUT_OFS_FIELDS 2
  314         for (bf = fields ; bf->name != NULL ; bf++) {
  315                 int fd;
  316 
  317                 fd = openat(dirfd, bf->name, O_NOFOLLOW|O_RDONLY);
  318                 if (fd < 0) {
  319                         if (bf->required)
  320                                 return false;
  321                         continue;
  322                 }
  323                 close(fd);
  324                 f = new_output(bat, bf->name_template,
  325                                bf->description, PROCMETER_TEXT, bf->variable ? 5 : 60);
  326                 f->field = bf;
  327                 if (f == NULL)
  328                         return false;
  329                 switch (bf->fieldtype) {
  330                         case bft_muAh:
  331                                 if(bf->variable)
  332                                         f->output.type = PROCMETER_TEXT|PROCMETER_BAR|PROCMETER_GRAPH;
  333                                 strcpy(f->output.graph_units, "(%dmAh)");
  334                                 f->output.graph_scale = 100;
  335                                 break;
  336                         case bft_muA:
  337                                 f->output.type = PROCMETER_TEXT|PROCMETER_BAR|PROCMETER_GRAPH;
  338                                 strcpy(f->output.graph_units, "(%dmA)");
  339                                 f->output.graph_scale = 100;
  340                                 break;
  341                         case bft_v:
  342                                 if(bf->variable)
  343                                         f->output.type = PROCMETER_TEXT|PROCMETER_BAR|PROCMETER_GRAPH;
  344                                 strcpy(f->output.graph_units, "(%dV)");
  345                 f->output.graph_scale = 1;
  346                                 break;
  347                         default:
  348                                 break;
  349                 }
  350         }
  351         return true;
  352 };
  353 
  354 static struct battery *find_batteries(const char *basepath, bool presentonly) {
  355         struct battery *list = NULL;
  356         struct dirent *ent;
  357         DIR *dir;
  358         int dir_fd;
  359 
  360         if (basepath == NULL)
  361                 return NULL;
  362 
  363         dir = opendir(basepath);
  364         if (dir == NULL)
  365                 return NULL;
  366 
  367         dir_fd = dirfd(dir);
  368         if (dir_fd == -1)
  369                 return NULL;
  370 
  371         while ((ent = readdir(dir)) != NULL) {
  372                 struct battery *n;
  373                 char *dirname;
  374                 int fd;
  375                 char value[9];
  376                 bool present;
  377 
  378                 if (ent->d_name[0] == '.')
  379                         continue;
  380                 /* /sys should have d_type set, so no lstat dance */
  381                 if (ent->d_type != DT_LNK)
  382                         continue;
  383                 dirname = canonify_link(basepath, dir_fd, ent->d_name);
  384                 fd = open(dirname, O_RDONLY|O_DIRECTORY);
  385                 if (fd < 0) {
  386                         free(dirname);
  387                         continue;
  388                 }
  389                 if (!is_battery(fd, dirname)) {
  390                         close(fd);
  391                         free(dirname);
  392                         continue;
  393                 }
  394 
  395                 if (!read_file(fd, dirname, "present", value, sizeof(value))) {
  396                         close(fd);
  397                         free(dirname);
  398                         continue;
  399                 }
  400                 present = value[0] == '1' && value[1] == '\0';
  401                 if (presentonly && !present) {
  402                         close(fd);
  403                         free(dirname);
  404                         continue;
  405                 }
  406                 n = calloc(sizeof(struct battery), 1);
  407                 if (n == NULL) {
  408                         close(fd);
  409                         free(dirname);
  410                         continue;
  411                 }
  412                 n->directory = dirname;
  413                 dirname = NULL;
  414                 n->name = strdup(ent->d_name);
  415                 n->fd = fd;
  416                 n->state = present ? battery_unread : battery_unavailable;
  417                 if (!fill_outputs(n, fd)) {
  418                         free_batteries(n);
  419                         continue;
  420                 }
  421                 n->next = list;
  422                 list = n;
  423         }
  424         closedir(dir);
  425         return list;
  426 }
  427 
  428 static ProcMeterOutput **output_list = NULL;
  429 
  430 ProcMeterOutput **Initialise(char *options) {
  431         struct battery *b;
  432         ProcMeterOutput **o;
  433         int count;
  434         bool only_present = false;
  435 
  436         if (options != NULL && strstr(options, "debug") != NULL) {
  437                 debug_battery = true;
  438                 fprintf(stderr, "enabled debug mode\n");
  439         }
  440         if (options != NULL && strstr(options, "onlypresent") != NULL) {
  441                 only_present = true;
  442         }
  443         free_batteries(batteries);
  444         batteries = find_batteries("/sys/class/power_supply", only_present);
  445         count = 0;
  446         for (b = batteries ; b != NULL ; b = b->next)
  447                 count += b->output_count;
  448         output_list = calloc(sizeof(ProcMeterOutput *), count + 1);
  449         if (output_list == NULL)
  450                 return NULL;
  451         o = output_list;
  452         for (b = batteries ; b != NULL ; b = b->next) {
  453                 for (count = 0 ; count < b->output_count ; count++ ) {
  454                         *(o++) = &b->fields[count].output;
  455                 }
  456         }
  457         return(output_list);
  458 }
  459 
  460 static inline void battery_setstate(struct battery *bat, enum battery_state state) {
  461         if (bat->state == state)
  462                 return;
  463         bat->state = state;
  464         if (state == battery_unavailable) {
  465                 /* everything unavailable anyway... */
  466                 return;
  467         } else if (state == battery_unread) {
  468                 int i;
  469 
  470                 for (i = 0 ; i < bat->output_count ; i++) {
  471                         bat->fields[i].lastupdated = 0;
  472                 }
  473         } else
  474                 /* don't use an old rate if switching between
  475                  * charging and discharging or back */
  476                 bat->fields[OUTPUT_OFS_FIELDS + FIELD_OFS_CURRENT].lastupdated = 0;
  477 }
  478 
  479 void update_presence(time_t timenow, struct battery *bat) {
  480         char value[9];
  481         bool present;
  482 
  483         if (bat->lastupdated != 0 && bat->lastupdated == timenow)
  484                 return;
  485         bat->lastupdated = timenow;
  486         if (bat->fd < 0 || !read_file(bat->fd, bat->directory, "present", value, sizeof(value))) {
  487                 if (bat->fd >= 0)
  488                         close(bat->fd);
  489                 bat->fd = open(bat->directory, O_RDONLY|O_DIRECTORY);
  490                 if (bat->fd < 0 || !read_file(bat->fd, bat->directory, "present", value, sizeof(value))) {
  491                         battery_setstate(bat, battery_unavailable);
  492                         return;
  493                 }
  494         }
  495         present = value[0] == '1' && value[1] == '\0';
  496         if (present) {
  497                 if (bat->state == battery_unavailable) {
  498                         /* battery newly available, invalidate all data */
  499                         battery_setstate(bat, battery_unread);
  500                 }
  501         } else {
  502                 battery_setstate(bat, battery_unavailable);
  503         }
  504 }
  505 
  506 static int update_field(time_t timenow, struct field *f) {
  507         struct battery *bat = f->parent;
  508 
  509         update_presence(timenow, bat);
  510         if (bat->state == battery_unavailable) {
  511                 f->output.graph_value = 0;
  512                 strcpy(f->output.text_value, "not available");
  513                 return 0;
  514         }
  515         if (f->lastupdated != 0) {
  516                 if (f->lastupdated <= timenow && f->lastupdated + 5 > timenow)
  517                         return 0;
  518                 if (!f->field->variable && f->lastupdated + 60 > timenow)
  519                         return 0;
  520         }
  521 
  522         if (f->field != NULL) {
  523                 const struct battery_field *bf = f->field;
  524                 char text[PROCMETER_TEXT_LEN + 3];
  525                 long long l;
  526 
  527                 if (!read_file(bat->fd, bat->directory,
  528                                         bf->name, text, PROCMETER_TEXT_LEN))
  529                         return -1;
  530 
  531                 switch (bf->fieldtype) {
  532                         case bft_string:
  533                                 if ((f - bat->fields) == OUTPUT_OFS_FIELDS +
  534                                                 FIELD_OFS_STATUS) {
  535                                         if (strcmp(text, "Discharging") == 0)
  536                                                 battery_setstate(bat,
  537                                                         battery_discharging);
  538                                         else if (strcmp(text, "Charging") == 0)
  539                                                 battery_setstate(bat,
  540                                                         battery_charging);
  541                                         else
  542                                                 battery_setstate(bat,
  543                                                         battery_unknown);
  544 
  545                                 }
  546                                 memcpy(f->output.text_value, text, PROCMETER_TEXT_LEN);
  547                                 f->lastupdated = timenow;
  548                                 return 0;
  549                         default:
  550                                 break;
  551                 }
  552                 l = atoll(text);
  553                 if (f - bat->fields == OUTPUT_OFS_FIELDS + FIELD_OFS_CURRENT) {
  554                         bat->lastcurrent = l;
  555                 } else if (f - bat->fields == OUTPUT_OFS_FIELDS + FIELD_OFS_CHARGE) {
  556                         bat->lastcharge = l;
  557                 } else if (f - bat->fields == OUTPUT_OFS_FIELDS + FIELD_OFS_FULL) {
  558                         bat->lastfull = l;
  559         } else if (f - bat->fields == OUTPUT_OFS_FIELDS + FIELD_OFS_DESIGN_FULL) {
  560             bat->designfull = l;
  561                 }
  562                 switch (bf->fieldtype) {
  563                         case bft_string:
  564                                 assert (false);
  565                                 break;
  566                         case bft_muAh:
  567                                 f->output.graph_value = (l*PROCMETER_GRAPH_SCALE)/100000;
  568                                 snprintf(f->output.text_value, PROCMETER_TEXT_LEN,
  569                         "%.0f mAh", (double)l / 1000.0);
  570                                 break;
  571                         case bft_muA:
  572                                 f->output.graph_value = (l*PROCMETER_GRAPH_SCALE)/100000;
  573                                 snprintf(f->output.text_value, PROCMETER_TEXT_LEN,
  574                         "%.0f mA", (double)l / 1000.0);
  575                                 break;
  576                         case bft_v:
  577                                 f->output.graph_value = (l*PROCMETER_GRAPH_SCALE)/100000;
  578                                 snprintf(f->output.text_value, PROCMETER_TEXT_LEN,
  579                                                 "%.1f V", (double)l / 1000000.0);
  580                                 break;
  581                         case bft_yesno:
  582                                 f->output.graph_value = l;
  583                                 if (l == 0)
  584                                         strcpy(f->output.text_value, "no");
  585                                 else if (l == 1)
  586                                         strcpy(f->output.text_value, "yes");
  587                                 else
  588                                         snprintf(f->output.text_value, PROCMETER_TEXT_LEN,
  589                                                 "unknown (%lld)", l);
  590                                 break;
  591                         case bft_count:
  592                                 f->output.graph_value = l;
  593                                 memcpy(f->output.text_value, text, PROCMETER_TEXT_LEN);
  594                                 break;
  595                 }
  596         } else if (f - bat->fields == OUTPUT_OFS_PERCENT) {
  597                 unsigned int percent;
  598 
  599                 if (update_field(timenow, bat->fields + OUTPUT_OFS_FIELDS
  600                                 + FIELD_OFS_DESIGN_FULL) != 0)
  601                         return -1;
  602                 if (update_field(timenow, bat->fields + OUTPUT_OFS_FIELDS
  603                                 + FIELD_OFS_CHARGE) != 0)
  604                         return -1;
  605                 if (bat->lastfull == 0) {
  606                         strcpy(f->output.text_value, "not available");
  607                         return 0;
  608                 }
  609                 percent = ((PROCMETER_GRAPH_SCALE*100)*bat->lastcharge)/
  610                                         bat->designfull;
  611                 snprintf(f->output.text_value, PROCMETER_TEXT_LEN,
  612                                 "%u%%", (unsigned int)(percent /
  613                                         PROCMETER_GRAPH_SCALE));
  614                 f->output.graph_value = percent;
  615         } else if (f - bat->fields == OUTPUT_OFS_TIMELEFT) {
  616                 long seconds, minutes, hours;
  617 
  618                 if (update_field(timenow, bat->fields + OUTPUT_OFS_FIELDS
  619                                 + FIELD_OFS_STATUS) != 0)
  620                         return -1;
  621                 if (bat->state != battery_charging &&
  622                                 bat->state != battery_discharging) {
  623                         strcpy(f->output.text_value, "not available");
  624                         return 0;
  625                 }
  626                 if (update_field(timenow, bat->fields + OUTPUT_OFS_FIELDS
  627                                         + FIELD_OFS_CURRENT) != 0)
  628                         return -1;
  629                 if (bat->lastcurrent == 0) {
  630                         strcpy(f->output.text_value, "never");
  631                         return 0;
  632                 }
  633                 if (update_field(timenow, bat->fields + OUTPUT_OFS_FIELDS
  634                                 + FIELD_OFS_CHARGE) != 0)
  635                         return -1;
  636                 if (bat->state == battery_discharging) {
  637                         seconds = (3600 * bat->lastcharge) / bat->lastcurrent;
  638                 } else {
  639                         long long left;
  640                         if (update_field(timenow, bat->fields + OUTPUT_OFS_FIELDS
  641                                                 + FIELD_OFS_FULL) != 0)
  642                                 return -1;
  643                         left = bat->lastfull - bat->lastcharge;
  644                         seconds = (3600 * left) / bat->lastcurrent;
  645                 }
  646                 minutes = seconds / 60;
  647                 seconds = seconds % 60;
  648                 hours = minutes / 60;
  649                 minutes = minutes % 60;
  650                 snprintf(f->output.text_value, PROCMETER_TEXT_LEN,
  651                                 "%2lu:%02lu:%02lu", hours, minutes, seconds);
  652         }
  653 
  654         return 0;
  655 }
  656 
  657 #define get_container(container, field, pointer) (struct container *)((char *)pointer - (((char *)&((struct container *)NULL)->field)-(char*)NULL))
  658 
  659 int Update(time_t timenow, ProcMeterOutput *o) {
  660         /* ugly trick around ProcMeterOutput not having a privdata */
  661         return update_field(timenow, get_container(field, output, o));
  662 }
  663 
  664 void Unload(void)
  665 {
  666         free_batteries(batteries);
  667         batteries = NULL;
  668         free(output_list);
  669         output_list = NULL;
  670 }
  671 #endif