#include "config.h"
#include <locale.h>
#include <glib.h>
#include <glib-unix.h>
#include <glib/gi18n.h>
#include <packagekit-glib2/pk-debug.h>
#include <packagekit-glib2/pk-error.h>
#include <packagekit-glib2/pk-item-progress.h>
#include <packagekit-glib2/pk-package.h>
#include "pk-backend.h"
#include "pk-shared.h"
#define PK_ERROR 1
#define PK_ERROR_INVALID_ARGUMENTS 0
#define PK_ERROR_NO_SUCH_CMD 1
typedef struct {
GMainLoop *loop;
GOptionContext *context;
GPtrArray *cmd_array;
PkBackend *backend;
PkBackendJob *job;
gboolean value_only;
} PkDirectPrivate;
typedef gboolean (*PkDirectCommandCb) (PkDirectPrivate *util,
gchar **values,
GError **error);
typedef struct {
gchar *name;
gchar *arguments;
gchar *description;
PkDirectCommandCb callback;
} PkDirectItem;
static void
pk_direct_item_free (PkDirectItem *item)
{
g_free (item->name);
g_free (item->arguments);
g_free (item->description);
g_free (item);
}
static gint
pk_sort_command_name_cb (PkDirectItem **item1, PkDirectItem **item2)
{
return g_strcmp0 ((*item1)->name, (*item2)->name);
}
static void
pk_direct_add (GPtrArray *array,
const gchar *name,
const gchar *arguments,
const gchar *description,
PkDirectCommandCb callback)
{
PkDirectItem *item;
guint i;
g_auto(GStrv) names = NULL;
g_return_if_fail (name != NULL);
g_return_if_fail (description != NULL);
g_return_if_fail (callback != NULL);
names = g_strsplit (name, ",", -1);
for (i = 0; names[i] != NULL; i++) {
item = g_new0 (PkDirectItem, 1);
item->name = g_strdup (names[i]);
if (i == 0) {
item->description = g_strdup (description);
} else {
item->description = g_strdup_printf (_("Alias to %s"),
names[0]);
}
item->arguments = g_strdup (arguments);
item->callback = callback;
g_ptr_array_add (array, item);
}
}
static gchar *
pk_direct_get_descriptions (GPtrArray *array)
{
guint i;
guint j;
guint len;
const guint max_len = 35;
PkDirectItem *item;
GString *string;
string = g_string_new ("");
for (i = 0; i < array->len; i++) {
item = g_ptr_array_index (array, i);
g_string_append (string, " ");
g_string_append (string, item->name);
len = strlen (item->name) + 2;
if (item->arguments != NULL) {
g_string_append (string, " ");
g_string_append (string, item->arguments);
len += strlen (item->arguments) + 1;
}
if (len < max_len) {
for (j = len; j < max_len + 1; j++)
g_string_append_c (string, ' ');
g_string_append (string, item->description);
g_string_append_c (string, '\n');
} else {
g_string_append_c (string, '\n');
for (j = 0; j < max_len + 1; j++)
g_string_append_c (string, ' ');
g_string_append (string, item->description);
g_string_append_c (string, '\n');
}
}
if (string->len > 0)
g_string_set_size (string, string->len - 1);
return g_string_free (string, FALSE);
}
static gboolean
pk_direct_run (PkDirectPrivate *priv, const gchar *command, gchar **values, GError **error)
{
PkDirectItem *item;
guint i;
g_autoptr(GString) string = NULL;
for (i = 0; i < priv->cmd_array->len; i++) {
item = g_ptr_array_index (priv->cmd_array, i);
if (g_strcmp0 (item->name, command) == 0)
return item->callback (priv, values, error);
}
string = g_string_new ("");
g_string_append_printf (string, "%s\n",
_("Command not found, valid commands are:"));
for (i = 0; i < priv->cmd_array->len; i++) {
item = g_ptr_array_index (priv->cmd_array, i);
g_string_append_printf (string, " * %s %s\n",
item->name,
item->arguments ? item->arguments : "");
}
g_set_error_literal (error, PK_ERROR, PK_ERROR_NO_SUCH_CMD, string->str);
return FALSE;
}
static gboolean
pk_direct_refresh (PkDirectPrivate *priv, gchar **values, GError **error)
{
pk_backend_start_job (priv->backend, priv->job);
pk_backend_refresh_cache (priv->backend, priv->job, FALSE);
g_main_loop_run (priv->loop);
pk_backend_stop_job (priv->backend, priv->job);
return TRUE;
}
static gboolean
pk_direct_refresh_force (PkDirectPrivate *priv, gchar **values, GError **error)
{
pk_backend_start_job (priv->backend, priv->job);
pk_backend_refresh_cache (priv->backend, priv->job, TRUE);
g_main_loop_run (priv->loop);
pk_backend_stop_job (priv->backend, priv->job);
return TRUE;
}
static gboolean
pk_direct_search_names (PkDirectPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) < 1) {
g_set_error_literal (error,
PK_ERROR,
PK_ERROR_INVALID_ARGUMENTS,
"Not enough arguments, expected: <search>");
return FALSE;
}
pk_backend_start_job (priv->backend, priv->job);
pk_backend_search_names (priv->backend, priv->job, 0, values);
g_main_loop_run (priv->loop);
pk_backend_stop_job (priv->backend, priv->job);
return TRUE;
}
static gboolean
pk_direct_search_details (PkDirectPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) < 1) {
g_set_error_literal (error,
PK_ERROR,
PK_ERROR_INVALID_ARGUMENTS,
"Not enough arguments, expected: <search>");
return FALSE;
}
pk_backend_start_job (priv->backend, priv->job);
pk_backend_search_details (priv->backend, priv->job, 0, values);
g_main_loop_run (priv->loop);
pk_backend_stop_job (priv->backend, priv->job);
return TRUE;
}
static gboolean
pk_direct_search_files (PkDirectPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) < 1) {
g_set_error_literal (error,
PK_ERROR,
PK_ERROR_INVALID_ARGUMENTS,
"Not enough arguments, expected: <search>");
return FALSE;
}
pk_backend_start_job (priv->backend, priv->job);
pk_backend_search_files (priv->backend, priv->job, 0, values);
g_main_loop_run (priv->loop);
pk_backend_stop_job (priv->backend, priv->job);
return TRUE;
}
static gboolean
pk_direct_install (PkDirectPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) < 1) {
g_set_error_literal (error,
PK_ERROR,
PK_ERROR_INVALID_ARGUMENTS,
"Not enough arguments, expected: <pkgid>");
return FALSE;
}
if (!pk_package_id_check (values[0])) {
g_set_error (error,
PK_ERROR,
PK_ERROR_INVALID_ARGUMENTS,
"Not a package-id: %s", values[0]);
return FALSE;
}
pk_backend_start_job (priv->backend, priv->job);
pk_backend_install_packages (priv->backend, priv->job, 0, values);
g_main_loop_run (priv->loop);
pk_backend_stop_job (priv->backend, priv->job);
return TRUE;
}
static gboolean
pk_direct_remove (PkDirectPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) < 1) {
g_set_error_literal (error,
PK_ERROR,
PK_ERROR_INVALID_ARGUMENTS,
"Not enough arguments, expected: <pkgid>");
return FALSE;
}
if (!pk_package_id_check (values[0])) {
g_set_error (error,
PK_ERROR,
PK_ERROR_INVALID_ARGUMENTS,
"Not a package-id: %s", values[0]);
return FALSE;
}
pk_backend_start_job (priv->backend, priv->job);
pk_backend_remove_packages (priv->backend, priv->job, 0, values, FALSE, FALSE);
g_main_loop_run (priv->loop);
pk_backend_stop_job (priv->backend, priv->job);
return TRUE;
}
static gboolean
pk_direct_repo_set_data (PkDirectPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) != 3) {
g_set_error_literal (error,
PK_ERROR,
PK_ERROR_INVALID_ARGUMENTS,
"Not enough arguments, "
"expected: [id] [key] [value]");
return FALSE;
}
pk_backend_start_job (priv->backend, priv->job);
pk_backend_repo_set_data (priv->backend, priv->job,
values[0], values[1], values[2]);
g_main_loop_run (priv->loop);
pk_backend_stop_job (priv->backend, priv->job);
return TRUE;
}
static gboolean
pk_direct_sigint_cb (gpointer user_data)
{
PkDirectPrivate *priv = (PkDirectPrivate *) user_data;
g_debug ("Handling SIGINT");
g_main_loop_quit (priv->loop);
return FALSE;
}
static void
pk_direct_finished_cb (PkBackendJob *job, gpointer object, gpointer user_data)
{
PkExitEnum exit_enum = GPOINTER_TO_UINT (object);
PkDirectPrivate *priv = (PkDirectPrivate *) user_data;
g_print ("Exit code: %s\n", pk_exit_enum_to_string (exit_enum));
g_main_loop_quit (priv->loop);
}
static void
pk_direct_percentage_cb (PkBackendJob *job, gpointer object, gpointer user_data)
{
guint percentage = GPOINTER_TO_UINT (object);
g_print ("Done: %i%%\n", percentage);
}
static void
pk_direct_status_changed_cb (PkBackendJob *job, gpointer object, gpointer user_data)
{
PkStatusEnum status_enum = GPOINTER_TO_UINT (object);
g_print ("Status: %s\n", pk_status_enum_to_string (status_enum));
}
static void
pk_direct_package_cb (PkBackendJob *job, gpointer object, gpointer user_data)
{
PkPackage *pkg = PK_PACKAGE (object);
g_print ("Package: %s\t%s\n",
pk_info_enum_to_string (pk_package_get_info (pkg)),
pk_package_get_id (pkg));
}
static void
pk_direct_packages_cb (PkBackendJob *job, gpointer object, gpointer user_data)
{
GPtrArray *package_array = object;
for (guint i = 0; i < package_array->len; i++) {
PkPackage *pkg = g_ptr_array_index (package_array, i);
g_print ("Package: %s\t%s\n",
pk_info_enum_to_string (pk_package_get_info (pkg)),
pk_package_get_id (pkg));
}
}
static void
pk_direct_error_cb (PkBackendJob *job, gpointer object, gpointer user_data)
{
PkError *err = PK_ERROR_CODE (object);
g_print ("Error: %s\t%s\n",
pk_error_enum_to_string (pk_error_get_code (err)),
pk_error_get_details (err));
}
static void
pk_direct_item_progress_cb (PkBackendJob *job, gpointer object, gpointer user_data)
{
PkItemProgress *ip = PK_ITEM_PROGRESS (object);
g_print ("ItemProgress: %s\t%i%%\t%s\n",
pk_status_enum_to_string (pk_item_progress_get_status (ip)),
pk_item_progress_get_percentage (ip),
pk_item_progress_get_package_id (ip));
}
int
main (int argc, char *argv[])
{
PkDirectPrivate *priv = NULL;
const gchar *destdir;
gboolean ret = TRUE;
gint retval = EXIT_SUCCESS;
g_autoptr(GError) error = NULL;
g_autofree gchar *backend_name = NULL;
g_autofree gchar *cmd_descriptions = NULL;
g_autofree gchar *conf_filename = NULL;
g_autoptr(GKeyFile) conf = NULL;
const GOptionEntry options[] = {
{ "backend", '\0', 0, G_OPTION_ARG_STRING, &backend_name,
_("Packaging backend to use, e.g. dummy"), NULL },
{ NULL }
};
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
priv = g_new0 (PkDirectPrivate, 1);
priv->cmd_array = g_ptr_array_new_with_free_func ((GDestroyNotify) pk_direct_item_free);
pk_direct_add (priv->cmd_array, "refresh", NULL,
_("Refresh the cache"),
pk_direct_refresh);
pk_direct_add (priv->cmd_array, "refresh-force", NULL,
_("Refresh the cache (forced)"),
pk_direct_refresh_force);
pk_direct_add (priv->cmd_array, "search-name", "[SEARCH]",
_("Search by names"),
pk_direct_search_names);
pk_direct_add (priv->cmd_array, "search-detail", "[SEARCH]",
_("Search by details"),
pk_direct_search_details);
pk_direct_add (priv->cmd_array, "search-file", "[SEARCH]",
_("Search by files"),
pk_direct_search_files);
pk_direct_add (priv->cmd_array, "install", "[PKGID]",
_("Install package"),
pk_direct_install);
pk_direct_add (priv->cmd_array, "remove", "[PKGID]",
_("Remove package"),
pk_direct_remove);
pk_direct_add (priv->cmd_array, "repo-set-data", "[REPO] [KEY] [VALUE]",
_("Set repository options"),
pk_direct_repo_set_data);
g_ptr_array_sort (priv->cmd_array,
(GCompareFunc) pk_sort_command_name_cb);
priv->context = g_option_context_new (NULL);
cmd_descriptions = pk_direct_get_descriptions (priv->cmd_array);
g_option_context_set_summary (priv->context, cmd_descriptions);
g_set_application_name (_("PackageKit"));
g_option_context_add_main_entries (priv->context, options, NULL);
g_option_context_add_group (priv->context, pk_debug_get_option_group ());
ret = g_option_context_parse (priv->context, &argc, &argv, &error);
if (!ret) {
g_print ("%s: %s\n", _("Failed to parse arguments"), error->message);
goto out;
}
conf = g_key_file_new ();
conf_filename = pk_util_get_config_filename ();
ret = g_key_file_load_from_file (conf, conf_filename,
G_KEY_FILE_NONE, &error);
if (!ret) {
g_print ("%s: %s\n", _("Failed to load the config file"), error->message);
retval = EXIT_FAILURE;
goto out;
}
destdir = g_getenv ("DESTDIR");
if (destdir != NULL)
g_key_file_set_string (conf, "Daemon", "DestDir", destdir);
if (backend_name != NULL)
g_key_file_set_string (conf, "Daemon", "DefaultBackend", backend_name);
backend_name = g_key_file_get_string (conf, "Daemon", "DefaultBackend", NULL);
if (backend_name == NULL || g_strcmp0 (backend_name, "auto") == 0) {
if (!pk_util_set_auto_backend (conf, &error)) {
g_print ("Failed to resolve auto: %s\n", error->message);
retval = EXIT_FAILURE;
goto out;
}
}
priv->loop = g_main_loop_new (NULL, FALSE);
g_unix_signal_add_full (G_PRIORITY_DEFAULT, SIGINT,
pk_direct_sigint_cb, &priv, NULL);
priv->backend = pk_backend_new (conf);
if (!pk_backend_load (priv->backend, &error)) {
g_print ("%s: %s\n", _("Failed to load the backend"), error->message);
retval = EXIT_FAILURE;
goto out;
}
priv->job = pk_backend_job_new (conf);
pk_backend_job_set_cache_age (priv->job, G_MAXUINT);
pk_backend_job_set_backend (priv->job, priv->backend);
pk_backend_job_set_vfunc (priv->job, PK_BACKEND_SIGNAL_FINISHED,
pk_direct_finished_cb, priv);
pk_backend_job_set_vfunc (priv->job, PK_BACKEND_SIGNAL_PERCENTAGE,
pk_direct_percentage_cb, priv);
pk_backend_job_set_vfunc (priv->job, PK_BACKEND_SIGNAL_STATUS_CHANGED,
pk_direct_status_changed_cb, priv);
pk_backend_job_set_vfunc (priv->job, PK_BACKEND_SIGNAL_PACKAGE,
pk_direct_package_cb, priv);
pk_backend_job_set_vfunc (priv->job, PK_BACKEND_SIGNAL_PACKAGES,
pk_direct_packages_cb, priv);
pk_backend_job_set_vfunc (priv->job, PK_BACKEND_SIGNAL_ERROR_CODE,
pk_direct_error_cb, priv);
pk_backend_job_set_vfunc (priv->job, PK_BACKEND_SIGNAL_ITEM_PROGRESS,
pk_direct_item_progress_cb, priv);
ret = pk_direct_run (priv, argv[1], (gchar**) &argv[2], &error);
if (!ret) {
if (g_error_matches (error, PK_ERROR, PK_ERROR_NO_SUCH_CMD)) {
g_autofree gchar *tmp = NULL;
tmp = g_option_context_get_help (priv->context, TRUE, NULL);
g_print ("%s", tmp);
} else {
g_print ("%s\n", error->message);
}
goto out;
}
if (!pk_backend_unload (priv->backend)) {
g_print ("%s\n", _("Failed to unload the backend"));
retval = EXIT_FAILURE;
goto out;
}
out:
if (priv->cmd_array != NULL)
g_ptr_array_unref (priv->cmd_array);
if (priv->backend != NULL)
g_object_unref (priv->backend);
if (priv->job != NULL)
g_object_unref (priv->job);
if (priv->loop != NULL)
g_main_loop_unref (priv->loop);
g_option_context_free (priv->context);
g_free (priv);
return retval;
}