[PATCH 2/2] Add support for different power methods to suspend
Alberto Milone
alberto.milone at canonical.com
Tue Jul 15 14:46:54 UTC 2014
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 | 526 +++++++++++++++++++++++++++++++++---
src/lib/include/fwts_pipeio.h | 6 +
src/lib/include/fwts_stringextras.h | 1 +
src/lib/src/fwts_pipeio.c | 108 ++++++++
src/lib/src/fwts_stringextras.c | 28 ++
5 files changed, 630 insertions(+), 39 deletions(-)
diff --git a/src/acpi/s3/s3.c b/src/acpi/s3/s3.c
index e5b6ef1..6d6d63d 100644
--- a/src/acpi/s3/s3.c
+++ b/src/acpi/s3/s3.c
@@ -28,9 +28,63 @@
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+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 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);
+}
+
+#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"
@@ -46,6 +100,8 @@ 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 int s3_init(fwts_framework *fw)
{
@@ -60,6 +116,361 @@ 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)
+ 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, 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()
+{
+ return fwts_file_first_line_contains_string("/sys/power/state", "mem");
+}
+
+static bool sysfs_can_hybrid_suspend()
+{
+ bool status;
+
+ status = fwts_file_first_line_contains_string("/sys/power/state", "disk");
+
+ if (!status)
+ return 0;
+
+ return fwts_file_first_line_contains_string("/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() : sysfs_can_suspend())
+ 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 1; */
+ }
+
+ 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;
+}
+
+/* Write to a file and report the errno in the log */
+static int write_to_file_with_log(fwts_framework *fw,
+ const gchar *file,
+ const gchar *content)
+{
+ int status;
+ status = fwts_write_string_file(file, content);
+
+ if (status != 0)
+ fwts_log_error(fw, "Writing to %s failed with errno %d\n", file, status);
+
+ return status;
+}
+
+static int sysfs_do_suspend(fwts_vars *fwts_settings)
+{
+ int status;
+
+ if (s3_hybrid) {
+ status = write_to_file_with_log(fwts_settings->fw, "/sys/power/disk", "suspend");
+
+ if (status != 0)
+ return status;
+
+ status = write_to_file_with_log(fwts_settings->fw, "/sys/power/state", "disk");
+ }
+ else {
+ status = write_to_file_with_log(fwts_settings->fw, "/sys/power/state", "mem");
+ }
+
+ return status;
+}
+
+static int wrap_logind_do_suspend(fwts_vars *fwts_settings,
+ int percent,
+ int *duration,
+ 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,
+ int percent,
+ int *duration,
+ 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,
+ int percent,
+ int *duration,
+ 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 +481,80 @@ 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 *, int, int*, char*);
+
+ fwts_settings = calloc(1, sizeof(fwts_vars));
+ 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 +901,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 +941,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 +968,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..bc9b80c 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(FILE *file, const char *str);
+int fwts_write_string_file(const char *file_name, const char *str);
+int fwts_read_file_first_line(const char *file_name, char **line);
+bool fwts_file_first_line_contains_string(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..7c1f719 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,26 @@
#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 *p)
+{
+ free(*(void**) p);
+}
+
+static inline void fclosep(FILE **file)
+{
+ if (*file)
+ fclose(*file);
+}
+
+
+#define _cleanup_free_ __attribute__((cleanup(freep)))
+#define _cleanup_fclose_ __attribute__((cleanup(fclosep)))
+
/*
* fwts_pipe_open()
* execl a command, return pid in *childpid and
@@ -184,3 +206,89 @@ 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 0 if writing worked, errno if it failed.
+ */
+int fwts_write_string_to_file(FILE *file, const char *str)
+{
+ errno = 0;
+ fputs(str, file);
+ if (!fwts_string_endswith(str, "\n"))
+ fputc('\n', file);
+
+ fflush(file);
+
+ if (ferror(file))
+ return errno ? -errno : -EIO;
+
+ return 0;
+}
+
+/*
+ * fwts_write_string_file()
+ * write a string to a file
+ * Return 0 if writing worked, errno if it failed.
+ */
+int fwts_write_string_file(const char *file_name, const char *str) {
+ _cleanup_fclose_ FILE *file = NULL;
+
+ file = fopen(file_name, "we");
+ if (!file)
+ return -errno;
+
+ return fwts_write_string_to_file(file, str);
+}
+
+/*
+ * fwts_read_file_first_line()
+ * read the first line of a file
+ * Return 0 if reading worked, errno if it failed.
+ */
+int fwts_read_file_first_line(const char *file_name, char **line)
+{
+ _cleanup_fclose_ FILE *file = NULL;
+ char buffer[LINE_MAX], *temp;
+
+ file = fopen(file_name, "re");
+ if (!file)
+ return -errno;
+
+ if (!fgets(buffer, sizeof(buffer), file)) {
+ if (ferror(file))
+ return errno ? -errno : -EIO;
+ buffer[0] = 0;
+ }
+
+ temp = strdup(buffer);
+ if (!temp)
+ return -ENOMEM;
+
+ fwts_chop_newline(temp);
+
+ *line = temp;
+ return 0;
+}
+
+/*
+ * fwts_file_first_line_contains_string()
+ * read the first line of a file
+ * Return 0 if reading worked, errno if it failed.
+ */
+bool fwts_file_first_line_contains_string(const char *file_name, const char *str)
+{
+ _cleanup_free_ char *contents = NULL;
+ int ret;
+
+ ret = fwts_read_file_first_line(file_name, &contents);
+
+ if (ret < 0) {
+ fprintf(stderr, "Cannot get the contents of %s. Errno: %d\n",
+ file_name, ret);
+ return 0;
+ }
+
+ 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
--
1.9.1
More information about the fwts-devel
mailing list