vn-updater/src/vn-updater.cc

477 lines
9.9 KiB
C++

/*
* Copyright (C) 2013 - Juan Ferrer Toribio
*
* 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 3 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 <apt-pkg/aptconfiguration.h>
#include <apt-pkg/error.h>
#include <apt-pkg/cmndline.h>
#include <apt-pkg/init.h>
#include <apt-pkg/depcache.h>
#include <apt-pkg/sourcelist.h>
#include <apt-pkg/algorithms.h>
#include <apt-pkg/acquire-item.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/clean.h>
#include <apt-pkg/srcrecords.h>
#include <apt-pkg/version.h>
#include <apt-pkg/cachefile.h>
#include <apt-pkg/cacheset.h>
#include <apt-pkg/sptr.h>
#include <apt-pkg/md5.h>
#include <apt-pkg/versionmatch.h>
#include <apt-pkg/progress.h>
#include <apt-pkg/pkgsystem.h>
#include <apt-pkg/pkgrecords.h>
#include <apt-pkg/indexfile.h>
#include <stdio.h>
#include <string.h>
#include <glib-unix.h>
#include "vn-status.h"
#include "vn-updater.h"
#define PKG_BUFFER_SIZE 256
#define APT_CONFIG_FILE _CONFIG_DIR"/apt.conf"
#define PACKAGES_FILE _CONFIG_DIR"/packages.conf"
using namespace std;
typedef struct
{
GMainLoop * main_loop;
GSocket * sock;
GMutex mutex;
VnUpdaterStatus status;
GCancellable * cancel;
GThread * thread;
gboolean exit;
GSList * packages;
}
VnUpdater;
static void vn_updater_thread (VnUpdater * self);
static gboolean vn_updater_signal_handler (VnUpdater * self);
int main (int argc, char * argv[])
{
if (setuid (0))
{
g_warning ("VnUpdater must run as root.");
return 1;
}
if (!pkgInitConfig (*_config) || !pkgInitSystem (*_config, _system))
{
g_warning ("Can't initialize the package system.");
return 2;
}
if (!ReadConfigFile (*_config, APT_CONFIG_FILE))
{
g_warning ("Error reading config file: %s", APT_CONFIG_FILE);
return 3;
}
// Initializing service
VnUpdater * self;
FILE * file;
GSocketAddress * address;
GError * err = NULL;
self = g_new (VnUpdater, 1);
self->status = VN_UPDATER_STATUS_UNKNOWN;
self->cancel = g_cancellable_new ();
self->exit = FALSE;
g_mutex_init (&self->mutex);
// Loading the list of packages
self->packages = NULL;
file = g_fopen (PACKAGES_FILE, "r");
if (file)
{
gchar buffer[PKG_BUFFER_SIZE];
while (fgets (buffer, PKG_BUFFER_SIZE, file))
{
gsize len = strlen (buffer) - 1;
if (len > 0)
{
buffer[len] = '\0';
self->packages =
g_slist_prepend (self->packages, g_strdup (buffer));
g_message ("Package: %s", buffer);
}
}
fclose (file);
}
if (!file || !self->packages)
g_warning ("Can't get the list of packages.");
// Initializing the unix socket
self->sock = g_socket_new (
G_SOCKET_FAMILY_UNIX
,G_SOCKET_TYPE_STREAM
,G_SOCKET_PROTOCOL_DEFAULT
,&err
);
g_unlink (VN_UPDATER_SOCKET);
address = g_unix_socket_address_new (VN_UPDATER_SOCKET);
if (self->sock
&& g_socket_bind (self->sock, address, TRUE, &err)
&& g_socket_listen (self->sock, &err))
{
g_chmod (VN_UPDATER_SOCKET, 0777);
g_message ("Service started.");
self->main_loop = g_main_loop_new (NULL, FALSE);
self->thread = g_thread_new ("vn-updater",
(GThreadFunc) vn_updater_thread, self);
g_unix_signal_add_full (G_PRIORITY_HIGH, SIGTERM,
(GSourceFunc) vn_updater_signal_handler, self, NULL);
g_main_loop_run (self->main_loop);
g_main_loop_unref (self->main_loop);
g_thread_join (self->thread);
}
else
{
g_warning (err->message);
g_clear_error (&err);
}
g_object_unref (address);
g_socket_close (self->sock, NULL);
g_unlink (VN_UPDATER_SOCKET);
g_object_unref (self->sock);
g_object_unref (self->cancel);
g_mutex_clear (&self->mutex);
g_slist_free_full (self->packages, g_free);
g_free (self);
g_message ("Service stopped.");
return 0;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private
static gboolean vn_updater_check (VnUpdater * self)
{
pkgCacheFile cache;
pkgSourceList * list;
g_message ("Updating indexes...");
// Build the sources list
if (!cache.BuildSourceList())
return FALSE;
list = cache.GetSourceList();
// List repositories
pkgAcquire fetcher;
list->GetIndexes (&fetcher, false);
pkgAcquire::UriIterator uri = fetcher.UriBegin();
for (; uri != fetcher.UriEnd (); ++uri)
g_message (uri->URI.c_str ());
// Download and update indexes
VnStatus * stat = new VnStatus ();
ListUpdate (*stat, *list);
delete stat;
// Rebuild the cache
if (_config->FindB("pkgCacheFile::Generate", true))
{
pkgCacheFile::RemoveCaches ();
if (!cache.BuildCaches ())
return FALSE;
}
// Check if new package version is available
if (!cache.Open ())
return FALSE;
GSList * i;
int updates = 0;
pkgCache::PkgIterator pkg;
pkgCache::VerIterator lastVersion;
for (i = self->packages; i; i = i->next)
{
pkg = cache->FindPkg ((gchar *) i->data);
if (!pkg)
{
g_warning ("Package '%s' not found.", (gchar *) i->data);
continue;
}
lastVersion = pkg.VersionList ();
if (lastVersion.CompareVer (pkg.CurrentVer ())
&& lastVersion.Downloadable ())
updates++;
}
cache.Close ();
if (updates > 0)
self->status = VN_UPDATER_STATUS_UPDATABLE;
else
self->status = VN_UPDATER_STATUS_UPDATED;
g_message ("Ok");
return TRUE;
}
static gboolean vn_updater_update (VnUpdater * self, gboolean download_only)
{
GSList * i;
pkgCacheFile cache;
pkgCache::PkgIterator pkg;
pkgCache::DepIterator dep;
pkgCache::PkgIterator dep_pkg;
g_message ("Downloading packages...");
if (!cache.Open ())
return FALSE;
for (i = self->packages; i; i = i->next)
{
pkg = cache->FindPkg ((gchar *) i->data);
if (!pkg)
continue;
dep = pkg.VersionList ().DependsList ();
for (; !dep.end (); dep++)
if (dep.IsCritical ())
{
dep_pkg = dep.TargetPkg ();
if (dep_pkg.VersionList ().CompareVer (dep_pkg.CurrentVer ()) == 0)
continue;
g_message ("Checking dependence: %s: %s"
,pkg.Name ()
,dep.TargetPkg ().Name ()
);
cache->MarkInstall (dep.TargetPkg (), true);
}
cache->MarkInstall (pkg, false);
}
g_message ("Update/Install count: %d", cache->InstCount ());
if (cache->InstCount () == 0)
{
g_message ("Ok");
self->status = VN_UPDATER_STATUS_UPDATED;
return TRUE;
}
// Downloads the packages
pkgAcquire fetcher;
fetcher.Setup ();
pkgRecords recs (cache);
pkgSourceList * list = cache.GetSourceList ();
SPtr<pkgPackageManager> packageManager = _system->CreatePM (cache);
if (!packageManager->GetArchives (&fetcher, list, &recs))
return FALSE;
unsigned long long fetchBytes = fetcher.FetchNeeded();
unsigned long long totalBytes = fetcher.TotalNeeded();
g_message ("Download size: %sB/%sB"
,SizeToStr (fetchBytes).c_str ()
,SizeToStr (totalBytes).c_str ()
);
if (fetcher.Run () == pkgAcquire::Failed)
{
g_warning ("Can't download the packages.");
return FALSE;
}
g_message ("Ok");
if (download_only)
{
self->status = VN_UPDATER_STATUS_DOWNLOADED;
return TRUE;
}
// Installs the packages
g_message ("Installing packages...");
_system->UnLock();
pkgPackageManager::OrderResult result = packageManager->DoInstall ();
if (_error->PendingError ())
return FALSE;
if (result != pkgPackageManager::Completed)
{
g_warning ("Can't install the packages.");
return FALSE;
}
g_message ("Ok");
self->status = VN_UPDATER_STATUS_UPDATED;
return TRUE;
}
static gboolean vn_updater_response (GSocket * client, VnUpdaterStatus status)
{
gssize bytes;
gchar buffer[VN_UPDATER_BUFFER_SIZE];
GError * err = NULL;
g_sprintf (buffer, "%d", status);
bytes = g_socket_send (client, buffer, strlen (buffer)+1, NULL, &err);
if (bytes == -1)
{
g_warning (err->message);
g_error_free (err);
return FALSE;
}
return TRUE;
}
static void vn_updater_check_apt_errors ()
{
std::string message;
while (_error->PendingError ())
{
_error->PopMessage (message);
g_warning (message.c_str ());
}
}
static void vn_updater_thread (VnUpdater * self)
{
GSocket * client;
GError * err = NULL;
do {
gboolean ok;
gchar buffer[VN_UPDATER_BUFFER_SIZE];
g_message ("Waiting for clients...");
client = g_socket_accept (self->sock, self->cancel, &err);
g_message ("Incoming request.");
if (!client)
{
if (err && err->code != G_IO_ERROR_CANCELLED)
g_warning (err->message);
g_clear_error (&err);
break;
}
do {
gssize bytes = g_socket_receive (client,
buffer, VN_UPDATER_BUFFER_SIZE, self->cancel, &err);
if (bytes > 0 && buffer[bytes-1] == '\0')
{
ok = TRUE;
g_mutex_lock (&self->mutex);
switch (atoi (buffer))
{
case VN_UPDATER_REQUEST_STATUS:
ok = TRUE;
break;
case VN_UPDATER_REQUEST_CHECK:
ok = vn_updater_check (self);
break;
case VN_UPDATER_REQUEST_DOWNLOAD:
ok = vn_updater_update (self, TRUE);
break;
case VN_UPDATER_REQUEST_UPDATE:
ok = vn_updater_update (self, FALSE);
break;
}
if (ok)
vn_updater_response (client, self->status);
else
vn_updater_response (client, VN_UPDATER_STATUS_ERROR);
vn_updater_check_apt_errors ();
g_mutex_unlock (&self->mutex);
}
else if (bytes == -1)
{
g_warning (err->message);
g_clear_error (&err);
break;
}
else
break;
}
while (!g_socket_is_closed (client));
g_socket_close (client, NULL);
g_object_unref (client);
}
while (!g_atomic_int_get (&self->exit));
}
static gboolean vn_updater_signal_handler (VnUpdater * self)
{
g_atomic_int_set (&self->exit, TRUE);
g_cancellable_cancel (self->cancel);
g_main_loop_quit (self->main_loop);
return FALSE;
}