Blame src/utils/exec.c

Packit 2ba279
/*
Packit 2ba279
 * Copyright (C) 2014  Red Hat, Inc.
Packit 2ba279
 *
Packit 2ba279
 * This library is free software; you can redistribute it and/or
Packit 2ba279
 * modify it under the terms of the GNU Lesser General Public
Packit 2ba279
 * License as published by the Free Software Foundation; either
Packit 2ba279
 * version 2.1 of the License, or (at your option) any later version.
Packit 2ba279
 *
Packit 2ba279
 * This library is distributed in the hope that it will be useful,
Packit 2ba279
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 2ba279
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 2ba279
 * Lesser General Public License for more details.
Packit 2ba279
 *
Packit 2ba279
 * You should have received a copy of the GNU Lesser General Public
Packit 2ba279
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit 2ba279
 *
Packit 2ba279
 * Author: Vratislav Podzimek <vpodzime@redhat.com>
Packit 2ba279
 */
Packit 2ba279
Packit 2ba279
#include <glib.h>
Packit 2ba279
#include "exec.h"
Packit 2ba279
#include "extra_arg.h"
Packit 2ba279
#include <syslog.h>
Packit 2ba279
#include <stdlib.h>
Packit 2ba279
#include <errno.h>
Packit 2ba279
#include <sys/types.h>
Packit 2ba279
#include <sys/wait.h>
Packit 2ba279
#include <unistd.h>
Packit 2ba279
Packit 2ba279
#ifdef __clang__
Packit 2ba279
#define ZERO_INIT {}
Packit 2ba279
#else
Packit 2ba279
#define ZERO_INIT {0}
Packit 2ba279
#endif
Packit 2ba279
Packit 2ba279
extern char **environ;
Packit 2ba279
Packit 2ba279
static GMutex id_counter_lock;
Packit 2ba279
static guint64 id_counter = 0;
Packit 2ba279
static BDUtilsLogFunc log_func = NULL;
Packit 2ba279
Packit 2ba279
static GMutex task_id_counter_lock;
Packit 2ba279
static guint64 task_id_counter = 0;
Packit 2ba279
static BDUtilsProgFunc prog_func = NULL;
Packit 2ba279
static __thread BDUtilsProgFunc thread_prog_func = NULL;
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_exec_error_quark: (skip)
Packit 2ba279
 */
Packit 2ba279
GQuark bd_utils_exec_error_quark (void)
Packit 2ba279
{
Packit 2ba279
    return g_quark_from_static_string ("g-bd-utils-exec-error-quark");
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * get_next_task_id: (skip)
Packit 2ba279
 */
Packit 2ba279
guint64 get_next_task_id (void) {
Packit 2ba279
    guint64 task_id = 0;
Packit 2ba279
Packit 2ba279
    g_mutex_lock (&id_counter_lock);
Packit 2ba279
    id_counter++;
Packit 2ba279
    task_id = id_counter;
Packit 2ba279
    g_mutex_unlock (&id_counter_lock);
Packit 2ba279
Packit 2ba279
    return task_id;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * log_task_status: (skip)
Packit 2ba279
 * @task_id: ID of the task the status of which is being logged
Packit 2ba279
 * @msg: log message
Packit 2ba279
 */
Packit 2ba279
void log_task_status (guint64 task_id, const gchar *msg) {
Packit 2ba279
    gchar *log_msg = NULL;
Packit 2ba279
Packit 2ba279
    if (log_func) {
Packit 2ba279
        log_msg = g_strdup_printf ("[%"G_GUINT64_FORMAT"] %s", task_id, msg);
Packit 2ba279
        log_func (LOG_INFO, log_msg);
Packit 2ba279
        g_free (log_msg);
Packit 2ba279
    }
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * log_running: (skip)
Packit 2ba279
 *
Packit 2ba279
 * Returns: id of the running task
Packit 2ba279
 */
Packit 2ba279
static guint64 log_running (const gchar **argv) {
Packit 2ba279
    guint64 task_id = 0;
Packit 2ba279
    gchar *str_argv = NULL;
Packit 2ba279
    gchar *log_msg = NULL;
Packit 2ba279
Packit 2ba279
    task_id = get_next_task_id ();
Packit 2ba279
Packit 2ba279
    if (log_func) {
Packit 2ba279
        str_argv = g_strjoinv (" ", (gchar **) argv);
Packit 2ba279
        log_msg = g_strdup_printf ("Running [%"G_GUINT64_FORMAT"] %s ...", task_id, str_argv);
Packit 2ba279
        log_func (LOG_INFO, log_msg);
Packit 2ba279
        g_free (str_argv);
Packit 2ba279
        g_free (log_msg);
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    return task_id;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * log_out: (skip)
Packit 2ba279
 *
Packit 2ba279
 */
Packit 2ba279
static void log_out (guint64 task_id, const gchar *stdout, const gchar *stderr) {
Packit 2ba279
    gchar *log_msg = NULL;
Packit 2ba279
Packit 2ba279
    if (log_func) {
Packit 2ba279
        log_msg = g_strdup_printf ("stdout[%"G_GUINT64_FORMAT"]: %s", task_id, stdout);
Packit 2ba279
        log_func (LOG_INFO, log_msg);
Packit 2ba279
        g_free (log_msg);
Packit 2ba279
Packit 2ba279
        log_msg = g_strdup_printf ("stderr[%"G_GUINT64_FORMAT"]: %s", task_id, stderr);
Packit 2ba279
        log_func (LOG_INFO, log_msg);
Packit 2ba279
        g_free (log_msg);
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    return;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * log_done: (skip)
Packit 2ba279
 *
Packit 2ba279
 */
Packit 2ba279
static void log_done (guint64 task_id, gint exit_code) {
Packit 2ba279
    gchar *log_msg = NULL;
Packit 2ba279
Packit 2ba279
    if (log_func) {
Packit 2ba279
        log_msg = g_strdup_printf ("...done [%"G_GUINT64_FORMAT"] (exit code: %d)", task_id, exit_code);
Packit 2ba279
        log_func (LOG_INFO, log_msg);
Packit 2ba279
        g_free (log_msg);
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    return;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
static void set_c_locale(gpointer user_data __attribute__((unused))) {
Packit 2ba279
    if (setenv ("LC_ALL", "C", 1) != 0)
Packit 2ba279
        g_warning ("Failed to set LC_ALL=C for a child process!");
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_exec_and_report_error:
Packit 2ba279
 * @argv: (array zero-terminated=1): the argv array for the call
Packit 2ba279
 * @extra: (allow-none) (array zero-terminated=1): extra arguments
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether the @argv was successfully executed (no error and exit code 0) or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_exec_and_report_error (const gchar **argv, const BDExtraArg **extra, GError **error) {
Packit 2ba279
    gint status = 0;
Packit 2ba279
    /* just use the "stronger" function providing dumb progress reporting (just
Packit 2ba279
       'started' and 'finished') and throw away the returned status */
Packit 2ba279
    return bd_utils_exec_and_report_progress (argv, extra, NULL, &status, error);
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_exec_and_report_error_no_progress:
Packit 2ba279
 * @argv: (array zero-terminated=1): the argv array for the call
Packit 2ba279
 * @extra: (allow-none) (array zero-terminated=1): extra arguments
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether the @argv was successfully executed (no error and exit code 0) or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_exec_and_report_error_no_progress (const gchar **argv, const BDExtraArg **extra, GError **error) {
Packit 2ba279
    gint status = 0;
Packit 2ba279
    /* just use the "stronger" function and throw away the returned status */
Packit 2ba279
    return bd_utils_exec_and_report_status_error (argv, extra, &status, error);
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_exec_and_report_status_error:
Packit 2ba279
 * @argv: (array zero-terminated=1): the argv array for the call
Packit 2ba279
 * @extra: (allow-none) (array zero-terminated=1): extra arguments
Packit 2ba279
 * @status: (out): place to store the status
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether the @argv was successfully executed (no error and exit code 0) or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_exec_and_report_status_error (const gchar **argv, const BDExtraArg **extra, gint *status, GError **error) {
Packit 2ba279
    gboolean success = FALSE;
Packit 2ba279
    gchar *stdout_data = NULL;
Packit 2ba279
    gchar *stderr_data = NULL;
Packit 2ba279
    guint64 task_id = 0;
Packit 2ba279
    const gchar **args = NULL;
Packit 2ba279
    guint args_len = 0;
Packit 2ba279
    const gchar **arg_p = NULL;
Packit 2ba279
    const BDExtraArg **extra_p = NULL;
Packit 2ba279
    gint exit_status = 0;
Packit 2ba279
    guint i = 0;
Packit 2ba279
Packit 2ba279
    if (extra) {
Packit 2ba279
        args_len = g_strv_length ((gchar **) argv);
Packit 2ba279
        for (extra_p=extra; *extra_p; extra_p++) {
Packit 2ba279
            if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0))
Packit 2ba279
                args_len++;
Packit 2ba279
            if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0))
Packit 2ba279
                args_len++;
Packit 2ba279
        }
Packit 2ba279
        args = g_new0 (const gchar*, args_len + 1);
Packit 2ba279
        for (arg_p=argv; *arg_p; arg_p++, i++)
Packit 2ba279
            args[i] = *arg_p;
Packit 2ba279
        for (extra_p=extra; *extra_p; extra_p++) {
Packit 2ba279
            if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0)) {
Packit 2ba279
                args[i] = (*extra_p)->opt;
Packit 2ba279
                i++;
Packit 2ba279
            }
Packit 2ba279
            if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0)) {
Packit 2ba279
                args[i] = (*extra_p)->val;
Packit 2ba279
                i++;
Packit 2ba279
            }
Packit 2ba279
        }
Packit 2ba279
        args[i] = NULL;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    task_id = log_running (args ? args : argv);
Packit 2ba279
    success = g_spawn_sync (NULL, args ? (gchar **) args : (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
Packit 2ba279
                            (GSpawnChildSetupFunc) set_c_locale, NULL,
Packit 2ba279
                            &stdout_data, &stderr_data, &exit_status, error);
Packit 2ba279
    if (!success) {
Packit 2ba279
        /* error is already populated from the call */
Packit 2ba279
        g_free (stdout_data);
Packit 2ba279
        g_free (stderr_data);
Packit 2ba279
        return FALSE;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    /* g_spawn_sync set the status in the same way waitpid() does, we need
Packit 2ba279
       to get the process exit code manually (this is similar to calling
Packit 2ba279
       WEXITSTATUS but also sets the error for terminated processes */
Packit 2ba279
    if (!g_spawn_check_exit_status (exit_status, error)) {
Packit 2ba279
        if (g_error_matches (*error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED)) {
Packit 2ba279
            /* process was terminated abnormally (e.g. using a signal) */
Packit 2ba279
            g_free (stdout_data);
Packit 2ba279
            g_free (stderr_data);
Packit 2ba279
            return FALSE;
Packit 2ba279
        }
Packit 2ba279
Packit 2ba279
        *status = (*error)->code;
Packit 2ba279
        g_clear_error (error);
Packit 2ba279
    } else
Packit 2ba279
        *status = 0;
Packit 2ba279
Packit 2ba279
    log_out (task_id, stdout_data, stderr_data);
Packit 2ba279
    log_done (task_id, *status);
Packit 2ba279
Packit 2ba279
    g_free (args);
Packit 2ba279
Packit 2ba279
    if (*status != 0) {
Packit 2ba279
        if (stderr_data && (g_strcmp0 ("", stderr_data) != 0)) {
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
Packit 2ba279
                         "Process reported exit code %d: %s", *status, stderr_data);
Packit 2ba279
            g_free (stdout_data);
Packit 2ba279
        } else {
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
Packit 2ba279
                         "Process reported exit code %d: %s", *status, stdout_data);
Packit 2ba279
            g_free (stderr_data);
Packit 2ba279
        }
Packit 2ba279
Packit 2ba279
        return FALSE;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    g_free (stdout_data);
Packit 2ba279
    g_free (stderr_data);
Packit 2ba279
    return TRUE;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
static gboolean _utils_exec_and_report_progress (const gchar **argv, const BDExtraArg **extra, BDUtilsProgExtract prog_extract, gint *proc_status, gchar **stdout, gchar **stderr, GError **error) {
Packit 2ba279
    const gchar **args = NULL;
Packit 2ba279
    guint args_len = 0;
Packit 2ba279
    const gchar **arg_p = NULL;
Packit 2ba279
    gchar *args_str = NULL;
Packit 2ba279
    const BDExtraArg **extra_p = NULL;
Packit 2ba279
    guint64 task_id = 0;
Packit 2ba279
    guint64 progress_id = 0;
Packit 2ba279
    gchar *msg = NULL;
Packit 2ba279
    GPid pid = 0;
Packit 2ba279
    gint out_fd = 0;
Packit 2ba279
    GIOChannel *out_pipe = NULL;
Packit 2ba279
    gint err_fd = 0;
Packit 2ba279
    GIOChannel *err_pipe = NULL;
Packit 2ba279
    gchar *line = NULL;
Packit 2ba279
    gint child_ret = -1;
Packit 2ba279
    gint status = 0;
Packit 2ba279
    gboolean ret = FALSE;
Packit 2ba279
    GIOStatus io_status = G_IO_STATUS_NORMAL;
Packit 2ba279
    gint poll_status = 0;
Packit 2ba279
    guint i = 0;
Packit 2ba279
    guint8 completion = 0;
Packit 2ba279
    GPollFD fds[2] = {ZERO_INIT, ZERO_INIT};
Packit 2ba279
    gboolean out_done = FALSE;
Packit 2ba279
    gboolean err_done = FALSE;
Packit 2ba279
    GString *stdout_data = g_string_new (NULL);
Packit 2ba279
    GString *stderr_data = g_string_new (NULL);
Packit 2ba279
Packit 2ba279
    /* TODO: share this code between functions */
Packit 2ba279
    if (extra) {
Packit 2ba279
        args_len = g_strv_length ((gchar **) argv);
Packit 2ba279
        for (extra_p=extra; *extra_p; extra_p++) {
Packit 2ba279
            if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0))
Packit 2ba279
                args_len++;
Packit 2ba279
            if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0))
Packit 2ba279
                args_len++;
Packit 2ba279
        }
Packit 2ba279
        args = g_new0 (const gchar*, args_len + 1);
Packit 2ba279
        for (arg_p=argv; *arg_p; arg_p++, i++)
Packit 2ba279
            args[i] = *arg_p;
Packit 2ba279
        for (extra_p=extra; *extra_p; extra_p++) {
Packit 2ba279
            if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0)) {
Packit 2ba279
                args[i] = (*extra_p)->opt;
Packit 2ba279
                i++;
Packit 2ba279
            }
Packit 2ba279
            if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0)) {
Packit 2ba279
                args[i] = (*extra_p)->val;
Packit 2ba279
                i++;
Packit 2ba279
            }
Packit 2ba279
        }
Packit 2ba279
        args[i] = NULL;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    task_id = log_running (args ? args : argv);
Packit 2ba279
Packit 2ba279
    ret = g_spawn_async_with_pipes (NULL, args ? (gchar**) args : (gchar**) argv, NULL,
Packit 2ba279
                                    G_SPAWN_DEFAULT|G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD,
Packit 2ba279
                                    NULL, NULL, &pid, NULL, &out_fd, &err_fd, error);
Packit 2ba279
Packit 2ba279
    if (!ret) {
Packit 2ba279
        /* error is already populated */
Packit 2ba279
        g_string_free (stdout_data, TRUE);
Packit 2ba279
        g_string_free (stderr_data, TRUE);
Packit 2ba279
        g_free (args);
Packit 2ba279
        return FALSE;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    args_str = g_strjoinv (" ", args ? (gchar **) args : (gchar **) argv);
Packit 2ba279
    msg = g_strdup_printf ("Started '%s'", args_str);
Packit 2ba279
    progress_id = bd_utils_report_started (msg);
Packit 2ba279
    g_free (args_str);
Packit 2ba279
    g_free (args);
Packit 2ba279
    g_free (msg);
Packit 2ba279
Packit 2ba279
    out_pipe = g_io_channel_unix_new (out_fd);
Packit 2ba279
    err_pipe = g_io_channel_unix_new (err_fd);
Packit 2ba279
Packit 2ba279
    g_io_channel_set_encoding (out_pipe, NULL, NULL);
Packit 2ba279
    g_io_channel_set_encoding (err_pipe, NULL, NULL);
Packit 2ba279
Packit 2ba279
    fds[0].fd = out_fd;
Packit 2ba279
    fds[1].fd = err_fd;
Packit 2ba279
    fds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
Packit 2ba279
    fds[1].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
Packit 2ba279
    while (!out_done || !err_done) {
Packit 2ba279
        poll_status = g_poll (fds, 2, -1);
Packit 2ba279
        if (poll_status < 0) {
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
Packit 2ba279
                         "Failed to poll output FDs.");
Packit 2ba279
            bd_utils_report_finished (progress_id, (*error)->message);
Packit 2ba279
            g_io_channel_shutdown (out_pipe, FALSE, NULL);
Packit 2ba279
            g_io_channel_unref (out_pipe);
Packit 2ba279
            g_io_channel_shutdown (err_pipe, FALSE, NULL);
Packit 2ba279
            g_io_channel_unref (err_pipe);
Packit 2ba279
            g_string_free (stdout_data, TRUE);
Packit 2ba279
            g_string_free (stderr_data, TRUE);
Packit 2ba279
            return FALSE;
Packit 2ba279
        } else if (poll_status != 2)
Packit 2ba279
            /* both revents fields were not filled yet */
Packit 2ba279
            continue;
Packit 2ba279
        if (!(fds[0].revents & G_IO_IN))
Packit 2ba279
            out_done = TRUE;
Packit 2ba279
        while (!out_done) {
Packit 2ba279
            io_status = g_io_channel_read_line (out_pipe, &line, NULL, NULL, error);
Packit 2ba279
            if (io_status == G_IO_STATUS_NORMAL) {
Packit 2ba279
                if (prog_extract && prog_extract (line, &completion))
Packit 2ba279
                    bd_utils_report_progress (progress_id, completion, NULL);
Packit 2ba279
                else
Packit 2ba279
                    g_string_append (stdout_data, line);
Packit 2ba279
                g_free (line);
Packit 2ba279
            } else if (io_status == G_IO_STATUS_EOF) {
Packit 2ba279
                out_done = TRUE;
Packit 2ba279
            } else if (error && (*error)) {
Packit 2ba279
                bd_utils_report_finished (progress_id, (*error)->message);
Packit 2ba279
                g_io_channel_shutdown (out_pipe, FALSE, NULL);
Packit 2ba279
                g_io_channel_unref (out_pipe);
Packit 2ba279
                g_io_channel_shutdown (err_pipe, FALSE, NULL);
Packit 2ba279
                g_io_channel_unref (err_pipe);
Packit 2ba279
                g_string_free (stdout_data, TRUE);
Packit 2ba279
                g_string_free (stderr_data, TRUE);
Packit 2ba279
                return FALSE;
Packit 2ba279
            }
Packit 2ba279
        }
Packit 2ba279
        if (!(fds[1].revents & G_IO_IN))
Packit 2ba279
            err_done = TRUE;
Packit 2ba279
        while (!err_done) {
Packit 2ba279
            io_status = g_io_channel_read_line (err_pipe, &line, NULL, NULL, error);
Packit 2ba279
            if (io_status == G_IO_STATUS_NORMAL) {
Packit 2ba279
                g_string_append (stderr_data, line);
Packit 2ba279
                g_free (line);
Packit 2ba279
            } else if (io_status == G_IO_STATUS_EOF) {
Packit 2ba279
                err_done = TRUE;
Packit 2ba279
            } else if (error && (*error)) {
Packit 2ba279
                bd_utils_report_finished (progress_id, (*error)->message);
Packit 2ba279
                g_io_channel_shutdown (out_pipe, FALSE, NULL);
Packit 2ba279
                g_io_channel_unref (out_pipe);
Packit 2ba279
                g_io_channel_shutdown (err_pipe, FALSE, NULL);
Packit 2ba279
                g_io_channel_unref (err_pipe);
Packit 2ba279
                g_string_free (stdout_data, TRUE);
Packit 2ba279
                g_string_free (stderr_data, TRUE);
Packit 2ba279
                return FALSE;
Packit 2ba279
            }
Packit 2ba279
        }
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    child_ret = waitpid (pid, &status, 0);
Packit 2ba279
    *proc_status = WEXITSTATUS(status);
Packit 2ba279
    if (child_ret > 0) {
Packit 2ba279
        if (*proc_status != 0) {
Packit 2ba279
            if (stderr_data->str && (g_strcmp0 ("", stderr_data->str) != 0))
Packit 2ba279
                msg = stderr_data->str;
Packit 2ba279
            else
Packit 2ba279
                msg = stdout_data->str;
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
Packit 2ba279
                         "Process reported exit code %d: %s", *proc_status, msg);
Packit 2ba279
            bd_utils_report_finished (progress_id, (*error)->message);
Packit 2ba279
            g_io_channel_shutdown (out_pipe, FALSE, NULL);
Packit 2ba279
            g_io_channel_unref (out_pipe);
Packit 2ba279
            g_io_channel_shutdown (err_pipe, FALSE, NULL);
Packit 2ba279
            g_io_channel_unref (err_pipe);
Packit 2ba279
            g_string_free (stdout_data, TRUE);
Packit 2ba279
            g_string_free (stderr_data, TRUE);
Packit 2ba279
            return FALSE;
Packit 2ba279
        }
Packit 2ba279
        if (WIFSIGNALED(status)) {
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
Packit 2ba279
                         "Process killed with a signal");
Packit 2ba279
            bd_utils_report_finished (progress_id, (*error)->message);
Packit 2ba279
            g_io_channel_shutdown (out_pipe, FALSE, NULL);
Packit 2ba279
            g_io_channel_unref (out_pipe);
Packit 2ba279
            g_io_channel_shutdown (err_pipe, FALSE, NULL);
Packit 2ba279
            g_io_channel_unref (err_pipe);
Packit 2ba279
            g_string_free (stdout_data, TRUE);
Packit 2ba279
            g_string_free (stderr_data, TRUE);
Packit 2ba279
            return FALSE;
Packit 2ba279
        }
Packit 2ba279
    } else if (child_ret == -1) {
Packit 2ba279
        if (errno != ECHILD) {
Packit 2ba279
            errno = 0;
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
Packit 2ba279
                         "Failed to wait for the process");
Packit 2ba279
            bd_utils_report_finished (progress_id, (*error)->message);
Packit 2ba279
            g_io_channel_shutdown (out_pipe, FALSE, NULL);
Packit 2ba279
            g_io_channel_unref (out_pipe);
Packit 2ba279
            g_io_channel_shutdown (err_pipe, FALSE, NULL);
Packit 2ba279
            g_io_channel_unref (err_pipe);
Packit 2ba279
            g_string_free (stdout_data, TRUE);
Packit 2ba279
            g_string_free (stderr_data, TRUE);
Packit 2ba279
            return FALSE;
Packit 2ba279
        }
Packit 2ba279
        /* no such process (the child exited before we tried to wait for it) */
Packit 2ba279
        errno = 0;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    bd_utils_report_finished (progress_id, "Completed");
Packit 2ba279
    log_out (task_id, stdout_data->str, stderr_data->str);
Packit 2ba279
    log_done (task_id, *proc_status);
Packit 2ba279
Packit 2ba279
    /* we don't care about the status here */
Packit 2ba279
    g_io_channel_shutdown (out_pipe, FALSE, error);
Packit 2ba279
    g_io_channel_unref (out_pipe);
Packit 2ba279
    g_io_channel_shutdown (err_pipe, FALSE, error);
Packit 2ba279
    g_io_channel_unref (err_pipe);
Packit 2ba279
Packit 2ba279
    if (stdout)
Packit 2ba279
        *stdout = g_string_free (stdout_data, FALSE);
Packit 2ba279
    else
Packit 2ba279
        g_string_free (stdout_data, TRUE);
Packit 2ba279
    if (stderr)
Packit 2ba279
        *stderr = g_string_free (stderr_data, FALSE);
Packit 2ba279
    else
Packit 2ba279
        g_string_free (stderr_data, TRUE);
Packit 2ba279
Packit 2ba279
    return TRUE;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_exec_and_report_progress:
Packit 2ba279
 * @argv: (array zero-terminated=1): the argv array for the call
Packit 2ba279
 * @extra: (allow-none) (array zero-terminated=1): extra arguments
Packit 2ba279
 * @prog_extract: (scope notified): function for extracting progress information
Packit 2ba279
 * @proc_status: (out): place to store the process exit status
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether the @argv was successfully executed (no error and exit code 0) or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_exec_and_report_progress (const gchar **argv, const BDExtraArg **extra, BDUtilsProgExtract prog_extract, gint *proc_status, GError **error) {
Packit 2ba279
    return _utils_exec_and_report_progress (argv, extra, prog_extract, proc_status, NULL, NULL, error);
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_exec_and_capture_output:
Packit 2ba279
 * @argv: (array zero-terminated=1): the argv array for the call
Packit 2ba279
 * @extra: (allow-none) (array zero-terminated=1): extra arguments
Packit 2ba279
 * @output: (out): variable to store output to
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether the @argv was successfully executed capturing the output or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_exec_and_capture_output (const gchar **argv, const BDExtraArg **extra, gchar **output, GError **error) {
Packit 2ba279
    gint status = 0;
Packit 2ba279
    gchar *stdout = NULL;
Packit 2ba279
    gchar *stderr = NULL;
Packit 2ba279
    gboolean ret = FALSE;
Packit 2ba279
Packit 2ba279
    ret = _utils_exec_and_report_progress (argv, extra, NULL, &status, &stdout, &stderr, error);
Packit 2ba279
    if (!ret)
Packit 2ba279
        return ret;
Packit 2ba279
Packit 2ba279
    if ((status != 0) || (g_strcmp0 ("", stdout) == 0)) {
Packit 2ba279
        if (status != 0)
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
Packit 2ba279
                         "Process reported exit code %d: %s%s", status,
Packit 2ba279
                         stdout ? stdout : "",
Packit 2ba279
                         stderr ? stderr : "");
Packit 2ba279
        else
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT,
Packit 2ba279
                         "Process didn't provide any data on standard output. "
Packit 2ba279
                         "Error output: %s", stderr ? stderr : "");
Packit 2ba279
        g_free (stderr);
Packit 2ba279
        g_free (stdout);
Packit 2ba279
        return FALSE;
Packit 2ba279
    } else {
Packit 2ba279
        *output = stdout;
Packit 2ba279
        g_free (stderr);
Packit 2ba279
        return TRUE;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_init_logging:
Packit 2ba279
 * @new_log_func: (allow-none) (scope notified): logging function to use or
Packit 2ba279
 *                                               %NULL to reset to default
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether logging was successfully initialized or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_init_logging (BDUtilsLogFunc new_log_func, GError **error __attribute__((unused))) {
Packit 2ba279
    /* XXX: the error attribute will likely be used in the future when this
Packit 2ba279
       function gets more complicated */
Packit 2ba279
Packit 2ba279
    log_func = new_log_func;
Packit 2ba279
Packit 2ba279
    return TRUE;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_version_cmp:
Packit 2ba279
 * @ver_string1: first version string
Packit 2ba279
 * @ver_string2: second version string
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: -1, 0 or 1 if @ver_string1 is lower, the same or higher version as
Packit 2ba279
 *          @ver_string2 respectively. If an error occurs, returns -2 and @error
Packit 2ba279
 *          is set.
Packit 2ba279
 *
Packit 2ba279
 * **ONLY SUPPORTS VERSION STRINGS OF FORMAT X[.Y[.Z[.Z2[.Z3...[-R]]]]] where all components
Packit 2ba279
 *   are natural numbers!**
Packit 2ba279
 */
Packit 2ba279
gint bd_utils_version_cmp (const gchar *ver_string1, const gchar *ver_string2, GError **error) {
Packit 2ba279
    gchar **v1_fields = NULL;
Packit 2ba279
    gchar **v2_fields = NULL;
Packit 2ba279
    guint v1_fields_len = 0;
Packit 2ba279
    guint v2_fields_len = 0;
Packit 2ba279
    guint64 v1_value = 0;
Packit 2ba279
    guint64 v2_value = 0;
Packit 2ba279
    GRegex *regex = NULL;
Packit 2ba279
    gboolean success = FALSE;
Packit 2ba279
    gint ret = -2;
Packit 2ba279
Packit 2ba279
    regex = g_regex_new ("^(\\d+)(\\.\\d+)*(-\\d)?$", 0, 0, error);
Packit 2ba279
    if (!regex) {
Packit 2ba279
        /* error is already populated */
Packit 2ba279
        return -2;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    success = g_regex_match (regex, ver_string1, 0, NULL);
Packit 2ba279
    if (!success) {
Packit 2ba279
        g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_INVAL_VER,
Packit 2ba279
                     "Invalid or unsupported version (1) format: %s", ver_string1);
Packit 2ba279
        return -2;
Packit 2ba279
    }
Packit 2ba279
    success = g_regex_match (regex, ver_string2, 0, NULL);
Packit 2ba279
    if (!success) {
Packit 2ba279
        g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_INVAL_VER,
Packit 2ba279
                     "Invalid or unsupported version (2) format: %s", ver_string2);
Packit 2ba279
        return -2;
Packit 2ba279
    }
Packit 2ba279
    g_regex_unref (regex);
Packit 2ba279
Packit 2ba279
    v1_fields = g_strsplit_set (ver_string1, ".-", 0);
Packit 2ba279
    v2_fields = g_strsplit_set (ver_string2, ".-", 0);
Packit 2ba279
    v1_fields_len = g_strv_length (v1_fields);
Packit 2ba279
    v2_fields_len = g_strv_length (v2_fields);
Packit 2ba279
Packit 2ba279
    for (guint i=0; (i < v1_fields_len) && (i < v2_fields_len) && ret == -2; i++) {
Packit 2ba279
        v1_value = g_ascii_strtoull (v1_fields[i], NULL, 0);
Packit 2ba279
        v2_value = g_ascii_strtoull (v2_fields[i], NULL, 0);
Packit 2ba279
        if (v1_value < v2_value)
Packit 2ba279
            ret = -1;
Packit 2ba279
        else if (v1_value > v2_value)
Packit 2ba279
            ret = 1;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    if (ret == -2) {
Packit 2ba279
        if (v1_fields_len < v2_fields_len)
Packit 2ba279
            ret = -1;
Packit 2ba279
        else if (v1_fields_len > v2_fields_len)
Packit 2ba279
            ret = 1;
Packit 2ba279
        else
Packit 2ba279
            ret = 0;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    g_strfreev (v1_fields);
Packit 2ba279
    g_strfreev (v2_fields);
Packit 2ba279
Packit 2ba279
    return ret;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_check_util_version:
Packit 2ba279
 * @util: name of the utility to check
Packit 2ba279
 * @version: (allow-none): minimum required version of the utility or %NULL
Packit 2ba279
 *           if no version is required
Packit 2ba279
 * @version_arg: (allow-none): argument to use with the @util to get version
Packit 2ba279
 *               info or %NULL to use "--version"
Packit 2ba279
 * @version_regexp: (allow-none): regexp to extract version from the version
Packit 2ba279
 *                  info or %NULL if only version is printed by "$ @util @version_arg"
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether the @util is available in a version >= @version or not
Packit 2ba279
 *          (@error is set in such case).
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_check_util_version (const gchar *util, const gchar *version, const gchar *version_arg, const gchar *version_regexp, GError **error) {
Packit 2ba279
    gchar *util_path = NULL;
Packit 2ba279
    const gchar *argv[] = {util, version_arg ? version_arg : "--version", NULL};
Packit 2ba279
    gchar *output = NULL;
Packit 2ba279
    gboolean succ = FALSE;
Packit 2ba279
    GRegex *regex = NULL;
Packit 2ba279
    GMatchInfo *match_info = NULL;
Packit 2ba279
    gchar *version_str = NULL;
Packit 2ba279
Packit 2ba279
    util_path = g_find_program_in_path (util);
Packit 2ba279
    if (!util_path) {
Packit 2ba279
        g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_UNAVAILABLE,
Packit 2ba279
                     "The '%s' utility is not available", util);
Packit 2ba279
        return FALSE;
Packit 2ba279
    }
Packit 2ba279
    g_free (util_path);
Packit 2ba279
Packit 2ba279
    if (!version)
Packit 2ba279
        /* nothing more to do here */
Packit 2ba279
        return TRUE;
Packit 2ba279
Packit 2ba279
    succ = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
Packit 2ba279
    if (!succ) {
Packit 2ba279
        /* if we got nothing on STDOUT, try using STDERR data from error message */
Packit 2ba279
        if (g_error_matches ((*error), BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
Packit 2ba279
            output = g_strdup ((*error)->message);
Packit 2ba279
            g_clear_error (error);
Packit 2ba279
        } else if (g_error_matches ((*error), BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED)) {
Packit 2ba279
            /* exit status != 0, try using the output anyway */
Packit 2ba279
            output = g_strdup ((*error)->message);
Packit 2ba279
            g_clear_error (error);
Packit 2ba279
        }
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    if (version_regexp) {
Packit 2ba279
        regex = g_regex_new (version_regexp, 0, 0, error);
Packit 2ba279
        if (!regex) {
Packit 2ba279
            g_free (output);
Packit 2ba279
            /* error is already populated */
Packit 2ba279
            return FALSE;
Packit 2ba279
        }
Packit 2ba279
Packit 2ba279
        succ = g_regex_match (regex, output, 0, &match_info);
Packit 2ba279
        if (!succ) {
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_UNKNOWN_VER,
Packit 2ba279
                         "Failed to determine %s's version from: %s", util, output);
Packit 2ba279
            g_free (output);
Packit 2ba279
            g_regex_unref (regex);
Packit 2ba279
            g_match_info_free (match_info);
Packit 2ba279
            return FALSE;
Packit 2ba279
        }
Packit 2ba279
        g_regex_unref (regex);
Packit 2ba279
Packit 2ba279
        version_str = g_match_info_fetch (match_info, 1);
Packit 2ba279
        g_match_info_free (match_info);
Packit 2ba279
    }
Packit 2ba279
    else
Packit 2ba279
        version_str = g_strstrip (g_strdup (output));
Packit 2ba279
Packit 2ba279
    if (!version_str || (g_strcmp0 (version_str, "") == 0)) {
Packit 2ba279
        g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_UNKNOWN_VER,
Packit 2ba279
                     "Failed to determine %s's version from: %s", util, output);
Packit 2ba279
        g_free (version_str);
Packit 2ba279
        g_free (output);
Packit 2ba279
        return FALSE;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    g_free (output);
Packit 2ba279
Packit 2ba279
    if (bd_utils_version_cmp (version_str, version, error) < 0) {
Packit 2ba279
        /* smaller version or error */
Packit 2ba279
        if (!(*error))
Packit 2ba279
            g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_LOW_VER,
Packit 2ba279
                         "Too low version of %s: %s. At least %s required.",
Packit 2ba279
                         util, version_str, version);
Packit 2ba279
        g_free (version_str);
Packit 2ba279
        return FALSE;
Packit 2ba279
    }
Packit 2ba279
Packit 2ba279
    g_free (version_str);
Packit 2ba279
    return TRUE;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_init_prog_reporting:
Packit 2ba279
 * @new_prog_func: (allow-none) (scope notified): progress reporting function to
Packit 2ba279
 *                                                use or %NULL to reset to default
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether progress reporting was successfully initialized or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_init_prog_reporting (BDUtilsProgFunc new_prog_func, GError **error __attribute__((unused))) {
Packit 2ba279
    /* XXX: the error attribute will likely be used in the future when this
Packit 2ba279
       function gets more complicated */
Packit 2ba279
Packit 2ba279
    prog_func = new_prog_func;
Packit 2ba279
Packit 2ba279
    return TRUE;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_init_prog_reporting_thread:
Packit 2ba279
 * @new_prog_func: (allow-none) (scope notified): progress reporting function to
Packit 2ba279
 *                                                use on current thread or %NULL
Packit 2ba279
 *                                                to reset to default or global
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether progress reporting was successfully initialized or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_init_prog_reporting_thread (BDUtilsProgFunc new_prog_func, GError **error __attribute__((unused))) {
Packit 2ba279
    /* XXX: the error attribute will likely be used in the future when this
Packit 2ba279
       function gets more complicated */
Packit 2ba279
Packit 2ba279
    thread_prog_func = new_prog_func;
Packit 2ba279
Packit 2ba279
    return TRUE;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
static void thread_progress_muted (guint64 task_id __attribute__((unused)), BDUtilsProgStatus status __attribute__((unused)), guint8 completion __attribute__((unused)), gchar *msg __attribute__((unused))) {
Packit 2ba279
    /* This function serves as a special value for the progress reporting
Packit 2ba279
     * function to detect that nothing is done here. If clients use their own
Packit 2ba279
     * empty function then bd_utils_prog_reporting_initialized will return TRUE
Packit 2ba279
     * but with this function here it returns FALSE.
Packit 2ba279
     */
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_mute_prog_reporting_thread:
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether progress reporting for the current thread was successfully
Packit 2ba279
 * muted (deinitialized even in presence of a global reporting function) or not
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_mute_prog_reporting_thread (GError **error __attribute__((unused))) {
Packit 2ba279
    /* XXX: the error attribute will likely be used in the future when this
Packit 2ba279
       function gets more complicated */
Packit 2ba279
Packit 2ba279
    thread_prog_func = thread_progress_muted;
Packit 2ba279
Packit 2ba279
    return TRUE;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_prog_reporting_initialized:
Packit 2ba279
 *
Packit 2ba279
 * Returns: TRUE if progress reporting has been initialized, i.e. a reporting
Packit 2ba279
 * function was set up with either bd_utils_init_prog_reporting or
Packit 2ba279
 * bd_utils_init_prog_reporting_thread (takes precedence). FALSE if
Packit 2ba279
 * bd_utils_mute_prog_reporting_thread was used to mute the thread.
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_prog_reporting_initialized (void) {
Packit 2ba279
    return (thread_prog_func != NULL || prog_func != NULL) && thread_prog_func != thread_progress_muted;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_report_started:
Packit 2ba279
 * @msg: message describing the started task/action
Packit 2ba279
 *
Packit 2ba279
 * Returns: ID of the started task/action
Packit 2ba279
 */
Packit 2ba279
guint64 bd_utils_report_started (const gchar *msg) {
Packit 2ba279
    guint64 task_id = 0;
Packit 2ba279
    BDUtilsProgFunc current_prog_func;
Packit 2ba279
Packit 2ba279
    current_prog_func = thread_prog_func != NULL ? thread_prog_func : prog_func;
Packit 2ba279
Packit 2ba279
    g_mutex_lock (&task_id_counter_lock);
Packit 2ba279
    task_id_counter++;
Packit 2ba279
    task_id = task_id_counter;
Packit 2ba279
    g_mutex_unlock (&task_id_counter_lock);
Packit 2ba279
Packit 2ba279
    if (current_prog_func)
Packit 2ba279
        current_prog_func (task_id, BD_UTILS_PROG_STARTED, 0, (gchar *)msg);
Packit 2ba279
    return task_id;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_report_progress:
Packit 2ba279
 * @task_id: ID of the task/action
Packit 2ba279
 * @completion: percentage of completion
Packit 2ba279
 * @msg: message describing the status of the task/action
Packit 2ba279
 */
Packit 2ba279
void bd_utils_report_progress (guint64 task_id, guint64 completion, const gchar *msg) {
Packit 2ba279
    BDUtilsProgFunc current_prog_func;
Packit 2ba279
Packit 2ba279
    current_prog_func = thread_prog_func != NULL ? thread_prog_func : prog_func;
Packit 2ba279
    if (current_prog_func)
Packit 2ba279
        current_prog_func (task_id, BD_UTILS_PROG_PROGRESS, completion, (gchar *)msg);
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_report_finished:
Packit 2ba279
 * @task_id: ID of the task/action
Packit 2ba279
 * @msg: message describing the status of the task/action
Packit 2ba279
 */
Packit 2ba279
void bd_utils_report_finished (guint64 task_id, const gchar *msg) {
Packit 2ba279
    BDUtilsProgFunc current_prog_func;
Packit 2ba279
Packit 2ba279
    current_prog_func = thread_prog_func != NULL ? thread_prog_func : prog_func;
Packit 2ba279
    if (current_prog_func)
Packit 2ba279
        current_prog_func (task_id, BD_UTILS_PROG_FINISHED, 100, (gchar *)msg);
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_echo_str_to_file:
Packit 2ba279
 * @str: string to write to @file_path
Packit 2ba279
 * @file_path: path to file
Packit 2ba279
 * @error: (out): place to store error (if any)
Packit 2ba279
 *
Packit 2ba279
 * Returns: whether the @str was successfully written to @file_path
Packit 2ba279
 * or not.
Packit 2ba279
 */
Packit 2ba279
gboolean bd_utils_echo_str_to_file (const gchar *str, const gchar *file_path, GError **error) {
Packit 2ba279
    GIOChannel *out_file = NULL;
Packit 2ba279
    gsize bytes_written = 0;
Packit 2ba279
Packit 2ba279
    out_file = g_io_channel_new_file (file_path, "w", error);
Packit 2ba279
    if (!out_file || g_io_channel_write_chars (out_file, str, -1, &bytes_written, error) != G_IO_STATUS_NORMAL) {
Packit 2ba279
        g_prefix_error (error, "Failed to write '%s' to file '%s': ", str, file_path);
Packit 2ba279
        return FALSE;
Packit 2ba279
    }
Packit 2ba279
    if (g_io_channel_shutdown (out_file, TRUE, error) != G_IO_STATUS_NORMAL) {
Packit 2ba279
        g_prefix_error (error, "Failed to flush and close the file '%s': ", file_path);
Packit 2ba279
        g_io_channel_unref (out_file);
Packit 2ba279
        return FALSE;
Packit 2ba279
    }
Packit 2ba279
    g_io_channel_unref (out_file);
Packit 2ba279
    return TRUE;
Packit 2ba279
}
Packit 2ba279
Packit 2ba279
/**
Packit 2ba279
 * bd_utils_log:
Packit 2ba279
 * @level: log level
Packit 2ba279
 * @msg: log message
Packit 2ba279
 */
Packit 2ba279
void bd_utils_log (gint level, const gchar *msg) {
Packit 2ba279
    if (log_func)
Packit 2ba279
        log_func (level, msg);
Packit 2ba279
}