[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