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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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