/* * Copyright (C) 2012 Red Hat, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Radek Novacek */ #include "power.h" #include #include #include #include "LMI_AssociatedPowerManagementService.h" #include "LMI_ConcreteJob.h" #ifdef HAS_UPOWER #include #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 #include #include 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 = LMI_AssociatedPowerManagementService_RequestedPowerState_Unknown; power->transitioningToPowerState = LMI_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 = LMI_ConcreteJob_JobState_Running; powerStateChangeJob->timeOfLastChange = time(NULL); MUTEX_UNLOCK(powerStateChangeJob); // Check if the job was cancelled if (powerStateChangeJob->cancelled) { MUTEX_LOCK(powerStateChangeJob); powerStateChangeJob->jobState = LMI_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 = LMI_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 LMI_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 LMI_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 LMI_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 LMI_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 LMI_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 LMI_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 = LMI_AssociatedPowerManagementService_TransitioningToPowerState_No_Change; MUTEX_UNLOCK(powerStateChangeJob->power); MUTEX_LOCK(powerStateChangeJob); if (succeeded) { powerStateChangeJob->jobState = LMI_ConcreteJob_JobState_Completed; } else { powerStateChangeJob->jobState = LMI_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 = LMI_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 != LMI_ConcreteJob_JobState_Suspended && job->jobState != LMI_ConcreteJob_JobState_Killed && job->jobState != LMI_ConcreteJob_JobState_Terminated) { job->cancelled = 1; job->superseded = 1; job->jobState = LMI_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 * LMI_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) * * LMI_AssociatedPowerManagementService_PowerState_On */ // not supported /* 3 Sleep - Light * corresponding to ACPI state G1, S1/S2, or D1. * * Standby * * LMI_AssociatedPowerManagementService_PowerState_Sleep___Light */ // not supported /* 4 Sleep - Deep * corresponding to ACPI state G1, S3, or D2. * * Suspend * * LMI_AssociatedPowerManagementService_PowerState_Sleep__Deep */ // Sleep #ifdef HAS_UPOWER if (up_client_get_can_suspend(power->up)) { list[i++] = LMI_AssociatedPowerManagementService_PowerState_Sleep__Deep; } #else if (system("pm-is-supported --suspend") == 0) { list[i++] = LMI_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 * * LMI_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Soft */ // Reboot (without shutting down programs) list[i++] = LMI_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 * * LMI_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 * * LMI_AssociatedPowerManagementService_PowerState_Hibernate_Off___Soft */ // Hibernate #ifdef HAS_UPOWER if (up_client_get_can_hibernate(power->up)) { list[i++] = LMI_AssociatedPowerManagementService_PowerState_Hibernate_Off___Soft; } #else if (system("pm-is-supported --hibernate") == 0) { list[i++] = LMI_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 * * LMI_AssociatedPowerManagementService_PowerState_Off___Soft */ // Poweroff (without shutting down programs) list[i++] = LMI_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 * * LMI_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 * * LMI_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 * * LMI_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. * * LMI_AssociatedPowerManagementService_PowerState_Off___Soft_Graceful */ // Poweroff (shut down programs first) list[i++] = LMI_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. * * LMI_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. * * LMI_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. * * LMI_AssociatedPowerManagementService_PowerState_Power_Cycle_Off___Soft_Graceful */ // Reboot (shut down programs first) list[i++] = LMI_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. * * LMI_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 == LMI_ConcreteJob_JobState_Completed || powerStateChangeJob->jobState == LMI_ConcreteJob_JobState_Killed || powerStateChangeJob->jobState == LMI_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; }