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