summaryrefslogtreecommitdiff
path: root/src/pk-main.c
blob: 41c01981b8fb408144e73de0f014c1fcc74c4122 (plain)
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
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2007-2011 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"

#include <errno.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <locale.h>
#include <glib.h>
#include <glib-unix.h>
#include <glib/gi18n.h>
#include <packagekit-glib2/pk-debug.h>
#ifdef HAVE_SYSTEMD_SD_DAEMON_H
#include <systemd/sd-daemon.h>
#endif

#include "pk-engine.h"
#include "pk-shared.h"
#include "pk-transaction.h"

typedef struct {
	GMainLoop	*loop;
	PkEngine	*engine;
	guint		 exit_idle_time;
	guint		 timer_id;
} PkMainHelper;

/**
 * timed_exit_cb:
 * @loop: The main loop
 *
 * Exits the main loop, which is helpful for valgrinding g-p-m.
 *
 * Return value: FALSE, as we don't want to repeat this action.
 **/
static gboolean
timed_exit_cb (GMainLoop *mainloop)
{
	g_main_loop_quit (mainloop);
	return FALSE;
}

static gboolean
pk_main_timeout_check_cb (PkMainHelper *helper)
{
	guint idle;
	idle = pk_engine_get_seconds_idle (helper->engine);
	g_debug ("idle is %i", idle);
	if (idle > helper->exit_idle_time) {
		g_main_loop_quit (helper->loop);
		helper->timer_id = 0;
		return FALSE;
	}
	return TRUE;
}

static void
pk_main_quit_cb (PkEngine *engine, GMainLoop *mainloop)
{
	g_debug ("engine quit");
	g_main_loop_quit (mainloop);
}

static gboolean
pk_main_sigint_cb (gpointer user_data)
{
	GMainLoop *mainloop = (GMainLoop *) user_data;
	g_debug ("Handling SIGINT");
	g_main_loop_quit (mainloop);
	return FALSE;
}

int
main (int argc, char *argv[])
{
	GMainLoop *loop = NULL;
	GOptionContext *context;
	PkMainHelper helper;
	gboolean ret = TRUE;
	gboolean disable_timer = FALSE;
	gboolean version = FALSE;
	gboolean timed_exit = FALSE;
	gboolean immediate_exit = FALSE;
	gboolean keep_environment = FALSE;
	gint exit_idle_time;
	g_autoptr(GError) error = NULL;
	g_autofree gchar *backend_name = NULL;
	g_autofree gchar *conf_filename = NULL;
	g_autoptr(GKeyFile) conf = NULL;
	g_autoptr(PkEngine) engine = NULL;

	const GOptionEntry options[] = {
		{ "backend", '\0', 0, G_OPTION_ARG_STRING, &backend_name,
		  /* TRANSLATORS: a backend is the system package tool, e.g. dnf, apt */
		  _("Packaging backend to use, e.g. dummy"), NULL },
		{ "disable-timer", '\0', 0, G_OPTION_ARG_NONE, &disable_timer,
		  /* TRANSLATORS: if we should not monitor how long we are inactive for */
		  _("Disable the idle timer"), NULL },
		{ "version", '\0', 0, G_OPTION_ARG_NONE, &version,
		  /* TRANSLATORS: show version */
		  _("Show version and exit"), NULL },
		{ "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
		  /* TRANSLATORS: exit after we've started up, used for user profiling */
		  _("Exit after a small delay"), NULL },
		{ "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
		  /* TRANSLATORS: exit straight away, used for automatic profiling */
		  _("Exit after the engine has loaded"), NULL },
		{ "keep-environment", '\0', 0, G_OPTION_ARG_NONE, &keep_environment,
		  /* TRANSLATORS: don't unset environment variables, used for debugging */
		  _("Don't clear environment on startup"), NULL },
		{ NULL }
	};

	setlocale (LC_ALL, "");
	bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
	textdomain (GETTEXT_PACKAGE);
	openlog ("PackageKit", LOG_NDELAY, LOG_USER);

	/* set the default thread explicitly */
	pk_is_thread_default ();

	/* TRANSLATORS: describing the service that is running */
	context = g_option_context_new (_("PackageKit service"));
	g_option_context_add_main_entries (context, options, NULL);
	g_option_context_add_group (context, pk_debug_get_option_group ());
	g_option_context_parse (context, &argc, &argv, NULL);
	g_option_context_free (context);

	if (version) {
		g_print ("Version %s\n", VERSION);
		goto exit_program;
	}

	/* we don't actually need to do this, except it rules out the
	 * 'it works from the command line but not service activation' bugs */
#ifdef HAVE_CLEARENV
	if (!keep_environment)
		clearenv ();
#endif

	/* get values from the config file */
	conf = g_key_file_new ();
	conf_filename = pk_util_get_config_filename ();
	if (conf_filename == NULL) {
		g_printerr ("Config file was not found.");
		goto out;
	}
	ret = g_key_file_load_from_file (conf, conf_filename,
					 G_KEY_FILE_NONE, &error);
	if (!ret) {
		g_printerr ("Failed to load config file: %s", error->message);
		goto out;
	}
	g_key_file_set_boolean (conf, "Daemon", "KeepEnvironment", keep_environment);

	/* log the startup */
	syslog (LOG_DAEMON | LOG_DEBUG, "daemon start");

	/* after how long do we timeout? */
	exit_idle_time = g_key_file_get_integer (conf, "Daemon", "ShutdownTimeout", NULL);
	g_debug ("daemon shutdown set to %i seconds", exit_idle_time);

	/* override the backend name */
	if (backend_name != NULL) {
		g_key_file_set_string (conf,
				       "Daemon",
				       "DefaultBackend",
				       backend_name);
	}

	/* resolve 'auto' to an actual name */
	backend_name = g_key_file_get_string (conf, "Daemon", "DefaultBackend", NULL);
	if (backend_name == NULL || g_strcmp0 (backend_name, "auto") == 0) {
		ret  = pk_util_set_auto_backend (conf, &error);
		if (!ret) {
			g_printerr ("Failed to resolve auto: %s", error->message);
			goto out;
		}
	}

	loop = g_main_loop_new (NULL, FALSE);

	/* create a new engine object */
	engine = pk_engine_new (conf);
	g_signal_connect (engine, "quit",
			  G_CALLBACK (pk_main_quit_cb), loop);

	/* do stuff on ctrl-c */
	g_unix_signal_add_full (G_PRIORITY_DEFAULT,
				SIGINT,
				pk_main_sigint_cb,
				loop,
				NULL);

	/* load the backend */
	ret = pk_engine_load_backend (engine, &error);
	if (!ret) {
		/* TRANSLATORS: cannot load the backend the user specified */
		g_printerr ("Failed to load the backend: %s", error->message);
		goto out;
	}

	/* Only timeout and close the mainloop if we have specified it
	 * on the command line */
	if (timed_exit)
		g_timeout_add_seconds (20, (GSourceFunc) timed_exit_cb, loop);

	/* only poll when we are alive */
	if (exit_idle_time > 0 && !disable_timer) {
		helper.engine = engine;
		helper.exit_idle_time = exit_idle_time;
		helper.loop = loop;
		helper.timer_id = g_timeout_add_seconds (5, (GSourceFunc) pk_main_timeout_check_cb, &helper);
		g_source_set_name_by_id (helper.timer_id, "[PkMain] main poll");
	}

	/* immediatly exit */
	if (immediate_exit)
		g_timeout_add (50, (GSourceFunc) timed_exit_cb, loop);

	/* run until quit */
	g_main_loop_run (loop);
out:
	/* log the shutdown */
	syslog (LOG_DAEMON | LOG_DEBUG, "daemon quit");
	closelog ();

#ifdef HAVE_SYSTEMD_SD_DAEMON_H
	sd_notify (0, "STOPPING=1");
#endif

	if (helper.timer_id > 0)
		g_source_remove (helper.timer_id);
	if (loop != NULL)
		g_main_loop_unref (loop);
exit_program:
	return 0;
}