|
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 |
}
|