// -*- mode: cpp; mode: fold -*-
// Description								/*{{{*/
// $Id: debsystem.cc,v 1.4 2004/01/26 17:01:53 mdz Exp $
/* ######################################################################

   System - Abstraction for running on different systems.

   Basic general structure..
   
   ##################################################################### */
									/*}}}*/
// Include Files							/*{{{*/
#include <config.h>

#include <apt-pkg/debsystem.h>
#include <apt-pkg/debversion.h>
#include <apt-pkg/debindexfile.h>
#include <apt-pkg/dpkgpm.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/error.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/pkgcache.h>
#include <apt-pkg/cacheiterators.h>

#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>

#include <apti18n.h>
									/*}}}*/

using std::string;

debSystem debSys;

class debSystemPrivate {
public:
   debSystemPrivate() : LockFD(-1), LockCount(0), StatusFile(0)
   {
   }
   // For locking support
   int LockFD;
   unsigned LockCount;
   
   debStatusIndex *StatusFile;
};

// System::debSystem - Constructor					/*{{{*/
// ---------------------------------------------------------------------
/* */
debSystem::debSystem()
{
   d = new debSystemPrivate();
   Label = "Debian dpkg interface";
   VS = &debVS;
}
									/*}}}*/
// System::~debSystem - Destructor					/*{{{*/
// ---------------------------------------------------------------------
/* */
debSystem::~debSystem()
{
   delete d->StatusFile;
   delete d;
}
									/*}}}*/
// System::Lock - Get the lock						/*{{{*/
// ---------------------------------------------------------------------
/* This mirrors the operations dpkg does when it starts up. Note the
   checking of the updates directory. */
bool debSystem::Lock()
{
   // Disable file locking
   if (_config->FindB("Debug::NoLocking",false) == true || d->LockCount > 1)
   {
      d->LockCount++;
      return true;
   }

   // Create the lockfile
   string AdminDir = flNotFile(_config->Find("Dir::State::status"));
   d->LockFD = GetLock(AdminDir + "lock");
   if (d->LockFD == -1)
   {
      if (errno == EACCES || errno == EAGAIN)
	 return _error->Error(_("Unable to lock the administration directory (%s), "
	                        "is another process using it?"),AdminDir.c_str());
      else
	 return _error->Error(_("Unable to lock the administration directory (%s), "
	                        "are you root?"),AdminDir.c_str());
   }
   
   // See if we need to abort with a dirty journal
   if (CheckUpdates() == true)
   {
      close(d->LockFD);
      d->LockFD = -1;
      const char *cmd;
      if (getenv("SUDO_USER") != NULL)
	 cmd = "sudo dpkg --configure -a";
      else
	 cmd = "dpkg --configure -a";
      // TRANSLATORS: the %s contains the recovery command, usually
      //              dpkg --configure -a
      return _error->Error(_("dpkg was interrupted, you must manually "
                             "run '%s' to correct the problem. "), cmd);
   }

	 d->LockCount++;
      
   return true;
}
									/*}}}*/
// System::UnLock - Drop a lock						/*{{{*/
// ---------------------------------------------------------------------
/* */
bool debSystem::UnLock(bool NoErrors)
{
   if (d->LockCount == 0 && NoErrors == true)
      return false;
   
   if (d->LockCount < 1)
      return _error->Error(_("Not locked"));
   if (--d->LockCount == 0)
   {
      close(d->LockFD);
      d->LockCount = 0;
   }
   
   return true;
}
									/*}}}*/
// System::CheckUpdates - Check if the updates dir is dirty		/*{{{*/
// ---------------------------------------------------------------------
/* This does a check of the updates directory (dpkg journal) to see if it has 
   any entries in it. */
bool debSystem::CheckUpdates()
{
   // Check for updates.. (dirty)
   string File = flNotFile(_config->Find("Dir::State::status")) + "updates/";
   DIR *DirP = opendir(File.c_str());
   if (DirP == 0)
      return false;
   
   /* We ignore any files that are not all digits, this skips .,.. and 
      some tmp files dpkg will leave behind.. */
   bool Damaged = false;
   for (struct dirent *Ent = readdir(DirP); Ent != 0; Ent = readdir(DirP))
   {
      Damaged = true;
      for (unsigned int I = 0; Ent->d_name[I] != 0; I++)
      {
	 // Check if its not a digit..
	 if (isdigit(Ent->d_name[I]) == 0)
	 {
	    Damaged = false;
	    break;
	 }
      }
      if (Damaged == true)
	 break;
   }
   closedir(DirP);

   return Damaged;
}
									/*}}}*/
// System::CreatePM - Create the underlying package manager		/*{{{*/
// ---------------------------------------------------------------------
/* */
pkgPackageManager *debSystem::CreatePM(pkgDepCache *Cache) const
{
   return new pkgDPkgPM(Cache);
}
									/*}}}*/
// System::Initialize - Setup the configuration space..			/*{{{*/
// ---------------------------------------------------------------------
/* These are the Debian specific configuration variables.. */
bool debSystem::Initialize(Configuration &Cnf)
{
   /* These really should be jammed into a generic 'Local Database' engine
      which is yet to be determined. The functions in pkgcachegen should
      be the only users of these */
   Cnf.CndSet("Dir::State::extended_states", "extended_states");
   Cnf.CndSet("Dir::State::status","/var/lib/dpkg/status");
   Cnf.CndSet("Dir::Bin::dpkg","/usr/bin/dpkg");

   if (d->StatusFile) {
     delete d->StatusFile;
     d->StatusFile = 0;
   }

   return true;
}
									/*}}}*/
// System::ArchiveSupported - Is a file format supported		/*{{{*/
// ---------------------------------------------------------------------
/* The standard name for a deb is 'deb'.. There are no separate versions
   of .deb to worry about.. */
APT_PURE bool debSystem::ArchiveSupported(const char *Type)
{
   if (strcmp(Type,"deb") == 0)
      return true;
   return false;
}
									/*}}}*/
// System::Score - Determine how 'Debiany' this sys is..		/*{{{*/
// ---------------------------------------------------------------------
/* We check some files that are sure tell signs of this being a Debian
   System.. */
signed debSystem::Score(Configuration const &Cnf)
{
   signed Score = 0;
   if (FileExists(Cnf.FindFile("Dir::State::status","/var/lib/dpkg/status")) == true)
       Score += 10;
   if (FileExists(Cnf.FindFile("Dir::Bin::dpkg","/usr/bin/dpkg")) == true)
      Score += 10;
   if (FileExists("/etc/debian_version") == true)
      Score += 10;
   return Score;
}
									/*}}}*/
// System::AddStatusFiles - Register the status files			/*{{{*/
// ---------------------------------------------------------------------
/* */
bool debSystem::AddStatusFiles(std::vector<pkgIndexFile *> &List)
{
   if (d->StatusFile == 0)
      d->StatusFile = new debStatusIndex(_config->FindFile("Dir::State::status"));
   List.push_back(d->StatusFile);
   return true;
}
									/*}}}*/
// System::FindIndex - Get an index file for status files		/*{{{*/
// ---------------------------------------------------------------------
/* */
bool debSystem::FindIndex(pkgCache::PkgFileIterator File,
			  pkgIndexFile *&Found) const
{
   if (d->StatusFile == 0)
      return false;
   if (d->StatusFile->FindInCache(*File.Cache()) == File)
   {
      Found = d->StatusFile;
      return true;
   }
   
   return false;
}
									/*}}}*/