1 /***************************************************************************
2 *
3 * addon-cpufreq.c : Routines to support CPUFreq interface
4 *
5 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
6 * Use is subject to license terms.
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
10 ***************************************************************************/
11
12
13 #ifdef HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16
17 #include <stdio.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <strings.h>
21 #include <stdarg.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <stdlib.h>
26 #include <fcntl.h>
27 #include <sys/dkio.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <glib.h>
31 #include <dbus/dbus-glib-lowlevel.h>
32 #include <dbus/dbus-glib.h>
33 #include <priv.h>
34 #include <pwd.h>
35
36 #include <syslog.h>
37
38 #include <libhal.h>
39 #include "../../hald/logger.h"
40 #include "../../utils/adt_data.h"
41
42 #include <pwd.h>
43 #ifdef HAVE_POLKIT
44 #include <libpolkit.h>
45 #endif
46
47 #ifdef sun
48 #include <bsm/adt.h>
49 #include <bsm/adt_event.h>
50 #include <sys/pm.h>
51 #endif
52
53 #define POWER_CONF_FILE "/etc/power.conf"
54 #define PMCONFIG "/usr/sbin/pmconfig -f"
55 #define PM "/dev/pm"
56
57 #define FILE_ARR_SIZE 256
58 #define EDIT_TYPE_SIZE 64
59 #define ERR_BUF_SIZE 256
60
61 #define WAIT_TIME 30
62
63 char TMP_CONF_FILE[64] = "/tmp/power.conf.XXXXXX";
64 const char *sender;
65 unsigned long uid;
66
67 /*
68 * Specify different CPUFreq related HAL activities that can be done
69 */
70 enum hal_type {
71 CPU_GOV,
72 CPU_PERFORMANCE
73 };
74 typedef enum hal_type power_conf_hal_type;
75
76 /*
77 * Various CPUFreq related editable parameters in the power.conf file
78 */
79 typedef struct {
80 char cpu_gov[EDIT_TYPE_SIZE];
81 int cpu_th;
82 } pconf_edit_type;
83
84 /*
85 * CPUFreq interospect XML that exports the various CPUFreq HAL interface
86 * supported methods
87 */
88 const char *cpufreq_introspect_xml = \
89 " <method name= \"SetCPUFreqGovernor\">\n \
90 <arg type= \"s\" name= \"governor\" direction= \"in\"/>\n \
91 </method>\n \
92 <method name= \"GetCPUFreqGovernor\">\n \
93 <type= \"s\" direction= \"out\"/>\n \
94 </method>\n \
95 <method name= \"SetCPUFreqPerformance\">\n \
96 <arg type=\"i\" direction=\"in\"/>\n \
97 </method>\n \
98 <method name= \"GetCPUFreqPerformance\">\n \
99 <type=\"i\" direction=\"out\"/>\n \
100 </method>\n \
101 <method name= \"GetCPUFreqAvailableGovernors\">\n \
102 <type=\"s\" direction=\"out\"/>\n \
103 </method>\n";
104
105 /*
106 * List of governors that are currently supported
107 */
108 char *const gov_list[] = {
109 "ondemand",
110 "performance",
111 NULL
112 };
113
114 static char current_gov[EDIT_TYPE_SIZE];
115
116 /*
117 * Free up the mem allocated to hold the DBusError
118 */
119 static void
check_and_free_error(DBusError * error)120 check_and_free_error(DBusError *error)
121 {
122 if (dbus_error_is_set (error)) {
123 dbus_error_free (error);
124 }
125 }
126
127 /*
128 * Edit the /etc/power.conf file to update the cpupm and cpupm_threshold values
129 * Return 0 on success
130 * 1 if the governor is not available or supported
131 * -1 all other errors
132 * NOTE: Before modifying power.conf, it is first copied into a temp file, and
133 * pmconfig is executed on the temp file with -f option, which uses temp file
134 * to set the PM config and then replaces power.conf with the temp file.
135 */
136 static int
edit_power_conf_file(pconf_edit_type pc_edit_type,power_conf_hal_type pc_hal_type,char * tmp_file)137 edit_power_conf_file(pconf_edit_type pc_edit_type,
138 power_conf_hal_type pc_hal_type, char *tmp_file)
139 {
140 FILE *pfile;
141 char tstr[FILE_ARR_SIZE];
142 char temp_str[FILE_ARR_SIZE];
143 long fset = 0;
144 long next_fset = 0;
145 char *file_edit_type;
146 char *file_edit_value;
147 char file_edit_threshold[FILE_ARR_SIZE];
148 char file_update_str[FILE_ARR_SIZE];
149 int res = 0;
150 char cp_cmd_str[128];
151 int tmp_fd;
152
153 /*
154 * Copy /etc/power.conf to temp file
155 */
156 if (tmp_file == NULL) {
157 HAL_INFO ((" Invalid temp file name"));
158 return (EINVAL);
159 }
160 sprintf (cp_cmd_str, "/usr/bin/cp %s %s", POWER_CONF_FILE, tmp_file);
161 if (system (cp_cmd_str) != 0) {
162 HAL_ERROR ((" Error in copying %s to %s, %s",
163 POWER_CONF_FILE, tmp_file, strerror (errno)));
164 return (errno);
165 }
166
167 pfile = fopen (tmp_file, "r+");
168 if (pfile == NULL) {
169 HAL_INFO (("Cannot open file %s: %s",
170 tmp_file, strerror (errno)));
171 return (errno);
172 }
173
174 switch (pc_hal_type) {
175 case CPU_GOV:
176 if ((pc_edit_type.cpu_gov == NULL) ||
177 ((strcmp (pc_edit_type.cpu_gov, "ondemand") != 0) &&
178 (strcmp (pc_edit_type.cpu_gov, "performance") != 0))) {
179 HAL_INFO ((" CPU governor is not available/valid."
180 " Should be either ondemand or performance"));
181 res = EINVAL;
182 goto out;
183 }
184 file_edit_type = "cpupm";
185 if (strcmp (pc_edit_type.cpu_gov, "ondemand") == 0) {
186 file_edit_value = " enable";
187 } else {
188 file_edit_value = "disable";
189 }
190 break;
191 case CPU_PERFORMANCE:
192 if (pc_edit_type.cpu_th == 0) {
193 HAL_INFO ((" CPU Threshold is not valid."));
194 res = EINVAL;
195 goto out;
196 }
197 file_edit_type = "cpu-threshold";
198 sprintf (file_edit_threshold, "%d", pc_edit_type.cpu_th);
199 file_edit_value = file_edit_threshold;
200 break;
201 default:
202 HAL_DEBUG ((" Cannot recognize the type of change being"
203 " made to /etc/power.conf"));
204 res = EINVAL;
205 goto out;
206 }
207
208 while (fgets (tstr, FILE_ARR_SIZE, pfile) != NULL) {
209 if ((tstr == NULL) || (strlen (tstr) <= 0))
210 continue;
211 /*
212 * Look for line containing "cpupm" or "cpu-threshold"
213 */
214
215 if (strstr (tstr, file_edit_type) == NULL) {
216 fset = fset + strlen (tstr);
217 continue;
218 }
219 /*
220 * If the required value already present. Just
221 * return
222 */
223 if (strstr (tstr, file_edit_value) != NULL) {
224 res = 0;
225 goto out;
226 }
227
228 if (fseek (pfile, fset, SEEK_SET) != 0) {
229 HAL_ERROR (("\n Error in fseek %s: %s",
230 POWER_CONF_FILE, strerror (errno)));
231 res = errno;
232 goto out;
233 }
234 /*
235 * Update the file with new values
236 */
237 sprintf (file_update_str, "%s %s \n",
238 file_edit_type, file_edit_value);
239
240 /*
241 * Check if the currrent line is the last one. If not,
242 * to avoid overwriting and wasting space, move remaining
243 * lines upwards and update at the end
244 */
245 next_fset = fset + strlen(tstr);
246 if (fseek (pfile, next_fset, SEEK_SET) != 0) {
247 HAL_ERROR (("\n Error in fseek %s: %s",
248 tmp_file, strerror (errno)));
249 res = errno;
250 goto out;
251 }
252 if (fgets (tstr, FILE_ARR_SIZE, pfile) != NULL) {
253 do {
254 snprintf (temp_str, FILE_ARR_SIZE,
255 "%s\n", tstr);
256 fseek (pfile, fset, SEEK_SET);
257 fputs (temp_str, pfile);
258 fset = fset + strlen(tstr);
259 next_fset = next_fset + strlen(tstr);
260 fseek (pfile, next_fset, SEEK_SET);
261
262 } while (fgets (tstr, FILE_ARR_SIZE, pfile) != NULL);
263 }
264
265 fseek (pfile, fset, SEEK_SET);
266
267 if (fputs (file_update_str, pfile) == EOF) {
268 HAL_ERROR (("\n Error in writing to"
269 " %s: %s", POWER_CONF_FILE,
270 strerror (errno)));
271 res = errno;
272 goto out;
273 }
274
275 if (fflush (pfile) == EOF) {
276 HAL_ERROR (("\n Error in flushing to"
277 " %s: %s", POWER_CONF_FILE,
278 strerror (errno)));
279 }
280 res = 0;
281 goto out;
282 }
283
284 /*
285 * If the pointer comes here, then the property is not already present.
286 * Have to append to the file
287 */
288 HAL_DEBUG (("\n Passed value not found. Will append to the file"));
289 if (fseek (pfile, 0, SEEK_END) != 0) {
290 HAL_ERROR (("\n Error in fseek to %s: %s",
291 POWER_CONF_FILE, strerror (errno)));
292 res = errno;
293 goto out;
294 }
295
296 /*
297 * Update the file with new values
298 */
299 sprintf (file_update_str, "%s %s \n", file_edit_type, file_edit_value);
300
301 if (fputs (file_update_str, pfile) == EOF) {
302 HAL_ERROR (("Error in writing to file %s: %s",
303 POWER_CONF_FILE, strerror (errno)));
304 res = errno;
305 goto out;
306 }
307
308 if (fflush (pfile) == EOF) {
309 HAL_ERROR (("\n Error in flushing to %s: %s",
310 POWER_CONF_FILE, strerror (errno)));
311 }
312 res = 0;
313 out:
314 fclose (pfile);
315 return (res);
316 }
317
318 /*
319 * Depending on the type(cpupm or cpu-threshold) to read, check if they are
320 * present. If present, return the corresponding value through pc_value arg
321 * and return 1 from the function. If there is no corresponding entry,return 0.
322 * Return -1 on error
323 */
324
325 static int
read_power_conf_file(pconf_edit_type * pc_value,power_conf_hal_type pc_hal_type)326 read_power_conf_file(pconf_edit_type *pc_value,
327 power_conf_hal_type pc_hal_type)
328 {
329
330 FILE *pfile;
331 char tstr[FILE_ARR_SIZE];
332 long fset = 0;
333 char *file_edit_type;
334 char *tpstr;
335 int res = 0;
336
337 pfile = fopen (POWER_CONF_FILE, "r");
338 if (pfile == NULL) {
339 HAL_INFO (("\n Cannot open the file %s: %s",
340 POWER_CONF_FILE, strerror (errno)));
341 return (-1);
342 }
343
344 switch (pc_hal_type) {
345 case CPU_GOV:
346 file_edit_type = "cpupm";
347 break;
348 case CPU_PERFORMANCE:
349 file_edit_type = "cpu-threshold";
350 break;
351 default :
352 HAL_DEBUG (("Cannot recognize the HAL type to get value"));
353 res = -1;
354 goto out;
355 }
356
357 while (fgets (tstr, FILE_ARR_SIZE, pfile) != NULL) {
358 if ((tstr == NULL) || (strlen (tstr) <= 0))
359 continue;
360 /*
361 * Look for line containing "cpupm" or "cpu-threshold"
362 */
363 if (strstr (tstr, file_edit_type) == NULL)
364 continue;
365
366 /*
367 * If the required value already present. Just
368 * get the value
369 */
370 tpstr = strtok (tstr, " ");
371 tpstr = strtok (NULL, " ");
372 if (tpstr == NULL) {
373 HAL_INFO (("Value of %s in %s is not valid",
374 file_edit_type, POWER_CONF_FILE));
375 res = -1;
376 goto out;
377 }
378
379 if (pc_hal_type == CPU_GOV) {
380 /*
381 * Copy the corresponding governor
382 */
383 if (strcmp (tpstr, "enable") == 0) {
384 sprintf (pc_value->cpu_gov,
385 "%s", "ondemand");
386 } else {
387 sprintf (pc_value->cpu_gov,
388 "%s", "performance");
389 }
390 } else {
391 pc_value->cpu_th = atoi (tpstr);
392 }
393 res = 1;
394 goto out;
395 }
396 /*
397 * Entry not found in the file
398 */
399 HAL_DEBUG ((" No entry of %s in %s", file_edit_type, POWER_CONF_FILE));
400 res = 0;
401
402 out:
403 fclose (pfile);
404 return (res);
405 }
406
407
408 /*
409 * Depending on the type(Governor or Perfromance) to read, get the current
410 * values through PM ioctls().
411 * For "Governor", return the cpupm state and for "Performance" return the
412 * current cpu threshold.
413 * Return the corresponding value through cur_value and return 1 from the
414 * function for success. Return -1 on error
415 */
416
417 static int
get_cur_val(pconf_edit_type * cur_value,power_conf_hal_type pc_hal_type)418 get_cur_val(pconf_edit_type *cur_value,
419 power_conf_hal_type pc_hal_type)
420 {
421
422 int pm_fd;
423 int res = -1;
424 int pm_ret;
425
426 pm_fd = open (PM, O_RDONLY);
427 if (pm_fd == -1) {
428 HAL_ERROR (("Error opening %s: %s \n", PM, strerror (errno)));
429 return (res);
430 }
431
432 switch (pc_hal_type) {
433 case CPU_GOV:
434 /*
435 * First check the PM_GET_CPUPM_STATE. If it is not available
436 * then check PM_GET_PM_STATE
437 */
438 pm_ret = ioctl (pm_fd, PM_GET_CPUPM_STATE);
439 if (pm_ret < 0) {
440 HAL_ERROR (("Error in ioctl PM_GET_CPUPM_STATE: %s \n",
441 strerror (errno)));
442 goto out;
443 }
444 switch (pm_ret) {
445 case PM_CPU_PM_ENABLED:
446 sprintf (cur_value->cpu_gov, "%s", "ondemand");
447 res = 1;
448 goto out;
449 case PM_CPU_PM_DISABLED:
450 sprintf (cur_value->cpu_gov, "%s", "performance");
451 res = 1;
452 goto out;
453 case PM_CPU_PM_NOTSET:
454 /*
455 * Check for PM_GET_PM_STATE
456 */
457 pm_ret = ioctl (pm_fd, PM_GET_PM_STATE);
458 if (pm_ret < 0) {
459 HAL_ERROR (("Error in ioctl PM_GET_PM_STATE: "
460 "%s", strerror (errno)));
461 goto out;
462 }
463 switch (pm_ret) {
464 case PM_SYSTEM_PM_ENABLED:
465 sprintf (cur_value->cpu_gov, "%s", "ondemand");
466 res = 1;
467 goto out;
468 case PM_SYSTEM_PM_DISABLED:
469 sprintf (cur_value->cpu_gov, "%s",
470 "performance");
471 res = 1;
472 goto out;
473 default:
474 HAL_ERROR (("PM Internal error during ioctl "
475 "PM_GET_PM_STATE"));
476 goto out;
477 }
478 default:
479 HAL_ERROR (("Unknown value ioctl PM_GET_CPUPM_STATE"));
480 goto out;
481 }
482 case CPU_PERFORMANCE:
483 /*
484 * First check the PM_GET_CPU_THRESHOLD. If it is not available
485 * then check PM_GET_SYSTEM_THRESHOLD
486 */
487 pm_ret = ioctl (pm_fd, PM_GET_CPU_THRESHOLD);
488 if (pm_ret >= 0) {
489 cur_value->cpu_th = pm_ret;
490 res = 1;
491 goto out;
492 } else if ((pm_ret == EINVAL) || (pm_ret == ENOTTY)) {
493 /*
494 * PM_GET_CPU_THRESHOLD is not available
495 */
496 pm_ret = ioctl (pm_fd, PM_GET_SYSTEM_THRESHOLD);
497 if (res >= 0) {
498 cur_value->cpu_th = pm_ret;
499 res = 1;
500 goto out;
501 } else {
502 HAL_ERROR (("Error in PM_GET_CPU_THRESHOLD: %s",
503 strerror (errno)));
504 goto out;
505 }
506 } else {
507 HAL_ERROR ((" Error in ioctl PM_GET_CPU_THRESHOLD: %s",
508 strerror (errno)));
509 goto out;
510 }
511 default :
512 HAL_DEBUG (("Cannot recognize the HAL type to get value"));
513 goto out;
514 }
515 out:
516 close (pm_fd);
517 return (res);
518 }
519 /*
520 * Send an error message as a response to the pending call
521 */
522 static void
generate_err_msg(DBusConnection * con,DBusMessage * msg,const char * err_name,char * fmt,...)523 generate_err_msg(DBusConnection *con,
524 DBusMessage *msg,
525 const char *err_name,
526 char *fmt, ...)
527 {
528
529 DBusMessage *err_msg;
530 char err_buf[ERR_BUF_SIZE];
531 va_list va_args;
532
533 va_start (va_args, fmt);
534 vsnprintf (err_buf, ERR_BUF_SIZE, fmt, va_args);
535 va_end (va_args);
536
537 HAL_DEBUG ((" Sending error message: %s", err_buf));
538
539 err_msg = dbus_message_new_error (msg, err_name, err_buf);
540 if (err_msg == NULL) {
541 HAL_ERROR (("No Memory for DBUS error msg"));
542 return;
543 }
544
545 if (!dbus_connection_send (con, err_msg, NULL)) {
546 HAL_ERROR ((" Out Of Memory!"));
547 }
548 dbus_connection_flush (con);
549
550 }
551
552 static void
gen_unknown_gov_err(DBusConnection * con,DBusMessage * msg,char * err_str)553 gen_unknown_gov_err(DBusConnection *con,
554 DBusMessage *msg,
555 char *err_str)
556 {
557
558 generate_err_msg (con,
559 msg,
560 "org.freedesktop.Hal.CPUFreq.UnknownGovernor",
561 "Unknown CPUFreq Governor: %s",
562 err_str);
563 }
564
565 static void
gen_no_suitable_gov_err(DBusConnection * con,DBusMessage * msg,char * err_str)566 gen_no_suitable_gov_err(DBusConnection *con,
567 DBusMessage *msg,
568 char *err_str)
569 {
570
571 generate_err_msg (con,
572 msg,
573 "org.freedesktop.Hal.CPUFreq.NoSuitableGovernor",
574 "Could not find a suitable governor: %s",
575 err_str);
576 }
577
578 static void
gen_cpufreq_err(DBusConnection * con,DBusMessage * msg,char * err_str)579 gen_cpufreq_err(DBusConnection *con,
580 DBusMessage *msg,
581 char *err_str)
582 {
583 generate_err_msg (con,
584 msg,
585 "org.freedesktop.Hal.CPUFreq.Error",
586 "%s: Syslog might give more information",
587 err_str);
588 }
589
590
591 /*
592 * Puts the required cpufreq audit data and calls adt_put_event()
593 * to generate auditing
594 */
595 static void
audit_cpufreq(const adt_export_data_t * imported_state,au_event_t event_id,int result,const char * auth_used,const int cpu_thr_value)596 audit_cpufreq(const adt_export_data_t *imported_state, au_event_t event_id,
597 int result, const char *auth_used, const int cpu_thr_value)
598 {
599 adt_session_data_t *ah;
600 adt_event_data_t *event;
601 struct passwd *msg_pwd;
602 uid_t gid;
603
604 if (adt_start_session (&ah, imported_state, 0) != 0) {
605 HAL_INFO (("adt_start_session failed: %s", strerror (errno)));
606 return;
607 }
608
609 if ((event = adt_alloc_event (ah, event_id)) == NULL) {
610 HAL_INFO(("adt_alloc_event audit_cpufreq failed: %s",
611 strerror (errno)));
612 return;
613 }
614
615 switch (event_id) {
616 case ADT_cpu_ondemand:
617 event->adt_cpu_ondemand.auth_used = (char *)auth_used;
618 break;
619 case ADT_cpu_performance:
620 event->adt_cpu_performance.auth_used = (char *)auth_used;
621 break;
622 case ADT_cpu_threshold:
623 event->adt_cpu_threshold.auth_used = (char *)auth_used;
624 event->adt_cpu_threshold.threshold = cpu_thr_value;
625 break;
626 default:
627 goto clean;
628 }
629
630 if (result == 0) {
631 if (adt_put_event (event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
632 HAL_INFO (("adt_put_event(%d, ADT_SUCCESS) failed",
633 event_id));
634 }
635 } else {
636 if (adt_put_event (event, ADT_FAILURE, result) != 0) {
637 HAL_INFO (("adt_put_event(%d, ADT_FAILURE) failed",
638 event_id));
639 }
640 }
641
642 clean:
643 adt_free_event (event);
644 (void) adt_end_session (ah);
645 }
646
647 /*
648 * Check if the cpufreq related operations are authorized
649 */
650
651 static int
check_authorization(DBusConnection * con,DBusMessage * msg)652 check_authorization(DBusConnection *con, DBusMessage *msg)
653 {
654 int adt_res = 0;
655 #ifdef HAVE_POLKIT
656 char user_id[128];
657 char *udi;
658 char *privilege;
659 DBusError error;
660 gboolean is_priv_allowed;
661 gboolean is_priv_temporary;
662 DBusConnection *system_bus = NULL;
663 LibPolKitContext *pol_ctx = NULL;
664
665 /*
666 * Check for authorization before proceeding
667 */
668 udi = getenv ("HAL_PROP_INFO_UDI");
669 privilege = "hal-power-cpu";
670
671 dbus_error_init (&error);
672 system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
673 if (system_bus == NULL) {
674 HAL_INFO (("Cannot connect to the system bus"));
675 LIBHAL_FREE_DBUS_ERROR (&error);
676 gen_cpufreq_err (con, msg, "Cannot connect to the system bus");
677 adt_res = EINVAL;
678 goto out;
679 }
680
681 sender = dbus_message_get_sender (msg);
682 HAL_INFO (("Auth Sender: %s", sender));
683
684 if (sender == NULL) {
685 HAL_INFO (("Could not get the sender of the message"));
686 gen_cpufreq_err (con, msg,
687 "Could not get the sender of the message");
688 adt_res = ADT_FAIL_VALUE_AUTH;
689 goto out;
690 }
691
692 dbus_error_init (&error);
693 uid = dbus_bus_get_unix_user (system_bus, sender, &error);
694 if (dbus_error_is_set (&error)) {
695 HAL_INFO (("Could not get the user id of the message"));
696 LIBHAL_FREE_DBUS_ERROR (&error);
697 gen_cpufreq_err (con, msg,
698 "Could not get the user id of the message sender");
699 adt_res = ADT_FAIL_VALUE_AUTH;
700 goto out;
701 }
702
703 snprintf (user_id, sizeof (user_id), "%d", uid);
704 HAL_DEBUG ((" User id is : %d", uid));
705
706 pol_ctx = libpolkit_new_context (system_bus);
707 if (pol_ctx == NULL) {
708 HAL_INFO (("Cannot get libpolkit context"));
709 gen_cpufreq_err (con, msg,
710 "Cannot get libpolkit context to check privileges");
711 adt_res = ADT_FAIL_VALUE_AUTH;
712 goto out;
713 }
714
715 if (libpolkit_is_uid_allowed_for_privilege (pol_ctx,
716 NULL,
717 user_id,
718 privilege,
719 udi,
720 &is_priv_allowed,
721 &is_priv_temporary,
722 NULL) != LIBPOLKIT_RESULT_OK) {
723 HAL_INFO (("Cannot lookup privilege from PolicyKit"));
724 gen_cpufreq_err (con, msg,
725 "Error looking up privileges from Policykit");
726 adt_res = ADT_FAIL_VALUE_AUTH;
727 goto out;
728 }
729
730 if (!is_priv_allowed) {
731 HAL_INFO (("Caller doesn't possess required privilege to"
732 " change the governor"));
733 gen_cpufreq_err (con, msg,
734 "Caller doesn't possess required "
735 "privilege to change the governor");
736 adt_res = ADT_FAIL_VALUE_AUTH;
737 goto out;
738 }
739
740 HAL_DEBUG ((" Privilege Succeed"));
741
742 #endif
743 out:
744 return (adt_res);
745 }
746
747 /*
748 * Sets the CPU Freq governor. It sets the gov name in the /etc/power.conf
749 * and executes pmconfig. If governor is "ondemand" then "cpupm" is enabled in
750 * and if governor is performance, then "cpupm" is disabled
751 */
752 static void
set_cpufreq_gov(DBusConnection * con,DBusMessage * msg,void * udata)753 set_cpufreq_gov(DBusConnection *con, DBusMessage *msg, void *udata)
754 {
755 DBusMessageIter arg_iter;
756 DBusMessage *msg_reply;
757 char *arg_val;
758 int arg_type;
759 int pid;
760 int done_flag = 0;
761 int sleep_time = 0;
762 int status;
763 int adt_res = 0;
764 char tmp_conf_file[64] = "/tmp/power.conf.XXXXXX";
765 int tmp_fd;
766 char pmconfig_cmd[128];
767 pconf_edit_type pc_edit_type;
768 #ifdef sun
769 adt_export_data_t *adt_data;
770 size_t adt_data_size;
771 DBusConnection *system_bus = NULL;
772 DBusError error;
773 #endif
774
775 if (! dbus_message_iter_init (msg, &arg_iter)) {
776 HAL_DEBUG (("Incoming message has no arguments"));
777 gen_unknown_gov_err (con, msg, "No governor specified");
778 adt_res = EINVAL;
779 goto out;
780 }
781 arg_type = dbus_message_iter_get_arg_type (&arg_iter);
782
783 if (arg_type != DBUS_TYPE_STRING) {
784 HAL_DEBUG (("Incomming message arg type is not string"));
785 gen_unknown_gov_err (con, msg,
786 "Specified governor is not a string");
787 adt_res = EINVAL;
788 goto out;
789 }
790 dbus_message_iter_get_basic (&arg_iter, &arg_val);
791 if (arg_val != NULL) {
792 HAL_DEBUG (("SetCPUFreqGov is: %s", arg_val));
793 } else {
794 HAL_DEBUG (("Could not get SetCPUFreqGov from message iter"));
795 adt_res = EINVAL;
796 goto out;
797 }
798
799 adt_res = check_authorization (con, msg);
800
801 if (adt_res != 0) {
802 goto out;
803 }
804
805 /*
806 * Update the /etc/power.conf file.
807 */
808 tmp_fd = mkstemp (tmp_conf_file);
809 if (tmp_fd == -1) {
810 HAL_ERROR ((" Error in creating a temp conf file"));
811 adt_res = EINVAL;
812 goto out;
813 }
814 strcpy (pc_edit_type.cpu_gov, arg_val);
815 adt_res = edit_power_conf_file (pc_edit_type, CPU_GOV, tmp_conf_file);
816 if (adt_res != 0) {
817 HAL_DEBUG (("Error in edit /etc/power.conf"));
818 gen_cpufreq_err (con, msg,
819 "Internal Error while setting the governor");
820 unlink (tmp_conf_file);
821 goto out;
822 }
823
824 /*
825 * Execute pmconfig
826 */
827 sprintf (pmconfig_cmd, "%s %s", PMCONFIG, tmp_conf_file);
828 if (system (pmconfig_cmd) != 0) {
829 HAL_ERROR ((" Error in executing pmconfig: %s",
830 strerror (errno)));
831 adt_res = errno;
832 gen_cpufreq_err (con, msg, "Error in executing pmconfig");
833 unlink (tmp_conf_file);
834 goto out;
835 }
836 unlink (tmp_conf_file);
837 HAL_DEBUG (("Executed pmconfig"));
838 sprintf (current_gov, "%s", arg_val);
839
840 /*
841 * Just return an empty response, so that if the client
842 * is waiting for any response will not keep waiting
843 */
844 msg_reply = dbus_message_new_method_return (msg);
845 if (msg_reply == NULL) {
846 HAL_ERROR (("Out of memory to msg reply"));
847 gen_cpufreq_err (con, msg,
848 "Out of memory to create a response");
849 adt_res = ENOMEM;
850 goto out;
851 }
852
853 if (!dbus_connection_send (con, msg_reply, NULL)) {
854 HAL_ERROR (("Out of memory to msg reply"));
855 gen_cpufreq_err (con, msg,
856 "Out of memory to create a response");
857 adt_res = ENOMEM;
858 goto out;
859 }
860
861 dbus_connection_flush (con);
862
863 out:
864
865 #ifdef sun
866 /*
867 * Audit the new governor change
868 */
869 dbus_error_init (&error);
870 system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
871 if (system_bus == NULL) {
872 HAL_INFO (("Cannot connect to the system bus %s",
873 error.message));
874 LIBHAL_FREE_DBUS_ERROR (&error);
875 return;
876 }
877
878 adt_data = get_audit_export_data (system_bus, sender, &adt_data_size);
879 if (adt_data != NULL) {
880 if (strcmp (arg_val, "ondemand") == 0) {
881 audit_cpufreq (adt_data, ADT_cpu_ondemand, adt_res,
882 "solaris.system.power.cpu", 0);
883 } else if (strcmp (arg_val, "performance") == 0) {
884 audit_cpufreq (adt_data, ADT_cpu_performance, adt_res,
885 "solaris.system.power.cpu", 0);
886 }
887 free (adt_data);
888 } else {
889 HAL_INFO ((" Could not get audit export data"));
890 }
891 #endif /* sun */
892 }
893
894 /*
895 * Sets the CPU Freq performance. It sets the cpu-threshold in the
896 * /etc/power.conf and executes pmconfig. The performnace value should
897 * be between 1 to 100. The cpu-threshold = ((performance val) * 15) secs.
898 */
899 static void
set_cpufreq_performance(DBusConnection * con,DBusMessage * msg,void * udata)900 set_cpufreq_performance(DBusConnection *con, DBusMessage *msg, void *udata)
901 {
902
903 DBusMessageIter arg_iter;
904 DBusMessage *msg_reply;
905 int arg_val;
906 int arg_type;
907 int pid;
908 int done_flag = 0;
909 int sleep_time = 0;
910 int adt_res = 0;
911 char tmp_conf_file[64] = "/tmp/power.conf.XXXXXX";
912 int tmp_fd;
913 char pmconfig_cmd[128];
914 pconf_edit_type pc_edit_type;
915 #ifdef sun
916 adt_export_data_t *adt_data;
917 size_t adt_data_size;
918 DBusConnection *system_bus = NULL;
919 DBusError error;
920 #endif
921
922 adt_res = check_authorization (con, msg);
923
924 if (adt_res != 0) {
925 goto out;
926 }
927
928 /*
929 * Performance can only be set to dynamic governors. Currently the
930 * only supported dynamic governor is ondemand.
931 */
932 if (current_gov[0] == 0) {
933 /*
934 * Read the current governor from /etc/power.conf
935 */
936 if (read_power_conf_file (&pc_edit_type, CPU_GOV) != 1) {
937 HAL_ERROR ((" Error in reading from /etc/power.conf"));
938 gen_cpufreq_err (con, msg, "Internal error while "
939 "getting the governor");
940 adt_res = EINVAL;
941 goto out;
942 }
943 sprintf (current_gov, "%s", pc_edit_type.cpu_gov);
944 }
945
946 if (strcmp (current_gov, "ondemand") != 0) {
947 HAL_DEBUG (("To set performance the current gov should be "
948 "dynamic like ondemand"));
949 gen_no_suitable_gov_err (con, msg, "Cannot set performance "
950 "to the current governor");
951 adt_res = EINVAL;
952 goto out;
953 }
954
955 if (! dbus_message_iter_init (msg, &arg_iter)) {
956 HAL_DEBUG (("Incoming message has no arguments"));
957 gen_no_suitable_gov_err(con, msg, "No performance specified");
958 adt_res = EINVAL;
959 goto out;
960 }
961 arg_type = dbus_message_iter_get_arg_type (&arg_iter);
962
963 if (arg_type != DBUS_TYPE_INT32) {
964 HAL_DEBUG (("Incomming message arg type is not Integer"));
965 gen_no_suitable_gov_err (con, msg,
966 "Specified performance is not a Integer");
967 adt_res = EINVAL;
968 goto out;
969 }
970 dbus_message_iter_get_basic (&arg_iter, &arg_val);
971 if ((arg_val < 1) || (arg_val > 100)) {
972 HAL_INFO (("SetCPUFreqPerformance should be between 1 to 100"
973 ": %d", arg_val));
974 gen_no_suitable_gov_err (con, msg,
975 "Performance value should be between 1 and 100");
976 adt_res = EINVAL;
977 goto out;
978 }
979
980 HAL_DEBUG (("SetCPUFreqPerformance is: %d", arg_val));
981
982 /*
983 * Update the /etc/power.conf file
984 */
985 tmp_fd = mkstemp (tmp_conf_file);
986 if (tmp_fd == -1) {
987 HAL_ERROR ((" Error in creating a temp conf file"));
988 adt_res = EINVAL;
989 goto out;
990 }
991 pc_edit_type.cpu_th = arg_val * 15;
992 adt_res = edit_power_conf_file (pc_edit_type, CPU_PERFORMANCE,
993 tmp_conf_file);
994 if (adt_res != 0) {
995 HAL_DEBUG (("Error while editing /etc/power.conf"));
996 gen_cpufreq_err (con, msg,
997 "Internal error while setting the performance");
998 unlink (tmp_conf_file);
999 goto out;
1000 }
1001
1002 /*
1003 * Execute pmconfig
1004 */
1005 sprintf (pmconfig_cmd, "%s %s", PMCONFIG, tmp_conf_file);
1006 if (system (pmconfig_cmd) != 0) {
1007 HAL_ERROR ((" Error in executing pmconfig: %s",
1008 strerror (errno)));
1009 adt_res = errno;
1010 gen_cpufreq_err (con, msg,
1011 "Internal error while setting the performance");
1012 unlink (tmp_conf_file);
1013 goto out;
1014 }
1015 unlink (tmp_conf_file);
1016 HAL_DEBUG (("Executed pmconfig"));
1017
1018 /*
1019 * Just return an empty response, so that if the client
1020 * is waiting for any response will not keep waiting
1021 */
1022
1023 msg_reply = dbus_message_new_method_return (msg);
1024 if (msg_reply == NULL) {
1025 HAL_ERROR (("Out of memory to msg reply"));
1026 gen_cpufreq_err (con, msg,
1027 "Out of memory to create a response");
1028 adt_res = ENOMEM;
1029 goto out;
1030 }
1031
1032 if (!dbus_connection_send (con, msg_reply, NULL)) {
1033 HAL_ERROR (("Out of memory to msg reply"));
1034 gen_cpufreq_err (con, msg,
1035 "Out of memory to create a response");
1036 adt_res = ENOMEM;
1037 goto out;
1038 }
1039
1040 dbus_connection_flush (con);
1041 out:
1042 #ifdef sun
1043
1044 /*
1045 * Audit the new performance change
1046 */
1047 dbus_error_init (&error);
1048 system_bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
1049 if (system_bus == NULL) {
1050 HAL_INFO (("Cannot connect to the system bus %s",
1051 error.message));
1052 LIBHAL_FREE_DBUS_ERROR (&error);
1053 return;
1054 }
1055
1056 adt_data = get_audit_export_data (system_bus, sender, &adt_data_size);
1057 if (adt_data != NULL) {
1058 audit_cpufreq (adt_data, ADT_cpu_threshold, adt_res,
1059 "solaris.system.power.cpu", arg_val);
1060 free (adt_data);
1061 } else {
1062 HAL_INFO ((" Could not get audit export data"));
1063 }
1064
1065 #endif /* sun */
1066 }
1067
1068 /*
1069 * Returns in the dbus message the current gov.
1070 */
1071 static void
get_cpufreq_gov(DBusConnection * con,DBusMessage * msg,void * udata)1072 get_cpufreq_gov(DBusConnection *con, DBusMessage *msg, void *udata)
1073 {
1074
1075 DBusMessageIter rep_iter;
1076 DBusMessage *msg_reply;
1077 int res;
1078 pconf_edit_type pc_type;
1079 char *param;
1080
1081 /*
1082 * Get the governor type from /etc/power.conf if it is present.
1083 */
1084 res = get_cur_val (&pc_type, CPU_GOV);
1085 if (res != 1) {
1086 HAL_INFO ((" Error in getting the current governor"));
1087 gen_cpufreq_err (con, msg, "Internal error while getting"
1088 " the governor");
1089 return;
1090 }
1091
1092 HAL_DEBUG ((" Current governor is: %s", pc_type.cpu_gov));
1093
1094 msg_reply = dbus_message_new_method_return (msg);
1095 if (msg_reply == NULL) {
1096 HAL_ERROR (("Out of memory to msg reply"));
1097 gen_cpufreq_err (con, msg,
1098 "Internal error while getting the governor");
1099 return;
1100 }
1101
1102 /*
1103 * Append reply arguments
1104 */
1105 param = (char *) malloc (sizeof (char) * 250);
1106 if (param == NULL) {
1107 HAL_ERROR (("\n Could not allocate mem to param"));
1108 gen_cpufreq_err (con, msg, "Internal error while getting"
1109 " the governor");
1110 return;
1111 }
1112 sprintf (param, "%s", pc_type.cpu_gov);
1113
1114 dbus_message_iter_init_append (msg_reply, &rep_iter);
1115 if (!dbus_message_iter_append_basic (&rep_iter, DBUS_TYPE_STRING,
1116 ¶m)) {
1117 HAL_ERROR (("\n Out Of Memory!\n"));
1118 gen_cpufreq_err (con, msg, "Internal error while getting"
1119 " the governor");
1120 free (param);
1121 return;
1122 }
1123
1124 if (!dbus_connection_send (con, msg_reply, NULL)) {
1125 HAL_ERROR (("\n Out Of Memory!\n"));
1126 gen_cpufreq_err (con, msg, "Internal error while getting"
1127 " the governor");
1128 free (param);
1129 return;
1130 }
1131 dbus_connection_flush (con);
1132 free (param);
1133 }
1134
1135 /*
1136 * Returns in the dbus message the current performance value
1137 */
1138 static void
get_cpufreq_performance(DBusConnection * con,DBusMessage * msg,void * udata)1139 get_cpufreq_performance(DBusConnection *con, DBusMessage *msg, void *udata)
1140 {
1141
1142 DBusMessageIter rep_iter;
1143 DBusMessage *msg_reply;
1144 int res;
1145 pconf_edit_type pc_type;
1146 int param_int;
1147
1148 /*
1149 * Get the performance value
1150 */
1151 res = get_cur_val (&pc_type, CPU_PERFORMANCE);
1152 if (res != 1) {
1153 HAL_INFO ((" Error in getting current performance"));
1154 gen_cpufreq_err (con, msg, "Internal error while getting"
1155 " the performance value");
1156 return;
1157 }
1158
1159 HAL_DEBUG ((" The current performance: %d", pc_type.cpu_th));
1160
1161 msg_reply = dbus_message_new_method_return (msg);
1162 if (msg_reply == NULL) {
1163 HAL_ERROR (("Out of memory to msg reply"));
1164 gen_cpufreq_err (con, msg, "Internal error while getting"
1165 " the performance value");
1166 return;
1167 }
1168
1169 /*
1170 * Append reply arguments.pc_type.cpu_th gives the current cputhreshold
1171 * vlaue in seconds. Have to convert it into CPU HAL interface
1172 * performance value
1173 */
1174 if (pc_type.cpu_th < 15)
1175 param_int = 1;
1176 else
1177 param_int = (pc_type.cpu_th / 15);
1178
1179 HAL_DEBUG (("Performance: %d \n", param_int));
1180
1181 dbus_message_iter_init_append (msg_reply, &rep_iter);
1182 if (!dbus_message_iter_append_basic (&rep_iter, DBUS_TYPE_INT32,
1183 ¶m_int)) {
1184 HAL_ERROR (("\n Out Of Memory!\n"));
1185 gen_cpufreq_err (con, msg, "Internal error while getting"
1186 " the performance value");
1187 return;
1188 }
1189
1190 if (!dbus_connection_send (con, msg_reply, NULL)) {
1191 HAL_ERROR (("\n Out Of Memory!\n"));
1192 gen_cpufreq_err (con, msg, "Internal error while getting"
1193 " the performance value");
1194 return;
1195 }
1196 dbus_connection_flush (con);
1197 }
1198
1199 /*
1200 * Returns list of available governors. Currently just two governors are
1201 * supported. They are "ondemand" and "performance"
1202 */
1203
1204 static void
get_cpufreq_avail_gov(DBusConnection * con,DBusMessage * msg,void * udata)1205 get_cpufreq_avail_gov(DBusConnection *con, DBusMessage *msg, void *udata)
1206 {
1207
1208 DBusMessageIter rep_iter;
1209 DBusMessageIter array_iter;
1210 DBusMessage *msg_reply;
1211 int ngov;
1212
1213 msg_reply = dbus_message_new_method_return (msg);
1214 if (msg_reply == NULL) {
1215 HAL_ERROR (("Out of memory to msg reply"));
1216 gen_cpufreq_err (con, msg, "Internal error while getting"
1217 " the list of governors");
1218 return;
1219 }
1220
1221 /*
1222 * Append reply arguments
1223 */
1224 dbus_message_iter_init_append (msg_reply, &rep_iter);
1225
1226 if (!dbus_message_iter_open_container (&rep_iter,
1227 DBUS_TYPE_ARRAY,
1228 DBUS_TYPE_STRING_AS_STRING,
1229 &array_iter)) {
1230 HAL_ERROR (("\n Out of memory to msg reply array"));
1231 gen_cpufreq_err (con, msg, "Internal error while getting"
1232 " the list of governors");
1233 return;
1234 }
1235
1236 for (ngov = 0; gov_list[ngov] != NULL; ngov++) {
1237 HAL_DEBUG (("\n%d Gov Name: %s", ngov, gov_list[ngov]));
1238 dbus_message_iter_append_basic (&array_iter, DBUS_TYPE_STRING,
1239 &gov_list[ngov]);
1240 }
1241 dbus_message_iter_close_container (&rep_iter, &array_iter);
1242
1243 if (!dbus_connection_send (con, msg_reply, NULL)) {
1244 HAL_ERROR (("\n Out Of Memory!\n"));
1245 gen_cpufreq_err (con, msg, "Internal error while getting"
1246 " the list of governors");
1247 return;
1248 }
1249 dbus_connection_flush (con);
1250 }
1251
1252 static DBusHandlerResult
hald_dbus_cpufreq_filter(DBusConnection * con,DBusMessage * msg,void * udata)1253 hald_dbus_cpufreq_filter(DBusConnection *con, DBusMessage *msg, void *udata)
1254 {
1255 HAL_DEBUG ((" Inside CPUFreq filter:%s", dbus_message_get_path(msg)));
1256 /*
1257 * Check for method types
1258 */
1259 if (!dbus_connection_get_is_connected (con))
1260 HAL_DEBUG (("Connection disconnected in cpufreq addon"));
1261
1262 if (dbus_message_is_method_call (msg,
1263 "org.freedesktop.Hal.Device.CPUFreq",
1264 "SetCPUFreqGovernor")) {
1265 HAL_DEBUG (("---- SetCPUFreqGovernor is called "));
1266
1267 set_cpufreq_gov (con, msg, udata);
1268
1269 } else if (dbus_message_is_method_call (msg,
1270 "org.freedesktop.Hal.Device.CPUFreq",
1271 "GetCPUFreqGovernor")) {
1272 HAL_DEBUG (("---- GetCPUFreqGovernor is called "));
1273
1274 get_cpufreq_gov (con, msg, udata);
1275 } else if (dbus_message_is_method_call (msg,
1276 "org.freedesktop.Hal.Device.CPUFreq",
1277 "GetCPUFreqAvailableGovernors")) {
1278 HAL_DEBUG (("---- GetCPUFreqAvailableGovernors is called "));
1279
1280 get_cpufreq_avail_gov (con, msg, udata);
1281 } else if (dbus_message_is_method_call (msg,
1282 "org.freedesktop.Hal.Device.CPUFreq",
1283 "SetCPUFreqPerformance")) {
1284 HAL_DEBUG (("---- SetCPUFreqPerformance is called "));
1285
1286 set_cpufreq_performance (con, msg, udata);
1287 } else if (dbus_message_is_method_call (msg,
1288 "org.freedesktop.Hal.Device.CPUFreq",
1289 "GetCPUFreqPerformance")) {
1290 HAL_DEBUG (("---- GetCPUFreqPerformance is called "));
1291
1292 get_cpufreq_performance (con, msg, udata);
1293 } else {
1294 HAL_DEBUG (("---Not Set/Get cpufreq gov---"));
1295 }
1296
1297 return (DBUS_HANDLER_RESULT_HANDLED);
1298
1299 }
1300
1301 static void
drop_privileges()1302 drop_privileges()
1303 {
1304 priv_set_t *pPrivSet = NULL;
1305 priv_set_t *lPrivSet = NULL;
1306
1307 /*
1308 * Start with the 'basic' privilege set and then add any
1309 * of the privileges that will be required.
1310 */
1311 if ((pPrivSet = priv_str_to_set ("basic", ",", NULL)) == NULL) {
1312 HAL_INFO (("Error in setting the priv"));
1313 return;
1314 }
1315
1316 (void) priv_addset (pPrivSet, PRIV_SYS_DEVICES);
1317
1318 if (setppriv (PRIV_SET, PRIV_INHERITABLE, pPrivSet) != 0) {
1319 HAL_INFO (("Could not set the privileges"));
1320 priv_freeset (pPrivSet);
1321 return;
1322 }
1323
1324 (void) priv_addset (pPrivSet, PRIV_PROC_AUDIT);
1325 (void) priv_addset (pPrivSet, PRIV_SYS_CONFIG);
1326
1327 if (setppriv (PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
1328 HAL_INFO (("Could not set the privileges"));
1329 priv_freeset (pPrivSet);
1330 return;
1331 }
1332
1333 priv_freeset (pPrivSet);
1334
1335 }
1336
1337 int
main(int argc,char ** argv)1338 main(int argc, char **argv)
1339 {
1340
1341 LibHalContext *ctx = NULL;
1342 char *udi;
1343 DBusError error;
1344 DBusConnection *conn;
1345
1346 GMainLoop *loop = g_main_loop_new (NULL, FALSE);
1347
1348 drop_privileges ();
1349 openlog ("hald-addon-cpufreq", LOG_PID, LOG_DAEMON);
1350 setup_logger ();
1351
1352 bzero (current_gov, EDIT_TYPE_SIZE-1);
1353
1354 if ((udi = getenv ("UDI")) == NULL) {
1355 HAL_INFO (("\n Could not get the UDI in addon-cpufreq"));
1356 return (0);
1357 }
1358
1359 dbus_error_init (&error);
1360 if ((ctx = libhal_ctx_init_direct (&error)) == NULL) {
1361 HAL_ERROR (("main(): init_direct failed\n"));
1362 return (0);
1363 }
1364 dbus_error_init (&error);
1365 if (!libhal_device_addon_is_ready (ctx, getenv ("UDI"), &error)) {
1366 check_and_free_error (&error);
1367 return (0);
1368 }
1369
1370 /*
1371 * Claim the cpufreq interface
1372 */
1373
1374 HAL_DEBUG (("cpufreq Introspect XML: %s", cpufreq_introspect_xml));
1375
1376 if (!libhal_device_claim_interface (ctx,
1377 udi,
1378 "org.freedesktop.Hal.Device.CPUFreq",
1379 cpufreq_introspect_xml,
1380 &error)) {
1381 HAL_DEBUG ((" Cannot claim the CPUFreq interface"));
1382 check_and_free_error (&error);
1383 return (0);
1384 }
1385
1386 conn = libhal_ctx_get_dbus_connection (ctx);
1387
1388 /*
1389 * Add the cpufreq capability
1390 */
1391 if (!libhal_device_add_capability (ctx,
1392 udi,
1393 "cpufreq_control",
1394 &error)) {
1395 HAL_DEBUG ((" Could not add cpufreq_control capability"));
1396 check_and_free_error (&error);
1397 return (0);
1398 }
1399 /*
1400 * Watches and times incoming messages
1401 */
1402
1403 dbus_connection_setup_with_g_main (conn, NULL);
1404
1405 /*
1406 * Add a filter function which gets called when a message comes in
1407 * and processes the message
1408 */
1409
1410 if (!dbus_connection_add_filter (conn,
1411 hald_dbus_cpufreq_filter,
1412 NULL,
1413 NULL)) {
1414 HAL_INFO ((" Cannot add the CPUFreq filter function"));
1415 return (0);
1416 }
1417
1418 dbus_connection_set_exit_on_disconnect (conn, 0);
1419
1420 g_main_loop_run (loop);
1421 }
1422