[PATCH 4/4] Add support for different power methods to suspend

Alex Hung alex.hung at canonical.com
Tue Jul 22 08:40:20 UTC 2014


On 07/18/2014 07:02 PM, Alberto Milone wrote:
> pm-utils is now deprecated and, as a result, we should rely on
> either Logind (where available) or on sysfs.
>
> While autodetection defaults to either Logind or sysfs (preferring
> The former), it is also possible to pass the --pm-method parameter
> to specify one of the following methods:
>
> logind, sysfs, pm-utils
>
> Note: quirks are only available when using pm-utils
>
> This makes a dependency on GLib necessary (only to avoid using
> the C Dbus bindings).
>
> Similar code should be written to handle S4.
>
> The new functions in fwts_pipeio.c and fwts_stringextras.c come
> from Logind's source code.
> ---
>   src/acpi/s3/s3.c                    | 550 +++++++++++++++++++++++++++++++++---
>   src/lib/include/fwts_pipeio.h       |   6 +
>   src/lib/include/fwts_stringextras.h |   1 +
>   src/lib/src/fwts_pipeio.c           | 154 ++++++++++
>   src/lib/src/fwts_stringextras.c     |  28 ++
>   5 files changed, 700 insertions(+), 39 deletions(-)
>
> diff --git a/src/acpi/s3/s3.c b/src/acpi/s3/s3.c
> index e5b6ef1..e22b5cb 100644
> --- a/src/acpi/s3/s3.c
> +++ b/src/acpi/s3/s3.c
> @@ -27,13 +27,43 @@
>   #include <sys/types.h>
>   #include <sys/stat.h>
>   #include <unistd.h>
> +#include <errno.h>
>   #include <time.h>
> +#include <glib.h>
> +#include <gio/gio.h>
> +
> +static inline void free_fwts_vars(void *);
> +static inline void freep(void *);
> +
> +#define _cleanup_free_ __attribute__((cleanup(freep)))
> +#define _cleanup_free_fw_ __attribute__((cleanup(free_fwts_vars)))
> +
> +#define PM_SUSPEND_LOGIND			"Suspend"
> +#define PM_SUSPEND_HYBRID_LOGIND	"HybridSleep"
> +#define PM_SUSPEND_PMUTILS			"pm-suspend"
> +#define PM_SUSPEND_HYBRID_PMUTILS	"pm-suspend-hybrid"
>
> -#define PM_SUSPEND 		"pm-suspend"
> -#define PM_SUSPEND_HYBRID 	"pm-suspend-hybrid"
>   #define FWTS_SUSPEND		"FWTS_SUSPEND"
>   #define FWTS_RESUME		"FWTS_RESUME"
>
> +enum pm_methods
> +{
> +	logind,
> +	pm_utils,
> +	sysfs,
> +	undefined
> +};
> +
> +typedef struct
> +{
> +	fwts_framework *fw;
> +	time_t t_start;
> +	time_t t_end;
> +	GDBusProxy *logind_proxy;
> +	GDBusConnection *logind_connection;
> +	GMainLoop *gmainloop;
> +} fwts_vars;
> +
>   static int  s3_multiple = 1;		/* number of s3 multiple tests to run */
>   static int  s3_min_delay = 0;		/* min time between resume and next suspend */
>   static int  s3_max_delay = 30;		/* max time between resume and next suspend */
> @@ -46,6 +76,34 @@ static bool s3_min_max_delay = false;
>   static float s3_suspend_time = 15.0;	/* Maximum allowed suspend time */
>   static float s3_resume_time = 15.0;	/* Maximum allowed resume time */
>   static bool s3_hybrid = false;
> +static enum pm_methods pm_method = undefined; /* Default pm-method to use to suspend */
> +
> +static inline void free_fwts_vars(void *vars)
> +{
> +	fwts_vars *var = *(void**)vars;
> +
> +	if (var) {
> +		if (var->logind_proxy) {
> +			g_object_unref(var->logind_proxy);
> +			var->logind_proxy = NULL;
> +		}
> +		if (var->logind_connection) {
> +			g_object_unref(var->logind_connection);
> +			var->logind_connection = NULL;
> +		}
> +		if (var->gmainloop) {
> +			g_main_loop_unref(var->gmainloop);
> +			var->gmainloop = NULL;
> +		}
> +	}
> +	free(var);
> +	var = NULL;
> +}
> +
> +static inline void freep(void *p)
> +{
> +	free(*(void**) p);
> +}
>
>   static int s3_init(fwts_framework *fw)
>   {
> @@ -60,6 +118,382 @@ static int s3_init(fwts_framework *fw)
>   	return FWTS_OK;
>   }
>
> +/* Initialise the Dbus proxy for Logind */
> +static int logind_init_proxy(fwts_vars *fwts_settings)
> +{
> +	int status = 0;
> +
> +	if (fwts_settings->logind_connection == NULL)
> +		fwts_settings->logind_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
> +
> +	if (fwts_settings->logind_connection == NULL) {
> +		status = 1;
> +		fwts_log_error(fwts_settings->fw, "Cannot establish a connection to Dbus\n");
> +		goto out;
> +	}
> +
> +	if (fwts_settings->logind_proxy == NULL) {
> +		fwts_settings->logind_proxy = g_dbus_proxy_new_sync(fwts_settings->logind_connection,
> +			G_DBUS_PROXY_FLAGS_NONE,
> +			NULL, "org.freedesktop.login1",
> +			"/org/freedesktop/login1",
> +			"org.freedesktop.login1.Manager",
> +			NULL, NULL);
> +	}
> +
> +	if (fwts_settings->logind_proxy == NULL) {
> +		status = 1;
> +		fwts_log_error(fwts_settings->fw,
> +			"Cannot establish a connection to login1.Manager\n");
> +		goto out;
> +	}
> +
> +out:
> +	return status;
> +}
> +
> +/* Callback to handle suspend and resume events */
> +static void logind_on_suspend_signal(
> +	GDBusConnection *connection,
> +	const gchar *sender_name,
> +	const gchar *object_path,
> +	const gchar *interface_name,
> +	const gchar *signal_name,
> +	GVariant *parameters,
> +	gpointer user_data)
> +{
> +	gboolean status;
> +	fwts_vars *fwts_settings = (fwts_vars *)user_data;
> +
> +	/* Prevent -Werror=unused-parameter from complaining */
> +	FWTS_UNUSED(connection);
> +	FWTS_UNUSED(sender_name);
> +	FWTS_UNUSED(object_path);
> +	FWTS_UNUSED(interface_name);
> +	FWTS_UNUSED(signal_name);
> +
> +	if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE ("(b)"))) {
> +		fwts_log_error(fwts_settings->fw, "Suspend type %s\n",
> +			g_variant_get_type_string(parameters));
> +		return;
> +	}
> +	else {
> +		g_variant_get(parameters, "(b)", &status);
> +		fwts_log_info(fwts_settings->fw,
> +			"Suspend status: %s\n",
> +			status ? "true" : "false");
> +
> +		if (status) {
> +			time(&(fwts_settings->t_start));
> +			(void)fwts_klog_write(fwts_settings->fw, "Starting fwts suspend\n");
> +			(void)fwts_klog_write(fwts_settings->fw, FWTS_SUSPEND "\n");
> +		}
> +		else {
> +			time(&(fwts_settings->t_end));
> +			(void)fwts_klog_write(fwts_settings->fw, FWTS_RESUME "\n");
> +			(void)fwts_klog_write(fwts_settings->fw, "Finished fwts resume\n");
> +			/*
> +			 * Let's give the system some time to get back from S3
> +			 * or Logind will refuse to suspend and shoot both events
> +			 * without doing anything
> +			 */
> +			if (s3_min_delay < 3) {
> +				fwts_log_info(fwts_settings->fw,
> +					"Skipping the minimum delay (%d) and using a 3 seconds delay instead\n",
> +					s3_min_delay);
> +				sleep(3);
> +			}
> +			g_main_loop_quit(fwts_settings->gmainloop);
> +		}
> +	}
> +}
> +
> +/* Generic function to test supported Logind actions that reply
> + * with a string
> + */
> +static bool logind_can_do_action(fwts_vars *fwts_settings, const gchar *action)
> +{
> +	GVariant *reply;
> +	GError *error = NULL;
> +	bool status = false;
> +	gchar *response;
> +
> +	if (logind_init_proxy(fwts_settings) != 0)
> +		return false;
> +
> +	reply = g_dbus_proxy_call_sync(fwts_settings->logind_proxy,
> +		action,
> +		NULL,
> +		G_DBUS_CALL_FLAGS_NONE,
> +		-1,
> +		NULL,
> +		&error);
> +
> +	if (reply != NULL) {
> +		if (!g_variant_is_of_type(reply, G_VARIANT_TYPE ("(s)"))) {
> +			fwts_log_error(fwts_settings->fw,
> +				"Unexpected response to %s action: %s\n",
> +				action,
> +				g_variant_get_type_string (reply));
> +
> +			g_variant_unref(reply);
> +			return status;
> +		}
> +
> +		g_variant_get(reply, "(&s)", &response);
> +		fwts_log_info(fwts_settings->fw, "Response to %s is %s\n",
> +			action, response);
> +
> +		if (strcmp(response, "challenge") == 0) {
> +			fwts_log_error(fwts_settings->fw,
> +				"%s action available only after authorisation\n",
> +				action);
> +		} else if (strcmp(response, "yes") == 0) {
> +			fwts_log_info(fwts_settings->fw,
> +				"User allowed to execute the %s action\n",
> +				action);
> +			status = true;
> +		} else if (strcmp(response, "no") == 0) {
> +			fwts_log_error(fwts_settings->fw,
> +				"User not allowed to execute the %s action\n",
> +				action);
> +		} else if (strcmp(response, "na") == 0) {
> +			fwts_log_error(fwts_settings->fw,
> +				"Hardware doesn't support %s action\n",
> +				action);
> +		}
> +
> +		g_variant_unref(reply);
> +	}
> +	else {
> +		fwts_log_error(fwts_settings->fw,
> +			"Invalid response from Logind on %s action\n",
> +			action);
> +		g_error_free(error);
> +	}
> +
> +	return status;
> +}
> +
> +static bool logind_can_suspend(fwts_vars *fwts_settings)
> +{
> +	return logind_can_do_action(fwts_settings, "CanSuspend");
> +}
> +
> +static bool logind_can_hybrid_suspend(fwts_vars *fwts_settings)
> +{
> +	return logind_can_do_action(fwts_settings, "CanHybridSleep");
> +}
> +
> +static bool sysfs_can_suspend(const fwts_vars *fwts_settings)
> +{
> +	return fwts_file_first_line_contains_string(fwts_settings->fw,
> +		"/sys/power/state",
> +		"mem");
> +}
> +
> +static bool sysfs_can_hybrid_suspend(const fwts_vars *fwts_settings)
> +{
> +	bool status;
> +
> +	status = fwts_file_first_line_contains_string(fwts_settings->fw,
> +		"/sys/power/state",
> +		"disk");
> +
> +	if (!status)
> +		return FALSE;
> +
> +	return fwts_file_first_line_contains_string(fwts_settings->fw,
> +		"/sys/power/disk",
> +		"suspend");
> +}
> +
> +/* Detect the best available power method */
> +static enum pm_methods detect_pm_method(fwts_vars *fwts_settings)
> +{
> +	if (s3_hybrid ?
> +		logind_can_hybrid_suspend(fwts_settings) :
> +		logind_can_suspend(fwts_settings))
> +		return logind;
> +	else if (s3_hybrid ?
> +		sysfs_can_hybrid_suspend(fwts_settings) :
> +		sysfs_can_suspend(fwts_settings))
> +		return sysfs;
> +	else
> +		return pm_utils;
> +}
> +
> +/* Call Logind to suspend.
> + * action can be either "Suspend" or "HybridSleep"
> + */
> +static gboolean logind_do_suspend(gpointer data)
> +{
> +	GError *error = NULL;
> +	GVariant *reply;
> +	fwts_vars *fwts_settings = (fwts_vars *)data;
> +
> +	/* If the loop is not running, return TRUE so as to repeat the operation */
> +	if (g_main_loop_is_running (fwts_settings->gmainloop)) {
> +		gchar *action = s3_hybrid ? PM_SUSPEND_HYBRID_LOGIND : PM_SUSPEND_LOGIND;
> +		fwts_log_info(fwts_settings->fw, "Requesting %s action\n", action);
> +		reply = g_dbus_proxy_call_sync(fwts_settings->logind_proxy,
> +			action,
> +			g_variant_new ("(b)",
> +					FALSE),
> +			G_DBUS_CALL_FLAGS_NONE,
> +			-1,
> +			NULL,
> +			&error);
> +
> +		if (reply != NULL) {
> +			g_variant_unref(reply);
> +		}
> +		else {
> +			fwts_log_error(fwts_settings->fw,
> +				"Error from Logind: %s\n",
> +				error->message);
> +			g_error_free(error);
> +		}
> +
> +		return FALSE;
> +
> +	}
> +	fwts_log_info(fwts_settings->fw, "Glib loop not ready\n");
> +	return TRUE;
> +}
> +
> +/* Start Glib mainloop and listen to suspend/resume events
> + * coming from Logind.
> + * Exit the loop and return the duration after an event.
> + */
> +static int logind_wait_for_suspend_resume(fwts_vars *fwts_settings)
> +{
> +	guint subscription_id = 0;
> +	int duration = 0;
> +
> +	if (logind_init_proxy(fwts_settings) != 0)
> +		return 0;
> +
> +	subscription_id = g_dbus_connection_signal_subscribe (fwts_settings->logind_connection,
> +				"org.freedesktop.login1", /* sender */
> +				"org.freedesktop.login1.Manager",
> +				"PrepareForSleep",
> +				"/org/freedesktop/login1",
> +				NULL, /* arg0 */
> +				G_DBUS_SIGNAL_FLAGS_NONE,
> +				logind_on_suspend_signal,
> +				fwts_settings,
> +				NULL);
> +
> +	fwts_settings->gmainloop = g_main_loop_new(NULL, FALSE);
> +	if (fwts_settings->gmainloop) {
> +		g_timeout_add(0.1,
> +					  logind_do_suspend,
> +					  fwts_settings);
> +
> +		g_main_loop_run(fwts_settings->gmainloop);
> +		duration = (int)(fwts_settings->t_end - fwts_settings->t_start);
> +
> +		/* Optional, as it will be freed together with the struct */
> +		g_main_loop_unref(fwts_settings->gmainloop);
> +		fwts_settings->gmainloop = NULL;
> +	}
> +	else {
> +		fwts_log_error(fwts_settings->fw, "Failed to start glib mainloop\n");
> +	}
> +
> +	g_dbus_connection_signal_unsubscribe(fwts_settings->logind_connection,
> +		subscription_id);
> +
> +	return duration;
> +}
> +
> +static int sysfs_do_suspend(const fwts_vars *fwts_settings)
> +{
> +	int status;
> +
> +	if (s3_hybrid) {
> +		status = fwts_write_string_file(fwts_settings->fw,
> +			"/sys/power/disk",
> +			"suspend");
> +
> +		if (status != FWTS_OK)
> +			return status;
> +
> +		status = fwts_write_string_file(fwts_settings->fw,
> +			"/sys/power/state",
> +			"disk");
> +	}
> +	else {
> +		status = fwts_write_string_file(fwts_settings->fw,
> +			"/sys/power/state",
> +			"mem");
> +	}
> +
> +	return status;
> +}
> +
> +static int wrap_logind_do_suspend(fwts_vars *fwts_settings,
> +	const int percent,
> +	int *duration,
> +	const char *str)
> +{
> +	FWTS_UNUSED(str);
> +	fwts_progress_message(fwts_settings->fw, percent, "(Suspending)");
> +
> +	/* This enters a glib mainloop */
> +	*duration = logind_wait_for_suspend_resume(fwts_settings);
> +	fwts_log_info(fwts_settings->fw, "S3 duration = %d.", *duration);
> +	fwts_progress_message(fwts_settings->fw, percent, "(Resumed)");
> +
> +	return *duration > 0 ? 0 : 1;
> +}
> +
> +static int wrap_sysfs_do_suspend(fwts_vars *fwts_settings,
> +	const int percent,
> +	int *duration,
> +	const char *str)
> +{
> +	int status;
> +
> +	FWTS_UNUSED(str);
> +	fwts_progress_message(fwts_settings->fw, percent, "(Suspending)");
> +	time(&(fwts_settings->t_start));
> +	(void)fwts_klog_write(fwts_settings->fw, "Starting fwts suspend\n");
> +	(void)fwts_klog_write(fwts_settings->fw, FWTS_SUSPEND "\n");
> +	status = sysfs_do_suspend(fwts_settings);
> +	(void)fwts_klog_write(fwts_settings->fw, FWTS_RESUME "\n");
> +	(void)fwts_klog_write(fwts_settings->fw, "Finished fwts resume\n");
> +	time(&(fwts_settings->t_end));
> +	fwts_progress_message(fwts_settings->fw, percent, "(Resumed)");
> +
> +	*duration = (int)(fwts_settings->t_end - fwts_settings->t_start);
> +
> +	return status;
> +}
> +
> +static int wrap_pmutils_do_suspend(fwts_vars *fwts_settings,
> +	const int percent,
> +	int *duration,
> +	const char *command)
> +{
> +	int status;
> +
> +	fwts_progress_message(fwts_settings->fw, percent, "(Suspending)");
> +	time(&(fwts_settings->t_start));
> +	(void)fwts_klog_write(fwts_settings->fw, "Starting fwts suspend\n");
> +	(void)fwts_klog_write(fwts_settings->fw, FWTS_SUSPEND "\n");
> +	(void)fwts_exec(command, &status);
> +	(void)fwts_klog_write(fwts_settings->fw, FWTS_RESUME "\n");
> +	(void)fwts_klog_write(fwts_settings->fw, "Finished fwts resume\n");
> +	time(&(fwts_settings->t_end));
> +	fwts_progress_message(fwts_settings->fw, percent, "(Resumed)");
> +
> +	*duration = (int)(fwts_settings->t_end - fwts_settings->t_start);
> +
> +	return status;
> +}
> +
>   static int s3_do_suspend_resume(fwts_framework *fw,
>   	int *hw_errors,
>   	int *pm_errors,
> @@ -70,54 +504,81 @@ static int s3_do_suspend_resume(fwts_framework *fw,
>   	int status;
>   	int duration;
>   	int differences;
> -	time_t t_start;
> -	time_t t_end;
> -	char *command;
> -	char *quirks;
> +	_cleanup_free_ char *command = NULL;
> +	_cleanup_free_ char *quirks = NULL;
> +	_cleanup_free_fw_ fwts_vars * fwts_settings = NULL;
>   	char buffer[80];
>
> +
> +	int (*do_suspend)(fwts_vars *, const int, int*, const char*);
> +
> +	fwts_settings = calloc(1, sizeof(fwts_vars));
> +	if (fwts_settings == NULL)
> +		return FWTS_OUT_OF_MEMORY;
> +	fwts_settings->fw = fw;
> +
> +	if (pm_method == undefined) {
> +		/* Autodetection */
> +		fwts_log_info(fw, "Detecting the power method.");
> +		pm_method = detect_pm_method(fwts_settings);
> +	}
> +
> +	switch (pm_method) {
> +		case logind:
> +			fwts_log_info(fw, "Using logind as the default power method.");
> +			if (logind_init_proxy(fwts_settings) != 0) {
> +				fwts_log_error(fw, "Failure to connect to Logind.");
> +				return FWTS_ERROR;
> +			}
> +			do_suspend = &wrap_logind_do_suspend;
> +			break;
> +		case pm_utils:
> +			fwts_log_info(fw, "Using pm-utils as the default power method.");
> +			do_suspend = &wrap_pmutils_do_suspend;
> +			break;
> +		case sysfs:
> +			fwts_log_info(fw, "Using sysfs as the default power method.");
> +			do_suspend = &wrap_sysfs_do_suspend;
> +			break;
> +		default:
> +			/* This should never happen */
> +			fwts_log_info(fw, "Using sysfs as the default power method.");
> +			do_suspend = &wrap_sysfs_do_suspend;
> +			break;
> +	}
> +
>   	if (s3_device_check)
>   		fwts_hwinfo_get(fw, &hwinfo1);
>
>   	/* Format up pm-suspend command with optional quirking arguments */
> -	if (s3_hybrid) {
> -		if ((command = fwts_realloc_strcat(NULL, PM_SUSPEND_HYBRID)) == NULL)
> -			return FWTS_OUT_OF_MEMORY;
> -	} else {
> -		if ((command = fwts_realloc_strcat(NULL, PM_SUSPEND)) == NULL)
> -			return FWTS_OUT_OF_MEMORY;
> -	}
> -
> -	if (s3_quirks) {
> -		if ((command = fwts_realloc_strcat(command, " ")) == NULL)
> -			return FWTS_OUT_OF_MEMORY;
> -		if ((quirks = fwts_args_comma_list(s3_quirks)) == NULL) {
> -			free(command);
> -			return FWTS_OUT_OF_MEMORY;
> +	if (pm_method == pm_utils) {
> +		if (s3_hybrid) {
> +			if ((command = fwts_realloc_strcat(NULL, PM_SUSPEND_HYBRID_PMUTILS)) == NULL)
> +				return FWTS_OUT_OF_MEMORY;
> +		} else {
> +			if ((command = fwts_realloc_strcat(NULL, PM_SUSPEND_PMUTILS)) == NULL)
> +				return FWTS_OUT_OF_MEMORY;
>   		}
> -		if ((command = fwts_realloc_strcat(command, quirks)) == NULL) {
> -			free(quirks);
> -			return FWTS_OUT_OF_MEMORY;
> +
> +		/* For now we only support quirks with pm_utils */
> +		if (s3_quirks) {
> +			if ((command = fwts_realloc_strcat(command, " ")) == NULL)
> +				return FWTS_OUT_OF_MEMORY;
> +			if ((quirks = fwts_args_comma_list(s3_quirks)) == NULL) {
> +				return FWTS_OUT_OF_MEMORY;
> +			}
> +			if ((command = fwts_realloc_strcat(command, quirks)) == NULL) {
> +				return FWTS_OUT_OF_MEMORY;
> +			}
>   		}
> -		free(quirks);
>   	}
>
>   	fwts_wakealarm_trigger(fw, delay);
>
>   	/* Do S3 here */
> -	fwts_progress_message(fw, percent, "(Suspending)");
> -	time(&t_start);
> -	(void)fwts_klog_write(fw, "Starting fwts suspend\n");
> -	(void)fwts_klog_write(fw, FWTS_SUSPEND "\n");
> -	(void)fwts_exec(command, &status);
> -	(void)fwts_klog_write(fw, FWTS_RESUME "\n");
> -	(void)fwts_klog_write(fw, "Finished fwts resume\n");
> -	time(&t_end);
> -	fwts_progress_message(fw, percent, "(Resumed)");
> -	free(command);
> +	status = do_suspend(fwts_settings, percent, &duration, command);
>
> -	duration = (int)(t_end - t_start);
> -	fwts_log_info(fw, "pm-suspend returned %d after %d seconds.", status, duration);
> +	fwts_log_info(fw, "pm-action returned %d after %d seconds.", status, duration);
>
>   	if (s3_device_check) {
>   		int i;
> @@ -464,9 +925,9 @@ static int s3_options_handler(fwts_framework *fw, int argc, char * const argv[],
>   	FWTS_UNUSED(argc);
>   	FWTS_UNUSED(argv);
>
> -        switch (option_char) {
> -        case 0:
> -                switch (long_index) {
> +	switch (option_char) {
> +	case 0:
> +		switch (long_index) {
>   		case 0:
>   			s3_multiple = atoi(optarg);
>   			break;
> @@ -504,6 +965,16 @@ static int s3_options_handler(fwts_framework *fw, int argc, char * const argv[],
>   		case 10:
>   			s3_hybrid = true;
>   			break;
> +		case 11:
> +			if (strcmp(optarg, "logind") == 0)
> +				pm_method = logind;
> +			else if (strcmp(optarg, "pm-utils") == 0)
> +				pm_method = pm_utils;
> +			else if (strcmp(optarg, "sysfs") == 0)
> +				pm_method = sysfs;
> +			else
> +				return FWTS_ERROR;
> +			break;
>   		}
>   	}
>   	return FWTS_OK;
> @@ -521,6 +992,7 @@ static fwts_option s3_options[] = {
>   	{ "s3-suspend-time",	"", 1, "Maximum expected suspend time in seconds, e.g. --s3-suspend-time=3.5" },
>   	{ "s3-resume-time", 	"", 1, "Maximum expected resume time in seconds, e.g. --s3-resume-time=5.1" },
>   	{ "s3-hybrid",		"", 0, "Run S3 with hybrid sleep, i.e. saving system states as S4 does." },
> +	{ "pm-method",      "", 1, "Select the power method to use. Accepted values are \"logind\", \"pm-utils\", \"sysfs\""},
>   	{ NULL, NULL, 0, NULL }
>   };
>
> diff --git a/src/lib/include/fwts_pipeio.h b/src/lib/include/fwts_pipeio.h
> index 2429e0a..07ece8c 100644
> --- a/src/lib/include/fwts_pipeio.h
> +++ b/src/lib/include/fwts_pipeio.h
> @@ -20,9 +20,11 @@
>   #ifndef __FWTS_PIPEIO_H__
>   #define __FWTS_PIPEIO_H__
>
> +#include <stdio.h>
>   #include <unistd.h>
>   #include <sys/types.h>
>   #include <sys/wait.h>
> +#include <stdbool.h>
>
>   #include "fwts.h"
>
> @@ -33,5 +35,9 @@ char *fwts_pipe_read(const int fd, ssize_t *length);
>   int   fwts_pipe_close(const int fd, const pid_t pid);
>   int   fwts_pipe_exec(const char *command, fwts_list **list, int *status);
>   int   fwts_exec(const char *command, int *status);
> +int   fwts_write_string_to_file(const fwts_framework *fw, FILE *file, const char *str);
> +int   fwts_write_string_file(const fwts_framework *fw, const char *file_name, const char *str);
> +int   fwts_read_file_first_line(const fwts_framework *fw, const char *file_name, char **line);
> +bool  fwts_file_first_line_contains_string(const fwts_framework *fw, const char *file_name, const char *str);
>
>   #endif
> diff --git a/src/lib/include/fwts_stringextras.h b/src/lib/include/fwts_stringextras.h
> index 3d89fe6..0a2b9c7 100644
> --- a/src/lib/include/fwts_stringextras.h
> +++ b/src/lib/include/fwts_stringextras.h
> @@ -24,5 +24,6 @@
>
>   void fwts_chop_newline(char *str);
>   char *fwts_realloc_strcat(char *orig, const char *newstr);
> +char *fwts_string_endswith(const char *str, const char *postfix);
>
>   #endif
> diff --git a/src/lib/src/fwts_pipeio.c b/src/lib/src/fwts_pipeio.c
> index df07295..7ed3160 100644
> --- a/src/lib/src/fwts_pipeio.c
> +++ b/src/lib/src/fwts_pipeio.c
> @@ -1,6 +1,11 @@
>   /*
>    * Copyright (C) 2010-2014 Canonical
>    *
> + * The following functions are derivative work from systemd, and
> + * are covered by Copyright 2010 Lennart Poettering:
> + *     fwts_write_string_to_file(), fwts_write_string_file(),
> + *     fwts_write_string_file(), fwts_read_file_first_line()
> + *
>    * This program is free software; you can redistribute it and/or
>    * modify it under the terms of the GNU General Public License
>    * as published by the Free Software Foundation; either version 2
> @@ -29,9 +34,28 @@
>   #include <sys/types.h>
>   #include <sys/wait.h>
>   #include <errno.h>
> +#include <limits.h>
> +#include <stdbool.h>
>
>   #include "fwts.h"
>
> +static inline void freep(void *);
> +static inline void fclosep(FILE **);
> +
> +#define _cleanup_free_ __attribute__((cleanup(freep)))
> +#define _cleanup_fclose_ __attribute__((cleanup(fclosep)))
> +
> +static inline void freep(void *p)
> +{
> +	free(*(void**) p);
> +}
> +
> +static inline void fclosep(FILE **file)
> +{
> +	if (*file)
> +		fclose(*file);
> +}
> +
>   /*
>    *  fwts_pipe_open()
>    *	execl a command, return pid in *childpid and
> @@ -184,3 +208,133 @@ int fwts_exec(const char *command, int *status)
>   		return FWTS_EXEC_ERROR;
>   	return FWTS_OK;
>   }
> +
> +/*
> + *  fwts_write_string_to_file()
> + *	write a string to a file pointer
> + *	Return FWTS_OK if writing worked, FWTS_ERROR if it failed.
> + */
> +int fwts_write_string_to_file(
> +	const fwts_framework *fw,
> +	FILE *file,
> +	const char *str)
> +{
> +	errno = 0;
> +	fputs(str, file);
> +	if (!fwts_string_endswith(str, "\n"))
> +		fputc('\n', file);
> +
> +	fflush(file);
> +
> +	if (ferror(file)) {
> +		fwts_log_error(fw,
> +			"Failed to write string '%s' to file descriptor, error: %d (%s).",
> +			str,
> +			errno,
> +			strerror(errno));
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +/*
> + *  fwts_write_string_file()
> + *	write a string to a file
> + *	Return FWTS_OK if writing worked, FWTS_ERROR if it failed.
> + */
> +int fwts_write_string_file(
> +	const fwts_framework *fw,
> +	const char *file_name,
> +	const char *str)
> +{
> +	_cleanup_fclose_ FILE *file = NULL;
> +	errno = 0;
> +
> +	file = fopen(file_name, "we");
> +	if (!file) {
> +		fwts_log_error(fw,
> +			"Failed to write string '%s' to %s, error: %d (%s).",
> +			str,
> +			file_name,
> +			errno,
> +			strerror(errno));
> +		return FWTS_ERROR;
> +	}
> +
> +	return fwts_write_string_to_file(fw, file, str);
> +}
> +
> +/*
> + *  fwts_read_file_first_line()
> + *	read the first line of a file
> + *	Return FWTS_OK if reading worked, FWTS_ERROR if it failed,
> + *      or FWTS_OUT_OF_MEMORY if it went out of memory.
> + */
> +int fwts_read_file_first_line(
> +	const fwts_framework *fw,
> +	const char *file_name,
> +	char **line)
> +{
> +	_cleanup_fclose_ FILE *file = NULL;
> +	char buffer[LINE_MAX], *temp;
> +	errno = 0;
> +
> +	file = fopen(file_name, "re");
> +	if (!file) {
> +		fwts_log_error(fw,
> +			"Failed to read first line from %s, error: %d (%s).",
> +			file_name,
> +			errno,
> +			strerror(errno));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!fgets(buffer, sizeof(buffer), file)) {
> +		if (ferror(file)) {
> +			fwts_log_error(fw,
> +				"Failed to read first line from %s, error: %d (%s).",
> +				file_name,
> +				errno,
> +				strerror(errno));
> +			return FWTS_ERROR;
> +		}
> +		buffer[0] = 0;
> +	}
> +
> +	temp = strdup(buffer);
> +	if (!temp) {
> +		fwts_log_error(fw,
> +			"Failed to read first line from %s: ran out of memory.",
> +			file_name);
> +		return FWTS_OUT_OF_MEMORY;
> +	}
> +
> +	fwts_chop_newline(temp);
> +	*line = temp;
> +
> +	return FWTS_OK;
> +}
> +
> +/*
> + *  fwts_file_first_line_contains_string()
> + *	read the first line of a file
> + *	Return true if the line contained the string, false if it didn't.
> + */
> +bool fwts_file_first_line_contains_string(
> +	const fwts_framework *fw,
> +	const char *file_name,
> +	const char *str)
> +{
> +	_cleanup_free_ char *contents = NULL;
> +	int ret;
> +
> +	ret = fwts_read_file_first_line(fw, file_name, &contents);
> +
> +	if (ret != FWTS_OK) {
> +		fwts_log_error(fw, "Failed to get the contents of %s.", file_name);
> +		return false;
> +	}
> +
> +	return (strstr(contents, str) != NULL);
> +}
> \ No newline at end of file
> diff --git a/src/lib/src/fwts_stringextras.c b/src/lib/src/fwts_stringextras.c
> index 7c3adac..a46f360 100644
> --- a/src/lib/src/fwts_stringextras.c
> +++ b/src/lib/src/fwts_stringextras.c
> @@ -1,6 +1,10 @@
>   /*
>    * Copyright (C) 2010-2014 Canonical
>    *
> + * The following functions are derivative work from systemd, and
> + * are covered by Copyright 2010 Lennart Poettering:
> + *     fwts_string_endswith()
> + *
>    * This program is free software; you can redistribute it and/or
>    * modify it under the terms of the GNU General Public License
>    * as published by the Free Software Foundation; either version 2
> @@ -60,3 +64,27 @@ char *fwts_realloc_strcat(char *orig, const char *newstr)
>   	}
>   	return orig;
>   }
> +
> +/*
> + * fwts_string_endswith()
> + * see if str ends with postfix
> + * return NULL if fails, otherwise return the matched substring
> + */
> +char* fwts_string_endswith(const char *str, const char *postfix)
> +{
> +	size_t sl, pl;
> +
> +	sl = strlen(str);
> +	pl = strlen(postfix);
> +
> +	if (pl == 0)
> +		return (char*) str + sl;
> +
> +	if (sl < pl)
> +		return NULL;
> +
> +	if (memcmp(str + sl - pl, postfix, pl) != 0)
> +		return NULL;
> +
> +	return (char*) str + sl - pl;
> +}
> \ No newline at end of file
>

Acked-by: Alex Hung <alex.hung at canonical.com>



More information about the fwts-devel mailing list