/* * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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; }