File: pkgcli.c

package info (click to toggle)
packagekit 1.3.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 14,824 kB
  • sloc: ansic: 56,491; cpp: 15,652; xml: 5,532; python: 4,932; sh: 316; perl: 60; makefile: 56
file content (275 lines) | stat: -rw-r--r-- 8,278 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2012-2025 Matthias Klumpp <matthias@tenstral.net>
 * Copyright (C) 2007-2014 Richard Hughes <richard@hughsie.com>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the license, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <locale.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib-unix.h>
#include <packagekit-glib2/packagekit.h>

#include "pkgc-context.h"
#include "pkgc-util.h"

#include "pkgc-query.h"
#include "pkgc-manage.h"
#include "pkgc-repo.h"
#include "pkgc-monitor.h"

/**
 * pkc_handle_sigint:
 */
static gboolean
pkc_handle_sigint (gpointer user_data)
{
	PkgcliContext *ctx = user_data;

	/* Cancel any running transaction */
	if (ctx->cancellable && !g_cancellable_is_cancelled (ctx->cancellable))
		g_cancellable_cancel (ctx->cancellable);

	/* Quit main loop if running */
	if (ctx->loop && g_main_loop_is_running (ctx->loop))
		g_main_loop_quit (ctx->loop);

	return G_SOURCE_REMOVE;
}

/* Global option variables */
static gboolean opt_version = FALSE;
static gboolean opt_help = FALSE;
static gboolean opt_quiet = FALSE;
static gboolean opt_verbose = FALSE;
static gboolean opt_json = FALSE;
static gboolean opt_yes = FALSE;
static gboolean opt_no_color = FALSE;
static gboolean opt_background = FALSE;
static gchar *opt_filter_str = NULL;

/* Global options that apply to all commands */
const GOptionEntry pkgc_global_options[] = {
	{ "version", 'v', 0, G_OPTION_ARG_NONE, &opt_version,
		/* TRANSLATORS: command line argument, just show the version string */
		N_("Show pkgcli version"), NULL },
	{ "help", 'h', 0, G_OPTION_ARG_NONE, &opt_help,
		N_("Show help"), NULL },
	{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &opt_quiet,
		N_("Only provide minimal output"), NULL },
	{ "verbose", 0, 0, G_OPTION_ARG_NONE, &opt_verbose,
		N_("Show more detailed output"), NULL },
	{ "json", 0, 0, G_OPTION_ARG_NONE, &opt_json,
		N_("Output in JSON format"), NULL },
	{ "no-color", 0, 0, G_OPTION_ARG_NONE, &opt_no_color,
		N_("Disable colored output"), NULL },
	{ "yes", 'y', 0, G_OPTION_ARG_NONE, &opt_yes,
		/* TRANSLATORS: command line argument, do we ask questions */
		N_("Answer 'yes' to all questions"), NULL },
	{ "filter", 'f', 0, G_OPTION_ARG_STRING, &opt_filter_str,
		/* TRANSLATORS: command line argument, use a filter to narrow down results */
	N_("Filter packages (installed, available, etc.)"), N_("FILTER") },
	{ "background", 'n', 0, G_OPTION_ARG_NONE, &opt_background,
		/* TRANSLATORS: command line argument, this command is not a priority */
		N_("Run the command using idle network bandwidth and also using less power"), NULL},
	{ NULL, 0, 0, 0, NULL, NULL, NULL }
};

/**
 * pkgc_dispatch_command:
 * @ctx: a valid #PkgctlContext
 * @argc: argument count
 * @argv: argument vector
 *
 * Dispatch the command based on the command-line arguments.
 *
 * Returns: exit code
 */
static int
pkgc_dispatch_command (PkgcliContext *ctx, int argc, char **argv)
{
	const gchar *command_name;
	PkgcliCommand *cmd;
	g_autoptr(GError) error = NULL;

	if (argc < 2) {
		pkgc_print_error (ctx,
				  _("No command specified. Use --help for usage information."));
		return PKGC_EXIT_SYNTAX_ERROR;
	}

	command_name = argv[1];
	cmd = pkgc_context_find_command (ctx, command_name);

	if (!cmd) {
		pkgc_print_error (ctx, _("Unknown command: %s"), command_name);
		return PKGC_EXIT_SYNTAX_ERROR;
	}

	if (g_strcmp0 (command_name, "monitor") != 0) {
		/* we defer initialization of the context if we are monitoring the bus */

		/* connect to PK with the selected parameters */
		if (!pkgc_context_init (ctx, &error)) {
			pkgc_print_error (ctx, _("Failed to connect to PackageKit: %s"), error->message);
			return PKGC_EXIT_FAILURE;
		}
	}

	/* call the command handler */
	return cmd->handler (ctx, cmd, argc - 1, argv + 1);
}

/**
 * main:
 */
int
main (int argc, char **argv)
{
	g_autoptr(GError) error = NULL;
	g_autoptr(GOptionContext) context = NULL;
	PkgcliContext *ctx = NULL;
	int ret = PKGC_EXIT_SUCCESS;

	/* setup locale */
	setlocale (LC_ALL, "");
	bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
	textdomain (GETTEXT_PACKAGE);

	/* create context early so we can register commands */
	ctx = pkgc_context_new ();

	/* register available commands */
	pkgc_register_query_commands (ctx);
	pkgc_register_manage_commands (ctx);
	pkgc_register_repo_commands (ctx);
	pkgc_register_monitor_commands (ctx);

	/* check if this is a command-specific help request before parsing options */
	if (argc >= 3) {
		for (gint i = 2; i < argc; i++) {
			if (strcmp (argv[i], "--help") == 0 || strcmp (argv[i], "-h") == 0) {
				/* this is command-specific help, skip global parsing */
				goto skip_global_parse;
			}
		}
	}

	/* parse only global options, ignore unknown ones (they're command-specific) */
	context = g_option_context_new ("COMMAND [OPTIONS...]");
	g_option_context_set_help_enabled (context, FALSE); /* We handle help ourselves to show commands */
	g_option_context_add_main_entries (context, pkgc_global_options, NULL);
	g_option_context_set_ignore_unknown_options (context, TRUE);

	if (!g_option_context_parse (context, &argc, &argv, &error)) {
		/* TRANSLATORS: Failed to parse command-line options in pkgcli */
		g_printerr (_("Failed to parse options: %s"), error->message);
		g_printerr ("\n");
		ret = PKGC_EXIT_SYNTAX_ERROR;
		goto out;
	}

	/* show version */
	if (opt_version) {
		g_print (_("Version: %s"), VERSION);
		g_print ("\n");
		ret = PKGC_EXIT_SUCCESS;
		goto out;
	}

	/* show help */
	if (opt_help || argc < 2) {
		g_autofree gchar *help_text = NULL;

		help_text = g_option_context_get_help (context, TRUE, NULL);
		g_print ("%s", help_text);

		/* Print available commands */
		g_print ("\n");
		g_print ("%s\n", _("Available Commands:"));
		for (guint i = 0; i < ctx->commands->len; i++) {
			PkgcliCommand *cmd = g_ptr_array_index (ctx->commands, i);
			g_print ("  %-23s %s\n", cmd->name, cmd->summary);
		}
		g_print ("\n");
		g_print ("%s\n", _("Use 'pkgcli COMMAND --help' for command-specific help."));

		ret = PKGC_EXIT_SUCCESS;
		goto out;
	}

skip_global_parse:

	/* setup signal handler */
	g_unix_signal_add_full (G_PRIORITY_DEFAULT, SIGINT, pkc_handle_sigint, ctx, NULL);

	/* parse output mode from global options */
	if (opt_json)
		ctx->output_mode = PKGCLI_MODE_JSON;
	else if (opt_quiet)
		ctx->output_mode = PKGCLI_MODE_QUIET;
	else if (opt_verbose)
		ctx->output_mode = PKGCLI_MODE_VERBOSE;

	if (ctx->output_mode == PKGCLI_MODE_VERBOSE)
		pk_debug_set_verbose (TRUE);

	/* disable colored output if NO_COLOR is present */
	if (g_getenv ("NO_COLOR") != NULL || opt_no_color)
		ctx->no_color = TRUE;

	/* -y flag means non-interactive */
	ctx->noninteractive = opt_yes;

	/* set user-defined filter if we have one */
	if (opt_filter_str != NULL) {
		ctx->filters = pk_filter_bitfield_from_string (opt_filter_str);
		ctx->user_filters_set = TRUE;
		if (ctx->filters == 0) {
			pkgc_print_error(ctx, "%s: %s",
						 /* TRANSLATORS: The user specified
						  * an incorrect filter */
						 _("The filter specified was invalid"),
						 opt_filter_str);
			ret = PKGC_EXIT_SYNTAX_ERROR;
			goto out;
		}
	}

	/* start polkit agent to listen for authentication requests */
	pk_polkit_agent_open ();

	/* dispatch command */
	ret = pkgc_dispatch_command (ctx, argc, argv);

out:
	/* stop listening for polkit questions */
	pk_polkit_agent_close ();

	pkgc_context_free (ctx);
	g_clear_pointer (&opt_filter_str, g_free);

	return ret;
}