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