summaryrefslogtreecommitdiff
path: root/methods
diff options
context:
space:
mode:
authorMichael Vogt <michael.vogt@ubuntu.com>2010-06-09 11:51:21 +0200
committerMichael Vogt <michael.vogt@ubuntu.com>2010-06-09 11:51:21 +0200
commit23c5897cbdb5a957788201a5178e963586dcdbc9 (patch)
tree8303be690b404c47d7a2da310175e1a40c62348c /methods
parent6dd8400ca787ef832d87121f304f98ad152dc3a6 (diff)
parent164fed49362b28c0264c293a14a06d167e32b76f (diff)
* merge the remaining Ubuntu change:
- on gpg verification failure warn and restore the last known good state - on failure display the IP of the server (useful for servers that use round robin DNS) - support Original-Maintainer in RewritePackageOrder - enable cdrom autodetection via libudev by default - show messsage about Vcs in use when apt-get source is run for packages maintained in a Vcs - better support transitional packages with mark auto-installed. when the transitional package is in "oldlibs" the new package is not marked auto installed (same is true for section metapackages) - provide new "deb mirror://archive.foo/mirrors.list sid main" method expects a list of mirrors (generated on the server e.g. via geoip) and will use that, including cycle on failure - write apport crash file on package failure (disabled by default on debian until apport is available) - support mirror failure reporting (disabled by default on debian)
Diffstat (limited to 'methods')
-rw-r--r--methods/connect.cc23
-rw-r--r--methods/copy.cc1
-rw-r--r--methods/http.cc14
-rw-r--r--methods/http.h6
-rw-r--r--methods/http_main.cc20
-rw-r--r--methods/makefile10
-rw-r--r--methods/mirror.cc330
-rw-r--r--methods/mirror.h52
8 files changed, 430 insertions, 26 deletions
diff --git a/methods/connect.cc b/methods/connect.cc
index 2f6b4833e..a5af1f1a6 100644
--- a/methods/connect.cc
+++ b/methods/connect.cc
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
+#include <sstream>
#include<set>
#include<string>
@@ -70,19 +71,17 @@ static bool DoConnect(struct addrinfo *Addr,string Host,
Owner->Status(_("Connecting to %s (%s)"),Host.c_str(),Name);
// if that addr did timeout before, we do not try it again
- if(bad_addr.find(string(Name)) != bad_addr.end())
+ if(bad_addr.find(string(Name)) != bad_addr.end())
return false;
/* If this is an IP rotation store the IP we are using.. If something goes
wrong this will get tacked onto the end of the error message */
if (LastHostAddr->ai_next != 0)
{
- char Name2[NI_MAXHOST + NI_MAXSERV + 10];
- snprintf(Name2,sizeof(Name2),_("[IP: %s %s]"),Name,Service);
- Owner->SetFailExtraMsg(string(Name2));
- }
- else
- Owner->SetFailExtraMsg("");
+ std::stringstream ss;
+ ioprintf(ss, _("[IP: %s %s]"),Name,Service);
+ Owner->SetIP(ss.str());
+ }
// Get a socket
if ((Fd = socket(Addr->ai_family,Addr->ai_socktype,
@@ -100,7 +99,7 @@ static bool DoConnect(struct addrinfo *Addr,string Host,
nonblocking */
if (WaitFd(Fd,true,TimeOut) == false) {
bad_addr.insert(bad_addr.begin(), string(Name));
- Owner->SetFailExtraMsg("\nFailReason: Timeout");
+ Owner->SetFailReason("Timeout");
return _error->Error(_("Could not connect to %s:%s (%s), "
"connection timed out"),Host.c_str(),Service,Name);
}
@@ -115,9 +114,9 @@ static bool DoConnect(struct addrinfo *Addr,string Host,
{
errno = Err;
if(errno == ECONNREFUSED)
- Owner->SetFailExtraMsg("\nFailReason: ConnectionRefused");
+ Owner->SetFailReason("ConnectionRefused");
else if (errno == ETIMEDOUT)
- Owner->SetFailExtraMsg("\nFailReason: ConnectionTimedOut");
+ Owner->SetFailReason("ConnectionTimedOut");
bad_addr.insert(bad_addr.begin(), string(Name));
return _error->Errno("connect",_("Could not connect to %s:%s (%s)."),Host.c_str(),
Service,Name);
@@ -184,13 +183,13 @@ bool Connect(string Host,int Port,const char *Service,int DefPort,int &Fd,
continue;
}
bad_addr.insert(bad_addr.begin(), Host);
- Owner->SetFailExtraMsg("\nFailReason: ResolveFailure");
+ Owner->SetFailReason("ResolveFailure");
return _error->Error(_("Could not resolve '%s'"),Host.c_str());
}
if (Res == EAI_AGAIN)
{
- Owner->SetFailExtraMsg("\nFailReason: TmpResolveFailure");
+ Owner->SetFailReason("TmpResolveFailure");
return _error->Error(_("Temporary failure resolving '%s'"),
Host.c_str());
}
diff --git a/methods/copy.cc b/methods/copy.cc
index 72896b4c0..027b59f46 100644
--- a/methods/copy.cc
+++ b/methods/copy.cc
@@ -84,6 +84,7 @@ bool CopyMethod::Fetch(FetchItem *Itm)
FileFd Fd(Res.Filename, FileFd::ReadOnly);
Hash.AddFD(Fd.Fd(), Fd.Size());
Res.TakeHashes(Hash);
+
URIDone(Res);
return true;
}
diff --git a/methods/http.cc b/methods/http.cc
index d43dd14c8..3e2227f2b 100644
--- a/methods/http.cc
+++ b/methods/http.cc
@@ -953,6 +953,9 @@ HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
failure */
if (Srv->Result < 200 || Srv->Result >= 300)
{
+ char err[255];
+ snprintf(err,sizeof(err)-1,"HttpError%i",Srv->Result);
+ SetFailReason(err);
_error->Error("%u %s",Srv->Result,Srv->Code);
if (Srv->HaveContent == true)
return ERROR_WITH_CONTENT_PAGE;
@@ -1366,15 +1369,4 @@ bool HttpMethod::AutoDetectProxy()
}
/*}}}*/
-int main()
-{
- setlocale(LC_ALL, "");
- // ignore SIGPIPE, this can happen on write() if the socket
- // closes the connection (this is dealt with via ServerDie())
- signal(SIGPIPE, SIG_IGN);
-
- HttpMethod Mth;
- return Mth.Loop();
-}
-
diff --git a/methods/http.h b/methods/http.h
index af0f5e033..d0677bdaa 100644
--- a/methods/http.h
+++ b/methods/http.h
@@ -13,7 +13,7 @@
#define MAXLEN 360
-#include <iostream>
+
using std::cout;
using std::endl;
@@ -167,7 +167,6 @@ class HttpMethod : public pkgAcqMethod
/** \brief Try to AutoDetect the proxy */
bool AutoDetectProxy();
- virtual bool Fetch(FetchItem *);
virtual bool Configuration(string Message);
// In the event of a fatal signal this file will be closed and timestamped.
@@ -175,6 +174,9 @@ class HttpMethod : public pkgAcqMethod
static int FailFd;
static time_t FailTime;
static void SigTerm(int);
+
+ protected:
+ virtual bool Fetch(FetchItem *);
string NextURI;
string AutoDetectProxyCmd;
diff --git a/methods/http_main.cc b/methods/http_main.cc
new file mode 100644
index 000000000..7815c2fc1
--- /dev/null
+++ b/methods/http_main.cc
@@ -0,0 +1,20 @@
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/acquire-method.h>
+#include <signal.h>
+
+#include "connect.h"
+#include "rfc2553emu.h"
+#include "http.h"
+
+
+int main()
+{
+ setlocale(LC_ALL, "");
+
+ // ignore SIGPIPE, this can happen on write() if the socket
+ // closes the connection (this is dealt with via ServerDie())
+ signal(SIGPIPE, SIG_IGN);
+
+ HttpMethod Mth;
+ return Mth.Loop();
+}
diff --git a/methods/makefile b/methods/makefile
index 7bcae6b9b..eabe85cfd 100644
--- a/methods/makefile
+++ b/methods/makefile
@@ -48,7 +48,7 @@ include $(PROGRAM_H)
PROGRAM=http
SLIBS = -lapt-pkg $(SOCKETLIBS) $(INTLLIBS)
LIB_MAKES = apt-pkg/makefile
-SOURCE = http.cc rfc2553emu.cc connect.cc
+SOURCE = http.cc http_main.cc rfc2553emu.cc connect.cc
include $(PROGRAM_H)
# The https method
@@ -79,9 +79,17 @@ LIB_MAKES = apt-pkg/makefile
SOURCE = rsh.cc
include $(PROGRAM_H)
+# The mirror method
+PROGRAM=mirror
+SLIBS = -lapt-pkg $(SOCKETLIBS)
+LIB_MAKES = apt-pkg/makefile
+SOURCE = mirror.cc http.cc rfc2553emu.cc connect.cc
+include $(PROGRAM_H)
+
# SSH and bzip2 method symlink
binary: $(BIN)/ssh $(BIN)/bzip2 $(BIN)/lzma
veryclean: clean-$(BIN)/ssh clean-$(BIN)/bzip2 clean-$(BIN)/lzma
+
$(BIN)/ssh:
echo "Installing ssh method link"
ln -fs rsh $(BIN)/ssh
diff --git a/methods/mirror.cc b/methods/mirror.cc
new file mode 100644
index 000000000..b3a956b95
--- /dev/null
+++ b/methods/mirror.cc
@@ -0,0 +1,330 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+// $Id: mirror.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $
+/* ######################################################################
+
+ Mirror Aquire Method - This is the Mirror aquire method for APT.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/acquire-method.h>
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/sourcelist.h>
+
+#include <fstream>
+#include <iostream>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+using namespace std;
+
+#include "mirror.h"
+#include "http.h"
+#include "apti18n.h"
+ /*}}}*/
+
+/* Done:
+ * - works with http (only!)
+ * - always picks the first mirror from the list
+ * - call out to problem reporting script
+ * - supports "deb mirror://host/path/to/mirror-list/// dist component"
+ * - uses pkgAcqMethod::FailReason() to have a string representation
+ * of the failure that is also send to LP
+ *
+ * TODO:
+ * - deal with runing as non-root because we can't write to the lists
+ dir then -> use the cached mirror file
+ * - better method to download than having a pkgAcquire interface here
+ * and better error handling there!
+ * - support more than http
+ * - testing :)
+ */
+
+MirrorMethod::MirrorMethod()
+ : HttpMethod(), DownloadedMirrorFile(false)
+{
+};
+
+// HttpMethod::Configuration - Handle a configuration message /*{{{*/
+// ---------------------------------------------------------------------
+/* We stash the desired pipeline depth */
+bool MirrorMethod::Configuration(string Message)
+{
+ if (pkgAcqMethod::Configuration(Message) == false)
+ return false;
+ Debug = _config->FindB("Debug::Acquire::mirror",false);
+
+ return true;
+}
+ /*}}}*/
+
+// clean the mirrors dir based on ttl information
+bool MirrorMethod::Clean(string Dir)
+{
+ vector<metaIndex *>::const_iterator I;
+
+ if(Debug)
+ clog << "MirrorMethod::Clean(): " << Dir << endl;
+
+ if(Dir == "/")
+ return _error->Error("will not clean: '/'");
+
+ // read sources.list
+ pkgSourceList list;
+ list.ReadMainList();
+
+ DIR *D = opendir(Dir.c_str());
+ if (D == 0)
+ return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
+
+ string StartDir = SafeGetCWD();
+ if (chdir(Dir.c_str()) != 0)
+ {
+ closedir(D);
+ return _error->Errno("chdir",_("Unable to change to %s"),Dir.c_str());
+ }
+
+ for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
+ {
+ // Skip some files..
+ if (strcmp(Dir->d_name,"lock") == 0 ||
+ strcmp(Dir->d_name,"partial") == 0 ||
+ strcmp(Dir->d_name,".") == 0 ||
+ strcmp(Dir->d_name,"..") == 0)
+ continue;
+
+ // see if we have that uri
+ for(I=list.begin(); I != list.end(); I++)
+ {
+ string uri = (*I)->GetURI();
+ if(uri.substr(0,strlen("mirror://")) != string("mirror://"))
+ continue;
+ string BaseUri = uri.substr(0,uri.size()-1);
+ if (URItoFileName(BaseUri) == Dir->d_name)
+ break;
+ }
+ // nothing found, nuke it
+ if (I == list.end())
+ unlink(Dir->d_name);
+ };
+
+ chdir(StartDir.c_str());
+ closedir(D);
+ return true;
+}
+
+
+bool MirrorMethod::DownloadMirrorFile(string mirror_uri_str)
+{
+ if(Debug)
+ clog << "MirrorMethod::DownloadMirrorFile(): " << endl;
+
+ // check the file, if it is not older than RefreshInterval just use it
+ // otherwise try to get a new one
+ if(FileExists(MirrorFile))
+ {
+ struct stat buf;
+ time_t t,now,refresh;
+ if(stat(MirrorFile.c_str(), &buf) != 0)
+ return false;
+ t = std::max(buf.st_mtime, buf.st_ctime);
+ now = time(NULL);
+ refresh = 60*_config->FindI("Acquire::Mirror::RefreshInterval",360);
+ if(t + refresh > now)
+ {
+ if(Debug)
+ clog << "Mirror file is in RefreshInterval" << endl;
+ DownloadedMirrorFile = true;
+ return true;
+ }
+ if(Debug)
+ clog << "Mirror file " << MirrorFile << " older than " << refresh << "min, re-download it" << endl;
+ }
+
+ // not that great to use pkgAcquire here, but we do not have
+ // any other way right now
+ string fetch = BaseUri;
+ fetch.replace(0,strlen("mirror://"),"http://");
+
+ pkgAcquire Fetcher;
+ new pkgAcqFile(&Fetcher, fetch, "", 0, "", "", "", MirrorFile);
+ bool res = (Fetcher.Run() == pkgAcquire::Continue);
+ if(res)
+ DownloadedMirrorFile = true;
+ Fetcher.Shutdown();
+ return res;
+}
+
+bool MirrorMethod::SelectMirror()
+{
+ // if we do not have a MirrorFile, fallback
+ if(!FileExists(MirrorFile))
+ {
+ // FIXME: fallback to a default mirror here instead
+ // and provide a config option to define that default
+ return _error->Error(_("No mirror file '%s' found "), MirrorFile.c_str());
+ }
+
+ // FIXME: make the mirror selection more clever, do not
+ // just use the first one!
+ // BUT: we can not make this random, the mirror has to be
+ // stable accross session, because otherwise we can
+ // get into sync issues (got indexfiles from mirror A,
+ // but packages from mirror B - one might be out of date etc)
+ ifstream in(MirrorFile.c_str());
+ getline(in, Mirror);
+ if(Debug)
+ cerr << "Using mirror: " << Mirror << endl;
+
+ UsedMirror = Mirror;
+ return true;
+}
+
+string MirrorMethod::GetMirrorFileName(string mirror_uri_str)
+{
+ /*
+ - a mirror_uri_str looks like this:
+ mirror://people.ubuntu.com/~mvo/apt/mirror/mirrors/dists/feisty/Release.gpg
+
+ - the matching source.list entry
+ deb mirror://people.ubuntu.com/~mvo/apt/mirror/mirrors feisty main
+
+ - we actually want to go after:
+ http://people.ubuntu.com/~mvo/apt/mirror/mirrors
+
+ And we need to save the BaseUri for later:
+ - mirror://people.ubuntu.com/~mvo/apt/mirror/mirrors
+
+ FIXME: what if we have two similar prefixes?
+ mirror://people.ubuntu.com/~mvo/mirror
+ mirror://people.ubuntu.com/~mvo/mirror2
+ then mirror_uri_str looks like:
+ mirror://people.ubuntu.com/~mvo/apt/mirror/dists/feisty/Release.gpg
+ mirror://people.ubuntu.com/~mvo/apt/mirror2/dists/feisty/Release.gpg
+ we search sources.list and find:
+ mirror://people.ubuntu.com/~mvo/apt/mirror
+ in both cases! So we need to apply some domain knowledge here :( and
+ check for /dists/ or /Release.gpg as suffixes
+ */
+ string name;
+ if(Debug)
+ std::cerr << "GetMirrorFileName: " << mirror_uri_str << std::endl;
+
+ // read sources.list and find match
+ vector<metaIndex *>::const_iterator I;
+ pkgSourceList list;
+ list.ReadMainList();
+ for(I=list.begin(); I != list.end(); I++)
+ {
+ string uristr = (*I)->GetURI();
+ if(Debug)
+ std::cerr << "Checking: " << uristr << std::endl;
+ if(uristr.substr(0,strlen("mirror://")) != string("mirror://"))
+ continue;
+ // find matching uri in sources.list
+ if(mirror_uri_str.substr(0,uristr.size()) == uristr)
+ {
+ if(Debug)
+ std::cerr << "found BaseURI: " << uristr << std::endl;
+ BaseUri = uristr.substr(0,uristr.size()-1);
+ }
+ }
+ // get new file
+ name = _config->FindDir("Dir::State::mirrors") + URItoFileName(BaseUri);
+
+ if(Debug)
+ {
+ cerr << "base-uri: " << BaseUri << endl;
+ cerr << "mirror-file: " << name << endl;
+ }
+ return name;
+}
+
+// MirrorMethod::Fetch - Fetch an item /*{{{*/
+// ---------------------------------------------------------------------
+/* This adds an item to the pipeline. We keep the pipeline at a fixed
+ depth. */
+bool MirrorMethod::Fetch(FetchItem *Itm)
+{
+ if(Debug)
+ clog << "MirrorMethod::Fetch()" << endl;
+
+ // the http method uses Fetch(0) as a way to update the pipeline,
+ // just let it do its work in this case - Fetch() with a valid
+ // Itm will always run before the first Fetch(0)
+ if(Itm == NULL)
+ return HttpMethod::Fetch(Itm);
+
+ // if we don't have the name of the mirror file on disk yet,
+ // calculate it now (can be derived from the uri)
+ if(MirrorFile.empty())
+ MirrorFile = GetMirrorFileName(Itm->Uri);
+
+ // download mirror file once (if we are after index files)
+ if(Itm->IndexFile && !DownloadedMirrorFile)
+ {
+ Clean(_config->FindDir("Dir::State::mirrors"));
+ DownloadMirrorFile(Itm->Uri);
+ }
+
+ if(Mirror.empty()) {
+ if(!SelectMirror()) {
+ // no valid mirror selected, something went wrong downloading
+ // from the master mirror site most likely and there is
+ // no old mirror file availalbe
+ return false;
+ }
+ }
+ if(Debug)
+ clog << "selected mirror: " << Mirror << endl;
+
+
+ for (FetchItem *I = Queue; I != 0; I = I->Next)
+ {
+ if(I->Uri.find("mirror://") != string::npos)
+ I->Uri.replace(0,BaseUri.size(), Mirror);
+ }
+
+ // now run the real fetcher
+ return HttpMethod::Fetch(Itm);
+};
+
+void MirrorMethod::Fail(string Err,bool Transient)
+{
+ if(Queue->Uri.find("http://") != string::npos)
+ Queue->Uri.replace(0,Mirror.size(), BaseUri);
+ pkgAcqMethod::Fail(Err, Transient);
+}
+
+void MirrorMethod::URIStart(FetchResult &Res)
+{
+ if(Queue->Uri.find("http://") != string::npos)
+ Queue->Uri.replace(0,Mirror.size(), BaseUri);
+ pkgAcqMethod::URIStart(Res);
+}
+
+void MirrorMethod::URIDone(FetchResult &Res,FetchResult *Alt)
+{
+ if(Queue->Uri.find("http://") != string::npos)
+ Queue->Uri.replace(0,Mirror.size(), BaseUri);
+ pkgAcqMethod::URIDone(Res, Alt);
+}
+
+
+int main()
+{
+ setlocale(LC_ALL, "");
+
+ MirrorMethod Mth;
+
+ return Mth.Loop();
+}
+
+
diff --git a/methods/mirror.h b/methods/mirror.h
new file mode 100644
index 000000000..ed817806b
--- /dev/null
+++ b/methods/mirror.h
@@ -0,0 +1,52 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/// $Id: http.h,v 1.12 2002/04/18 05:09:38 jgg Exp $
+// $Id: http.h,v 1.12 2002/04/18 05:09:38 jgg Exp $
+/* ######################################################################
+
+ MIRROR Aquire Method - This is the MIRROR aquire method for APT.
+
+ ##################################################################### */
+ /*}}}*/
+
+#ifndef APT_MIRROR_H
+#define APT_MIRROR_H
+
+
+#include <iostream>
+
+using std::cout;
+using std::cerr;
+using std::endl;
+
+#include "http.h"
+
+class MirrorMethod : public HttpMethod
+{
+ FetchResult Res;
+ // we simply transform between BaseUri and Mirror
+ string BaseUri; // the original mirror://... url
+ string Mirror; // the selected mirror uri (http://...)
+ string MirrorFile; // the file that contains the list of mirrors
+ bool DownloadedMirrorFile; // already downloaded this session
+
+ bool Debug;
+
+ protected:
+ bool DownloadMirrorFile(string uri);
+ string GetMirrorFileName(string uri);
+ bool SelectMirror();
+ bool Clean(string dir);
+
+ // we need to overwrite those to transform the url back
+ virtual void Fail(string Why, bool Transient = false);
+ virtual void URIStart(FetchResult &Res);
+ virtual void URIDone(FetchResult &Res,FetchResult *Alt = 0);
+ virtual bool Configuration(string Message);
+
+ public:
+ MirrorMethod();
+ virtual bool Fetch(FetchItem *Itm);
+};
+
+
+#endif