summaryrefslogtreecommitdiffstats
path: root/src/power/power.c
diff options
context:
space:
mode:
authorRadek Novacek <rnovacek@redhat.com>2012-07-23 13:41:47 +0200
committerRadek Novacek <rnovacek@redhat.com>2012-07-23 14:35:13 +0200
commitce3f2520c5046c7b7703742dd5c7481b5f98c52e (patch)
tree4e6ac6dbcb4b06f6d0ad02630a086ab263c1071b /src/power/power.c
parent3f906be3f651313c1b8709732ea0bdebba296784 (diff)
downloadopenlmi-providers-ce3f2520c5046c7b7703742dd5c7481b5f98c52e.tar.gz
openlmi-providers-ce3f2520c5046c7b7703742dd5c7481b5f98c52e.tar.xz
openlmi-providers-ce3f2520c5046c7b7703742dd5c7481b5f98c52e.zip
Restructure Power provider
* get rid of src/ subdirectory in power/ * cleanup CMake * use konkretcmpi_generate macro
Diffstat (limited to 'src/power/power.c')
-rw-r--r--src/power/power.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/src/power/power.c b/src/power/power.c
new file mode 100644
index 0000000..3210a37
--- /dev/null
+++ b/src/power/power.c
@@ -0,0 +1,518 @@
+#include "power.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include <glib.h>
+
+#include "Linux_AssociatedPowerManagementService.h"
+#include "Linux_ConcreteJob.h"
+
+#ifdef HAS_UPOWER
+#include <upower.h>
+#endif
+
+struct _Power {
+ unsigned int instances;
+ unsigned short requestedPowerState;
+ unsigned short transitioningToPowerState;
+ const CMPIBroker *broker;
+ CMPI_MUTEX_TYPE mutex;
+ GList *jobs; // list of PowerStateChangeJob
+#ifdef HAS_UPOWER
+ UpClient *up;
+#endif
+};
+
+#define MUTEX_LOCK(power) power->broker->xft->lockMutex(power->mutex)
+#define MUTEX_UNLOCK(power) power->broker->xft->unlockMutex(power->mutex)
+
+struct _PowerStateChangeJob {
+ const CMPIBroker *broker;
+ Power *power;
+ unsigned short requestedPowerState;
+ unsigned short jobState;
+ int timeOfLastChange;
+ int timeBeforeRemoval;
+ int cancelled;
+ int superseded; // There is another job that overrides this job
+ char *error;
+ CMPI_THREAD_TYPE thread;
+ CMPI_MUTEX_TYPE mutex;
+};
+
+Power *_power = NULL;
+
+
+// This is just for debugging purposes, remove later
+#include <execinfo.h>
+#include <signal.h>
+#include <unistd.h>
+void print_backtrace(int signal)
+{
+ fprintf(stderr, "BackTrace\n");
+ void *buffer[32];
+ int count = backtrace(buffer, 32);
+ fprintf(stderr, "Size: %d\n", count);
+ backtrace_symbols_fd(buffer, count, stderr->_fileno);
+ fprintf(stderr, "Segfault detected, process id: %d. Entering infinite loop.\n", getpid());
+ volatile int end = 0;
+ while (!end) {
+ sleep(1);
+ }
+}
+
+Power *power_new(const CMPIBroker *_cb)
+{
+ signal(SIGSEGV, print_backtrace);
+ fprintf(stderr, "BackTrace handler registered\n");
+
+ Power *power = malloc(sizeof(Power));
+ power->broker = _cb;
+ power->instances = 0;
+ power->requestedPowerState = Linux_AssociatedPowerManagementService_RequestedPowerState_Unknown;
+ power->transitioningToPowerState = Linux_AssociatedPowerManagementService_TransitioningToPowerState_No_Change;
+ power->mutex = _cb->xft->newMutex(0);
+ power->jobs = NULL;
+#ifdef HAS_UPOWER
+ g_type_init();
+ power->up = up_client_new();
+#endif
+ return power;
+}
+
+void power_destroy(Power *power)
+{
+#ifdef HAS_UPOWER
+ free(power->up);
+#endif
+}
+
+Power *power_ref(const CMPIBroker *_cb)
+{
+ if (_power == NULL) {
+ _power = power_new(_cb);
+ }
+ MUTEX_LOCK(_power);
+ _power->instances++;
+ MUTEX_UNLOCK(_power);
+ return _power;
+}
+
+void power_unref(Power *power)
+{
+ MUTEX_LOCK(power);
+ power->instances--;
+ MUTEX_UNLOCK(power);
+ if (power->instances == 0) {
+ power_destroy(power);
+ power = NULL;
+ _power = NULL;
+ }
+}
+
+unsigned short power_requested_power_state(Power *power)
+{
+ return power->requestedPowerState;
+}
+
+unsigned short power_transitioning_to_power_state(Power *power)
+{
+ return power->transitioningToPowerState;
+}
+
+void *state_change_thread(void *data)
+{
+ PowerStateChangeJob *powerStateChangeJob = data;
+ MUTEX_LOCK(powerStateChangeJob);
+ powerStateChangeJob->jobState = Linux_ConcreteJob_JobState_Running;
+ powerStateChangeJob->timeOfLastChange = time(NULL);
+ MUTEX_UNLOCK(powerStateChangeJob);
+
+
+ // Check if the job was cancelled
+ if (powerStateChangeJob->cancelled) {
+ MUTEX_LOCK(powerStateChangeJob);
+ powerStateChangeJob->jobState = Linux_ConcreteJob_JobState_Terminated;
+ powerStateChangeJob->timeOfLastChange = time(NULL);
+ MUTEX_UNLOCK(powerStateChangeJob);
+
+ if (!powerStateChangeJob->superseded) {
+ // There is no job that replaced this job
+ MUTEX_LOCK(powerStateChangeJob->power);
+ powerStateChangeJob->power->transitioningToPowerState = Linux_AssociatedPowerManagementService_TransitioningToPowerState_No_Change;
+ MUTEX_UNLOCK(powerStateChangeJob->power);
+ }
+
+ fprintf(stderr, "state_change_thread cancelled\n");
+ return NULL;
+ }
+
+ // Execute the job
+#ifdef HAS_UPOWER
+ GError *error = NULL;
+#endif
+
+ int succeeded = 0;
+ switch (powerStateChangeJob->requestedPowerState) {
+ case Linux_AssociatedPowerManagementService_PowerState_Sleep__Deep:
+ // Sleep
+#ifdef HAS_UPOWER
+ succeeded = up_client_suspend_sync(powerStateChangeJob->power->up, NULL, &error);
+#else
+ succeeded = system("pm-suspend") == 0;
+#endif
+ break;
+ case Linux_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Soft:
+ // Reboot (without shutting down programs)
+#ifdef HAS_SYSTEMCTL
+ succeeded = system("systemctl --force reboot &") == 0;
+#else
+ succeeded = system("reboot --force &") == 0;
+#endif
+ break;
+ case Linux_AssociatedPowerManagementService_PowerState_Hibernate_Off___Soft:
+ // Hibernate
+#ifdef HAS_UPOWER
+ succeeded = up_client_hibernate_sync(powerStateChangeJob->power->up, NULL, &error);
+#else
+ succeeded = system("pm-hibernate") == 0;
+#endif
+ break;
+ case Linux_AssociatedPowerManagementService_PowerState_Off___Soft:
+ // Poweroff (without shutting down programs)
+#ifdef HAS_SYSTEMCTL
+ succeeded = system("systemctl --force poweroff &") == 0;
+#else
+ succeeded = system("shutdown --halt now &") == 0;
+#endif
+ break;
+ case Linux_AssociatedPowerManagementService_PowerState_Off___Soft_Graceful:
+ // Poweroff (shut down programs first)
+#ifdef HAS_SYSTEMCTL
+ succeeded = system("systemctl poweroff &") == 0;
+#else
+ succeeded = system("shutdown --poweroff now &") == 0;
+#endif
+ break;
+ case Linux_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Soft_Graceful:
+ // Reboot (shut down programs first)
+#ifdef HAS_SYSTEMCTL
+ succeeded = system("systemctl reboot &") == 0;
+#else
+ succeeded = system("shutdown --reboot now &") == 0;
+#endif
+ break;
+ }
+
+ MUTEX_LOCK(powerStateChangeJob->power);
+ powerStateChangeJob->power->transitioningToPowerState = Linux_AssociatedPowerManagementService_TransitioningToPowerState_No_Change;
+ MUTEX_UNLOCK(powerStateChangeJob->power);
+
+ MUTEX_LOCK(powerStateChangeJob);
+ if (succeeded) {
+ powerStateChangeJob->jobState = Linux_ConcreteJob_JobState_Completed;
+ } else {
+ powerStateChangeJob->jobState = Linux_ConcreteJob_JobState_Exception;
+#ifdef HAS_UPOWER
+ if (error != NULL) {
+ powerStateChangeJob->error = error->message;
+ }
+#endif
+ }
+ powerStateChangeJob->timeOfLastChange = time(NULL);
+ MUTEX_UNLOCK(powerStateChangeJob);
+
+ fprintf(stderr, "state_change_thread finished\n");
+ return NULL;
+}
+
+int power_request_power_state(Power *power, unsigned short state)
+{
+ int rc = CMPI_RC_OK;
+
+ int count, found = 0;
+ unsigned short *states = power_available_requested_power_states(power, &count);
+ for (int i = 0; i < count; ++i) {
+ if (states[i] == state) {
+ found = 1;
+ break;
+ }
+ }
+ free(states);
+ if (!found) {
+ fprintf(stderr, "Invalid state: %d\n", state);
+ return CMPI_RC_ERR_INVALID_PARAMETER;
+ }
+
+ PowerStateChangeJob *powerStateChangeJob = malloc(sizeof(PowerStateChangeJob));
+ powerStateChangeJob->broker = power->broker;
+ powerStateChangeJob->power = power;
+ powerStateChangeJob->mutex = power->broker->xft->newMutex(0);
+ powerStateChangeJob->requestedPowerState = state;
+ powerStateChangeJob->jobState = Linux_ConcreteJob_JobState_New;
+ powerStateChangeJob->cancelled = 0;
+ powerStateChangeJob->superseded = 0;
+ powerStateChangeJob->timeOfLastChange = time(NULL);
+ powerStateChangeJob->timeBeforeRemoval = 300;
+ powerStateChangeJob->error = NULL;
+
+ MUTEX_LOCK(power);
+ power->requestedPowerState = state;
+ power->transitioningToPowerState = state;
+
+ PowerStateChangeJob *job;
+ GList *plist = power->jobs;
+ while (plist) {
+ job = plist->data;
+ MUTEX_LOCK(job);
+ if (job->jobState != Linux_ConcreteJob_JobState_Suspended &&
+ job->jobState != Linux_ConcreteJob_JobState_Killed &&
+ job->jobState != Linux_ConcreteJob_JobState_Terminated) {
+
+ job->cancelled = 1;
+ job->superseded = 1;
+ job->jobState = Linux_ConcreteJob_JobState_Shutting_Down;
+ job->timeOfLastChange = time(NULL);
+ }
+ MUTEX_UNLOCK(job);
+ plist = g_list_next(plist);
+ }
+ powerStateChangeJob->thread = power->broker->xft->newThread(state_change_thread, powerStateChangeJob, 1);
+ power->jobs = g_list_append(power->jobs, powerStateChangeJob);
+ MUTEX_UNLOCK(power);
+ fprintf(stderr, "State change thread started\n");
+
+ return rc;
+}
+
+unsigned short *power_available_requested_power_states(Power *power, int *count)
+{
+ unsigned short *list = malloc(17 * sizeof(unsigned short));
+ int i = 0;
+
+ /* 1 Other
+ * Linux_AssociatedPowerManagementService_PowerState_Other
+ */
+
+ /* 2 On
+ * corresponding to ACPI state G0 or S0 or D0.
+ *
+ * Bring system to full On from any state (Sleep, Hibernate, Off)
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_On
+ */
+ // not supported
+
+ /* 3 Sleep - Light
+ * corresponding to ACPI state G1, S1/S2, or D1.
+ *
+ * Standby
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Sleep___Light
+ */
+ // not supported
+
+ /* 4 Sleep - Deep
+ * corresponding to ACPI state G1, S3, or D2.
+ *
+ * Suspend
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Sleep__Deep
+ */
+ // Sleep
+#ifdef HAS_UPOWER
+ if (up_client_get_can_suspend(power->up)) {
+ list[i++] = Linux_AssociatedPowerManagementService_PowerState_Sleep__Deep;
+ }
+#else
+ if (system("pm-is-supported --suspend") == 0) {
+ list[i++] = Linux_AssociatedPowerManagementService_PowerState_Sleep__Deep;
+ }
+#endif
+
+ /* 5 Power Cycle (Off - Soft)
+ * corresponding to ACPI state G2, S5, or D3, but where the managed
+ * element is set to return to power state "On" at a pre-determined time.
+ *
+ * Reset system without removing power
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Soft
+ */
+ // Reboot (without shutting down programs)
+ list[i++] = Linux_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Soft;
+
+ /* 6 Off - Hard
+ * corresponding to ACPI state G3, S5, or D3.
+ *
+ * Power Off performed through mechanical means like unplugging
+ * power cable or UPS On
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Off___Hard
+ */
+
+ /* 7 Hibernate (Off - Soft)
+ * corresponding to ACPI state S4, where the state of the managed element
+ * is preserved and will be recovered upon powering on.
+ *
+ * System context and OS image written to non-volatile storage;
+ * system and devices powered off
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Hibernate_Off___Soft
+ */
+ // Hibernate
+#ifdef HAS_UPOWER
+ if (up_client_get_can_hibernate(power->up)) {
+ list[i++] = Linux_AssociatedPowerManagementService_PowerState_Hibernate_Off___Soft;
+ }
+#else
+ if (system("pm-is-supported --hibernate") == 0) {
+ list[i++] = Linux_AssociatedPowerManagementService_PowerState_Hibernate_Off___Soft;
+ }
+#endif
+
+ /* 8 Off - Soft
+ * corresponding to ACPI state G2, S5, or D3.
+ *
+ * System power off but auxiliary or flea power may be available
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Off___Soft
+ */
+ // Poweroff (without shutting down programs)
+ list[i++] = Linux_AssociatedPowerManagementService_PowerState_Off___Soft;
+
+ /* 9 Power Cycle (Off-Hard)
+ * corresponds to the managed element reaching the ACPI state G3
+ * followed by ACPI state S0.
+ *
+ * Equivalent to Off–Hard followed by On
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Power_Cycle_Off_Hard
+ */
+ // not implemented
+
+ /* 10 Master Bus Reset
+ * corresponds to the system reaching ACPI state S5 followed by ACPI
+ * state S0. This is used to represent system master bus reset.
+ *
+ * Hardware reset
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Master_Bus_Reset
+ */
+ // not implemented
+
+ /* 11 Diagnostic Interrupt (NMI)
+ * corresponding to the system reaching ACPI state S5 followed by ACPI
+ * state S0. This is used to represent system non-maskable interrupt.
+ *
+ * Hardware reset
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Diagnostic_Interrupt_NMI
+ */
+ // not implemented
+
+ /* 12 Off - Soft Graceful
+ * equivalent to Off Soft but preceded by a request to the managed element
+ * to perform an orderly shutdown.
+ *
+ * System power off but auxiliary or flea power may be available but preceded
+ * by a request to the managed element to perform an orderly shutdown.
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Off___Soft_Graceful
+ */
+ // Poweroff (shut down programs first)
+ list[i++] = Linux_AssociatedPowerManagementService_PowerState_Off___Soft_Graceful;
+
+ /* 13 Off - Hard Graceful
+ * equivalent to Off Hard but preceded by a request to the managed element
+ * to perform an orderly shutdown.
+ *
+ * Power Off performed through mechanical means like unplugging power cable
+ * or UPS On but preceded by a request to the managed element to perform
+ * an orderly shutdown.
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Off___Hard_Graceful
+ */
+ // not implemented
+
+ /* 14 Master Bus Rest Graceful
+ * equivalent to Master Bus Reset but preceded by a request to the managed
+ * element to perform an orderly shutdown.
+ *
+ * Hardware reset but preceded by a request to the managed element
+ * to perform an orderly shutdown.
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Master_Bus_Reset_Graceful
+ */
+ // not implemented
+
+ /* 15 Power Cycle (Off - Soft Graceful)
+ * equivalent to Power Cycle (Off - Soft) but preceded by a request
+ * to the managed element to perform an orderly shutdown.
+ *
+ * Reset system without removing power but preceded by a request
+ * to the managed element to perform an orderly shutdown.
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Soft_Graceful
+ */
+ // Reboot (shut down programs first)
+ list[i++] = Linux_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Soft_Graceful;
+
+ /* 16 Power Cycle (Off - Hard Graceful)
+ * equivalent to Power Cycle (Off - Hard) but preceded by a request
+ * to the managed element to perform an orderly shutdown.
+ *
+ * Equivalent to Off–Hard followed by On but preceded by a request
+ * to the managed element to perform an orderly shutdown.
+ *
+ * Linux_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Hard_Graceful
+ */
+ // not implemented
+
+ *count = i;
+ return list;
+}
+
+void job_free(PowerStateChangeJob *job)
+{
+ job->broker->xft->destroyMutex(job->mutex);
+}
+
+GList *power_get_jobs(Power *power)
+{
+ PowerStateChangeJob *powerStateChangeJob;
+ GList *plist = power->jobs;
+ while (plist) {
+ powerStateChangeJob = plist->data;
+ MUTEX_LOCK(powerStateChangeJob);
+ if ((powerStateChangeJob->jobState == Linux_ConcreteJob_JobState_Completed ||
+ powerStateChangeJob->jobState == Linux_ConcreteJob_JobState_Killed ||
+ powerStateChangeJob->jobState == Linux_ConcreteJob_JobState_Terminated) &&
+ time(NULL) - powerStateChangeJob->timeOfLastChange > powerStateChangeJob->timeBeforeRemoval) {
+
+ MUTEX_LOCK(power);
+ power->jobs = g_list_remove_link(power->jobs, plist);
+ MUTEX_UNLOCK(power);
+ job_free(powerStateChangeJob);
+ }
+ MUTEX_UNLOCK(powerStateChangeJob);
+ plist = g_list_next(plist);
+ }
+ return power->jobs;
+}
+
+unsigned short job_state(PowerStateChangeJob *state)
+{
+ return state->jobState;
+}
+
+int job_timeOfLastChange(PowerStateChangeJob *state)
+{
+ return state->timeOfLastChange;
+}
+
+int job_timeBeforeRemoval(PowerStateChangeJob *state)
+{
+ return state->timeBeforeRemoval;
+}
+