1 /*
2  * xenpmd.c
3  *
4  * xen power management daemon - Facilitates power management
5  * functionality within xen guests.
6  *
7  * Copyright (c) 2008  Kamala Narasimhan
8  * Copyright (c) 2008  Citrix Systems, Inc.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 /* Xen extended power management support provides HVM guest power management
25  * features beyond S3, S4, S5.  For example, it helps expose system level
26  * battery status and battery meter information and in future will be extended
27  * to include more power management support.  This extended power management
28  * support is enabled by setting xen_extended_power_mgmt to 1 or 2 in the HVM
29  * config file.  When set to 2, non-pass through mode is enabled which heavily
30  * relies on this power management daemon to glean battery information from
31  * dom0 and store it xenstore which would then be queries and used by qemu and
32  * passed to the guest when appropriate battery ports are read/written to.
33  */
34 
35 #define _GNU_SOURCE         /* for asprintf() */
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <dirent.h>
41 #include <unistd.h>
42 #include <sys/stat.h>
43 #include <xenstore.h>
44 #include <assert.h>
45 
46 /* #define RUN_STANDALONE */
47 #define RUN_IN_SIMULATE_MODE
48 
49 enum BATTERY_INFO_TYPE {
50     BIF,
51     BST
52 };
53 
54 enum BATTERY_PRESENT {
55     NO,
56     YES
57 };
58 
59 enum BATTERY_TECHNOLOGY {
60     NON_RECHARGEABLE,
61     RECHARGEABLE
62 };
63 
64 struct battery_info {
65     enum BATTERY_PRESENT    present;
66     unsigned long           design_capacity;
67     unsigned long           last_full_capacity;
68     enum BATTERY_TECHNOLOGY battery_technology;
69     unsigned long           design_voltage;
70     unsigned long           design_capacity_warning;
71     unsigned long           design_capacity_low;
72     unsigned long           capacity_granularity_1;
73     unsigned long           capacity_granularity_2;
74     char                    model_number[32];
75     char                    serial_number[32];
76     char                    battery_type[32];
77     char                    oem_info[32];
78 };
79 
80 struct battery_status {
81     enum BATTERY_PRESENT    present;
82     unsigned long           state;
83     unsigned long           present_rate;
84     unsigned long           remaining_capacity;
85     unsigned long           present_voltage;
86 };
87 
88 static struct xs_handle *xs;
89 
90 #ifdef RUN_IN_SIMULATE_MODE
91     #define BATTERY_DIR_PATH "/tmp/battery"
92     #define BATTERY_INFO_FILE_PATH "/tmp/battery/%s/info"
93     #define BATTERY_STATE_FILE_PATH "/tmp/battery/%s/state"
94 #else
95     #define BATTERY_DIR_PATH "/proc/acpi/battery"
96     #define BATTERY_INFO_FILE_PATH "/proc/acpi/battery/%s/info"
97     #define BATTERY_STATE_FILE_PATH "/proc/acpi/battery/%s/state"
98 #endif
99 
get_next_battery_file(DIR * battery_dir,enum BATTERY_INFO_TYPE battery_info_type)100 FILE *get_next_battery_file(DIR *battery_dir,
101                             enum BATTERY_INFO_TYPE battery_info_type)
102 {
103     FILE *file = 0;
104     struct dirent *dir_entries;
105     char *file_name;
106     int ret;
107 
108     do
109     {
110         dir_entries = readdir(battery_dir);
111         if ( !dir_entries )
112             return 0;
113         if ( strlen(dir_entries->d_name) < 4 )
114             continue;
115         if ( battery_info_type == BIF )
116             ret = asprintf(&file_name, BATTERY_INFO_FILE_PATH,
117                      dir_entries->d_name);
118         else
119             ret = asprintf(&file_name, BATTERY_STATE_FILE_PATH,
120                      dir_entries->d_name);
121         /* This should not happen but is needed to pass gcc checks */
122         if (ret < 0)
123             continue;
124         file = fopen(file_name, "r");
125         free(file_name);
126     } while ( !file );
127 
128     return file;
129 }
130 
set_attribute_battery_info(char * attrib_name,char * attrib_value,struct battery_info * info)131 void set_attribute_battery_info(char *attrib_name,
132                                 char *attrib_value,
133                                 struct battery_info *info)
134 {
135     if ( strstr(attrib_name, "present") )
136     {
137         if ( strstr(attrib_value, "yes") )
138             info->present = YES;
139         return;
140     }
141 
142     if ( strstr(attrib_name, "design capacity warning") )
143     {
144         info->design_capacity_warning = strtoull(attrib_value, NULL, 10);
145         return;
146     }
147 
148     if ( strstr(attrib_name, "design capacity low") )
149     {
150         info->design_capacity_low = strtoull(attrib_value, NULL, 10);
151         return;
152     }
153 
154     if ( strstr(attrib_name, "design capacity") )
155     {
156         info->design_capacity = strtoull(attrib_value, NULL, 10);
157         return;
158     }
159 
160     if ( strstr(attrib_name, "last full capacity") )
161     {
162         info->last_full_capacity = strtoull(attrib_value, NULL, 10);
163         return;
164     }
165 
166     if ( strstr(attrib_name, "design voltage") )
167     {
168         info->design_voltage = strtoull(attrib_value, NULL, 10);
169         return;
170     }
171 
172     if ( strstr(attrib_name, "capacity granularity 1") )
173     {
174         info->capacity_granularity_1 = strtoull(attrib_value, NULL, 10);
175         return;
176     }
177 
178     if ( strstr(attrib_name, "capacity granularity 2") )
179     {
180         info->capacity_granularity_2 = strtoull(attrib_value, NULL, 10);
181         return;
182     }
183 
184     if ( strstr(attrib_name, "battery technology") )
185     {
186         if ( strncmp(attrib_value, "rechargeable",
187                      strlen("rechargeable")) == 0 )
188             info->battery_technology = RECHARGEABLE;
189         else
190             info->battery_technology = NON_RECHARGEABLE;
191         return;
192     }
193 
194     if ( strstr(attrib_name, "model number") )
195     {
196         strncpy(info->model_number, attrib_value, 31);
197         info->model_number[31] = '\0';
198         return;
199     }
200 
201     if ( strstr(attrib_name, "serial number") )
202     {
203         strncpy(info->serial_number, attrib_value, 31);
204         info->serial_number[31] = '\0';
205         return;
206     }
207 
208     if ( strstr(attrib_name, "battery type") )
209     {
210         strncpy(info->battery_type, attrib_value, 31);
211         info->battery_type[31] = '\0';
212         return;
213     }
214 
215     if ( strstr(attrib_name, "OEM info") )
216     {
217         strncpy(info->oem_info, attrib_value, 31);
218         info->oem_info[31] = '\0';
219         return;
220     }
221 
222     return;
223 }
224 
set_attribute_battery_status(char * attrib_name,char * attrib_value,struct battery_status * status)225 void set_attribute_battery_status(char *attrib_name,
226                                   char *attrib_value,
227                                   struct battery_status *status)
228 {
229     if ( strstr(attrib_name, "charging state") )
230     {
231         /* Check this, below is half baked */
232         if ( strstr(attrib_value, "charged") )
233             status->state = 0;
234         else
235             status->state = 1;
236         return;
237     }
238 
239     if ( strstr(attrib_name, "present rate") )
240     {
241         status->present_rate = strtoull(attrib_value, NULL, 10);
242         return;
243     }
244 
245     if ( strstr(attrib_name, "remaining capacity") )
246     {
247         status->remaining_capacity = strtoull(attrib_value, NULL, 10);
248         return;
249     }
250 
251     if ( strstr(attrib_name, "present voltage") )
252     {
253         status->present_voltage = strtoull(attrib_value, NULL, 10);
254         return;
255     }
256 
257     if ( strstr(attrib_name, "present") )
258     {
259         if ( strstr(attrib_value, "yes") )
260             status->present = YES;
261         return;
262     }
263 }
264 
parse_battery_info_or_status(char * line_info,enum BATTERY_INFO_TYPE type,void * info_or_status)265 void parse_battery_info_or_status(char *line_info,
266                                   enum BATTERY_INFO_TYPE type,
267                                   void *info_or_status)
268 {
269     char attrib_name[128];
270     char attrib_value[64];
271     char *delimiter;
272     unsigned long length;
273 
274     length = strlen(line_info);
275     delimiter = (char *) strchr( line_info, ':');
276     if ( (!delimiter) || (delimiter == line_info) ||
277          (delimiter == line_info + length) )
278         return;
279 
280     strncpy(attrib_name, line_info, delimiter-line_info);
281     while ( *(delimiter+1) == ' ' )
282     {
283         delimiter++;
284         if ( delimiter+1 == line_info + length)
285             return;
286     }
287     strncpy(attrib_value, delimiter+1,
288             (unsigned long)line_info + length -(unsigned long)delimiter);
289 
290     if ( type == BIF )
291         set_attribute_battery_info(attrib_name, attrib_value,
292                                    (struct battery_info *)info_or_status);
293     else
294         set_attribute_battery_status(attrib_name, attrib_value,
295                                      (struct battery_status *)info_or_status);
296 
297     return;
298 }
299 
get_next_battery_info_or_status(DIR * battery_dir,enum BATTERY_INFO_TYPE type,void * info_or_status)300 int get_next_battery_info_or_status(DIR *battery_dir,
301                                     enum BATTERY_INFO_TYPE type,
302                                     void *info_or_status)
303 {
304     FILE *file;
305     char line_info[256];
306 
307     if  ( !info_or_status )
308         return 0;
309 
310     if (type == BIF)
311         memset(info_or_status, 0, sizeof(struct battery_info));
312     else
313         memset(info_or_status, 0, sizeof(struct battery_status));
314 
315     file = get_next_battery_file(battery_dir, type);
316     if ( !file )
317         return 0;
318 
319     while ( fgets(line_info, sizeof(line_info), file) != NULL )
320         parse_battery_info_or_status(line_info, type, info_or_status);
321 
322     fclose(file);
323     return 1;
324 }
325 
326 #ifdef RUN_STANDALONE
print_battery_info(struct battery_info * info)327 void print_battery_info(struct battery_info *info)
328 {
329     printf("present:                %d\n", info->present);
330     printf("design capacity:        %d\n", info->design_capacity);
331     printf("last full capacity:     %d\n", info->last_full_capacity);
332     printf("battery technology:     %d\n", info->battery_technology);
333     printf("design voltage:         %d\n", info->design_voltage);
334     printf("design capacity warning:%d\n", info->design_capacity_warning);
335     printf("design capacity low:    %d\n", info->design_capacity_low);
336     printf("capacity granularity 1: %d\n", info->capacity_granularity_1);
337     printf("capacity granularity 2: %d\n", info->capacity_granularity_2);
338     printf("model number:           %s\n", info->model_number);
339     printf("serial number:          %s\n", info->serial_number);
340     printf("battery type:           %s\n", info->battery_type);
341     printf("OEM info:               %s\n", info->oem_info);
342 }
343 #endif /*RUN_STANDALONE*/
344 
write_ulong_lsb_first(char * temp_val,unsigned long val)345 void write_ulong_lsb_first(char *temp_val, unsigned long val)
346 {
347     snprintf(temp_val, 9, "%02x%02x%02x%02x", (unsigned int)val & 0xff,
348     (unsigned int)(val & 0xff00) >> 8, (unsigned int)(val & 0xff0000) >> 16,
349     (unsigned int)(val & 0xff000000) >> 24);
350 }
351 
write_battery_info_to_xenstore(struct battery_info * info)352 void write_battery_info_to_xenstore(struct battery_info *info)
353 {
354     char val[1024], string_info[256];
355     unsigned int len;
356 
357     xs_mkdir(xs, XBT_NULL, "/pm");
358 
359     memset(val, 0, 1024);
360     memset(string_info, 0, 256);
361     /* write 9 dwords (so 9*4) + length of 4 strings + 4 null terminators */
362     len = 9 * 4 + strlen(info->model_number) + strlen(info->serial_number) +
363           strlen(info->battery_type) + strlen(info->oem_info) + 4;
364     assert(len < 255);
365     snprintf(val, 3, "%02x", len);
366     write_ulong_lsb_first(val+2, info->present);
367     write_ulong_lsb_first(val+10, info->design_capacity);
368     write_ulong_lsb_first(val+18, info->last_full_capacity);
369     write_ulong_lsb_first(val+26, info->battery_technology);
370     write_ulong_lsb_first(val+34, info->design_voltage);
371     write_ulong_lsb_first(val+42, info->design_capacity_warning);
372     write_ulong_lsb_first(val+50, info->design_capacity_low);
373     write_ulong_lsb_first(val+58, info->capacity_granularity_1);
374     write_ulong_lsb_first(val+66, info->capacity_granularity_2);
375 
376     snprintf(string_info, 256, "%02x%s%02x%s%02x%s%02x%s",
377              (unsigned int)strlen(info->model_number), info->model_number,
378              (unsigned int)strlen(info->serial_number), info->serial_number,
379              (unsigned int)strlen(info->battery_type), info->battery_type,
380              (unsigned int)strlen(info->oem_info), info->oem_info);
381     strncat(val+73, string_info, 1024-73-1);
382     xs_write(xs, XBT_NULL, "/pm/bif",
383              val, 73+8+strlen(info->model_number)+strlen(info->serial_number)+
384              strlen(info->battery_type)+strlen(info->oem_info)+1);
385 }
386 
write_one_time_battery_info(void)387 int write_one_time_battery_info(void)
388 {
389     DIR *dir;
390     int ret = 0;
391     struct battery_info info;
392 
393     dir = opendir(BATTERY_DIR_PATH);
394     if ( !dir )
395         return 0;
396 
397     while ( get_next_battery_info_or_status(dir, BIF, (void *)&info) )
398     {
399 #ifdef RUN_STANDALONE
400         print_battery_info(&info);
401 #endif
402         if ( info.present == YES )
403         {
404             write_battery_info_to_xenstore(&info);
405             ret = 1;
406             break; /* rethink this... */
407         }
408     }
409 
410     closedir(dir);
411     return ret;
412 }
413 
414 #ifdef RUN_STANDALONE
print_battery_status(struct battery_status * status)415 void print_battery_status(struct battery_status *status)
416 {
417     printf("present:                     %d\n", status->present);
418     printf("Battery state                %d\n", status->state);
419     printf("Battery present rate         %d\n", status->present_rate);
420     printf("Battery remining capacity    %d\n", status->remaining_capacity);
421     printf("Battery present voltage      %d\n", status->present_voltage);
422 }
423 #endif /*RUN_STANDALONE*/
424 
write_battery_status_to_xenstore(struct battery_status * status)425 void write_battery_status_to_xenstore(struct battery_status *status)
426 {
427     char val[35];
428 
429     xs_mkdir(xs, XBT_NULL, "/pm");
430 
431     memset(val, 0, 35);
432     snprintf(val, 3, "%02x", 16);
433     write_ulong_lsb_first(val+2, status->state);
434     write_ulong_lsb_first(val+10, status->present_rate);
435     write_ulong_lsb_first(val+18, status->remaining_capacity);
436     write_ulong_lsb_first(val+26, status->present_voltage);
437 
438     xs_write(xs, XBT_NULL, "/pm/bst", val, 35);
439 }
440 
wait_for_and_update_battery_status_request(void)441 int wait_for_and_update_battery_status_request(void)
442 {
443     DIR *dir;
444     int ret = 0;
445     unsigned int count;
446     struct battery_status status;
447 
448     while ( true )
449     {
450         /* KN:@TODO - It is rather inefficient to not cache the file handle.
451          *  Switch to caching file handle.
452          */
453         dir = opendir(BATTERY_DIR_PATH);
454         if ( !dir )
455             return 0;
456 
457         while ( get_next_battery_info_or_status(dir, BST, (void *)&status) )
458         {
459 #ifdef RUN_STANDALONE
460             print_battery_status(&status);
461 #endif
462             if ( status.present == YES )
463             {
464                 write_battery_status_to_xenstore(&status);
465                 ret = 1;
466                 /* rethink this; though I have never seen, there might be
467                  * systems out there with more than one battery device
468                  * present
469                  */
470                 break;
471             }
472         }
473         closedir(dir);
474         xs_watch(xs, "/pm/events", "refreshbatterystatus");
475         xs_read_watch(xs, &count);
476     }
477 
478     return ret;
479 }
480 
481 /* Borrowed daemonize from xenstored - Initially written by Stevens. */
daemonize(void)482 static void daemonize(void)
483 {
484     pid_t pid;
485 
486     if ( (pid = fork()) < 0 )
487         exit(1);
488 
489     if ( pid != 0 )
490         exit(0);
491 
492     setsid();
493 
494     if ( (pid = fork()) < 0 )
495         exit(1);
496 
497     if ( pid != 0 )
498         exit(0);
499 
500     if ( chdir("/") == -1 )
501         exit(1);
502 
503     umask(0);
504 }
505 
main(int argc,char * argv[])506 int main(int argc, char *argv[])
507 {
508 #ifndef RUN_STANDALONE
509     daemonize();
510 #endif
511     xs = xs_open(0);
512     if ( xs == NULL )
513         return -1;
514 
515     if ( write_one_time_battery_info() == 0 )
516     {
517         xs_close(xs);
518         return -1;
519     }
520 
521     wait_for_and_update_battery_status_request();
522     xs_close(xs);
523     return 0;
524 }
525 
526