aria2: disable pre file allocation
[platform/upstream/libzypp.git] / zypp / media / MediaAria2c.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaAria2c.cc
10  *
11 */
12
13 #include <iostream>
14 #include <list>
15
16 #include "zypp/base/Logger.h"
17 #include "zypp/ExternalProgram.h"
18 #include "zypp/ProgressData.h"
19 #include "zypp/base/String.h"
20 #include "zypp/base/Gettext.h"
21 #include "zypp/base/Sysconfig.h"
22 #include "zypp/base/Gettext.h"
23 #include "zypp/ZYppCallbacks.h"
24
25 #include "zypp/Edition.h"
26 #include "zypp/Target.h"
27 #include "zypp/ZYppFactory.h"
28
29 #include "zypp/media/MediaAria2c.h"
30 #include "zypp/media/proxyinfo/ProxyInfos.h"
31 #include "zypp/media/ProxyInfo.h"
32 #include "zypp/media/MediaUserAuth.h"
33 #include "zypp/media/MediaCurl.h"
34 #include "zypp/thread/Once.h"
35 #include <cstdlib>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/mount.h>
39 #include <errno.h>
40 #include <dirent.h>
41 #include <unistd.h>
42 #include <boost/format.hpp>
43
44 #define  DETECT_DIR_INDEX       0
45 #define  CONNECT_TIMEOUT        60
46 #define  TRANSFER_TIMEOUT       60 * 3
47 #define  TRANSFER_TIMEOUT_MAX   60 * 60
48
49
50 using namespace std;
51 using namespace zypp::base;
52
53 namespace zypp
54 {
55 namespace media
56 {
57
58 Pathname MediaAria2c::_cookieFile = "/var/lib/YaST2/cookies";
59 Pathname MediaAria2c::_aria2cPath = "/usr/local/bin/aria2c";
60 std::string MediaAria2c::_aria2cVersion = "WE DON'T KNOW ARIA2C VERSION";
61
62 //check if aria2c is present in the system
63 bool
64 MediaAria2c::existsAria2cmd()
65 {
66     const char* argv[] =
67     {
68       "which",
69       "aria2c",
70       NULL
71     };
72
73     ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
74     
75     for(std::string ariaResponse( aria.receiveLine());
76         ariaResponse.length();
77         ariaResponse = aria.receiveLine())
78     {
79         // nothing
80     }
81     
82     return ( aria.close() == 0 );
83 }
84
85 /**
86  * comannd line for aria.
87  * The argument list gets passed as reference
88  * and it is filled.
89  */
90 void fillAriaCmdLine( const Pathname &ariapath,
91                       const string &ariaver,
92                       const TransferSettings &s,
93                       const Url &url,
94                       const Pathname &destination,
95                       ExternalProgram::Arguments &args )
96 {
97     args.push_back(ariapath.c_str());
98     args.push_back(str::form("--user-agent=%s", s.userAgentString().c_str()));
99     args.push_back("--summary-interval=1");
100     args.push_back("--follow-metalink=mem");
101     args.push_back("--check-integrity=true");
102     args.push_back("--file-allocation=none");
103
104     // only present in recent aria lets find out the aria version
105     vector<string> fields;    
106     // "aria2c version x.x"
107     str::split( ariaver, std::back_inserter(fields));
108     if ( fields.size() == 3 )
109     {
110         if ( Edition(fields[2]) >= Edition("1.1.2") )
111             args.push_back( "--use-head=false");
112     }
113     
114     if ( s.maxDownloadSpeed() > 0 )
115         args.push_back(str::form("--max-download-limit=%ld", s.maxDownloadSpeed()));
116     if ( s.minDownloadSpeed() > 0 )
117         args.push_back(str::form("--lowest-speed-limit=%ld", s.minDownloadSpeed()));
118
119     args.push_back(str::form("--max-tries=%ld", s.maxSilentTries()));
120
121     if ( Edition(fields[2]) < Edition("1.2.0") )
122         WAR << "aria2c is older than 1.2.0, some features may be disabled" << endl;
123     
124     // TODO make this one configurable
125     args.push_back(str::form("--max-concurrent-downloads=%ld", s.maxConcurrentConnections()));
126
127     // add the anonymous id.
128     for ( TransferSettings::Headers::const_iterator it = s.headersBegin();
129           it != s.headersEnd();
130           ++it )
131         args.push_back(str::form("--header=%s", it->c_str() ));
132         
133     args.push_back( str::form("--connect-timeout=%ld", s.timeout()));
134
135     if ( s.username().empty() )
136     {
137         if ( url.getScheme() == "ftp" )
138         {
139             // set anonymous ftp
140             args.push_back(str::form("--ftp-user=%s", "suseuser" ));
141             args.push_back(str::form("--ftp-passwd=%s", VERSION ));
142
143             string id = "yast2";
144             id += VERSION;
145             DBG << "Anonymous FTP identification: '" << id << "'" << endl;
146         }
147     }
148     else
149     {
150         if ( url.getScheme() == "ftp" )
151             args.push_back(str::form("--ftp-user=%s", s.username().c_str() ));
152         else if ( url.getScheme() == "http" ||
153                   url.getScheme() == "https" )
154             args.push_back(str::form("--http-user=%s", s.username().c_str() ));
155         
156         if ( s.password().size() )
157         {
158             if ( url.getScheme() == "ftp" )
159                 args.push_back(str::form("--ftp-passwd=%s", s.password().c_str() ));
160             else if ( url.getScheme() == "http" ||
161                       url.getScheme() == "https" )
162                 args.push_back(str::form("--http-passwd=%s", s.password().c_str() ));
163         }
164     }
165     
166     if ( s.proxyEnabled() )
167     {
168         args.push_back(str::form("--http-proxy=%s", s.proxy().c_str() ));
169         if ( ! s.proxyUsername().empty() )
170         {
171             args.push_back(str::form("--http-proxy-user=%s", s.proxyUsername().c_str() ));
172             if ( ! s.proxyPassword().empty() )
173                 args.push_back(str::form("--http-proxy-passwd=%s", s.proxyPassword().c_str() ));
174         }
175     }
176
177     if ( ! destination.empty() )
178         args.push_back(str::form("--dir=%s", destination.c_str()));
179
180     args.push_back(url.asString().c_str());
181 }
182
183 const char *const MediaAria2c::agentString()
184 {
185   // we need to add the release and identifier to the
186   // agent string.
187   // The target could be not initialized, and then this information
188   // is not available.
189   Target_Ptr target = zypp::getZYpp()->getTarget();
190
191   static const std::string _value(
192     str::form(
193        "ZYpp %s (%s) %s"
194        , VERSION
195        , MediaAria2c::_aria2cVersion.c_str()
196        , target ? target->targetDistribution().c_str() : ""
197     )
198   );
199   return _value.c_str();
200 }
201
202
203
204 MediaAria2c::MediaAria2c( const Url &      url_r,
205                       const Pathname & attach_point_hint_r )
206     : MediaCurl( url_r, attach_point_hint_r )
207 {
208   MIL << "MediaAria2c::MediaAria2c(" << url_r << ", " << attach_point_hint_r << ")" << endl;
209
210    //At this point, we initialize aria2c path
211    _aria2cPath = Pathname( whereisAria2c().asString() );
212
213    //Get aria2c version
214    _aria2cVersion = getAria2cVersion();
215 }
216
217 void MediaAria2c::attachTo (bool next)
218 {
219   MediaCurl::attachTo(next);
220   _settings.setUserAgentString(agentString());
221 }
222
223 bool
224 MediaAria2c::checkAttachPoint(const Pathname &apoint) const
225 {
226     return MediaCurl::checkAttachPoint( apoint );
227 }
228
229 void MediaAria2c::disconnectFrom()
230 {
231     MediaCurl::disconnectFrom();
232 }
233
234 void MediaAria2c::releaseFrom( const std::string & ejectDev )
235 {
236   MediaCurl::releaseFrom(ejectDev);
237 }
238
239 static Url getFileUrl(const Url & url, const Pathname & filename)
240 {
241   Url newurl(url);
242   string path = url.getPathName();
243   if ( !path.empty() && path != "/" && *path.rbegin() == '/' &&
244        filename.absolute() )
245   {
246     // If url has a path with trailing slash, remove the leading slash from
247     // the absolute file name
248     path += filename.asString().substr( 1, filename.asString().size() - 1 );
249   }
250   else if ( filename.relative() )
251   {
252     // Add trailing slash to path, if not already there
253     if (path.empty()) path = "/";
254     else if (*path.rbegin() != '/' ) path += "/";
255     // Remove "./" from begin of relative file name
256     path += filename.asString().substr( 2, filename.asString().size() - 2 );
257   }
258   else
259   {
260     path += filename.asString();
261   }
262
263   newurl.setPathName(path);
264   return newurl;
265 }
266
267 void MediaAria2c::getFile( const Pathname & filename ) const
268 {
269     // Use absolute file name to prevent access of files outside of the
270     // hierarchy below the attach point.
271     getFileCopy(filename, localPath(filename).absolutename());
272 }
273
274 void MediaAria2c::getFileCopy( const Pathname & filename , const Pathname & target) const
275 {
276   callback::SendReport<DownloadProgressReport> report;
277
278   Url fileurl(getFileUrl(_url, filename));
279
280   bool retry = false;
281
282   ExternalProgram::Arguments args;
283
284   fillAriaCmdLine(_aria2cPath, _aria2cVersion, _settings, fileurl, target.dirname(), args);
285   
286   do
287   {
288     try
289     {
290       report->start(_url, target.asString() );
291
292       ExternalProgram aria(args, ExternalProgram::Stderr_To_Stdout);
293       int nLine = 0;
294
295       //Process response
296       for(std::string ariaResponse( aria.receiveLine());
297           ariaResponse.length();
298           ariaResponse = aria.receiveLine())
299       {
300         //cout << ariaResponse;
301
302         if (!ariaResponse.substr(0,31).compare("Exception: Authorization failed") )
303         {
304             ZYPP_THROW(MediaUnauthorizedException(
305                   _url, "Login failed.", "Login failed", "auth hint"
306                 ));
307         }
308         if (!ariaResponse.substr(0,29).compare("Exception: Resource not found") )
309         {
310             ZYPP_THROW(MediaFileNotFoundException(_url, filename));
311         }
312
313         if (!ariaResponse.substr(0,9).compare("[#2 SIZE:"))
314         {
315           if (!nLine)
316           {
317             size_t left_bound = ariaResponse.find('(',0) + 1;
318             size_t count = ariaResponse.find('%',left_bound) - left_bound;
319             //cout << ariaResponse.substr(left_bound, count) << endl;
320             //progressData.toMax();
321             report->progress ( std::atoi(ariaResponse.substr(left_bound, count).c_str()), _url, -1, -1 );
322             nLine = 1;
323           }
324           else
325           {
326             nLine = 0;
327           }
328         }
329       }
330
331       aria.close();
332
333       report->finish( _url ,  zypp::media::DownloadProgressReport::NO_ERROR, "");
334       retry = false;
335     }
336
337     // retry with proper authentication data
338     catch (MediaUnauthorizedException & ex_r)
339     {
340       if(authenticate(ex_r.hint(), !retry))
341         retry = true;
342       else
343       {
344         report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserHistory());
345         ZYPP_RETHROW(ex_r);
346       }
347
348     }
349     // unexpected exception
350     catch (MediaException & excpt_r)
351     {
352       // FIXME: error number fix
353       report->finish(fileurl, zypp::media::DownloadProgressReport::ERROR, excpt_r.asUserHistory());
354       ZYPP_RETHROW(excpt_r);
355     }
356   }
357   while (retry);
358
359   report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
360 }
361
362 bool MediaAria2c::getDoesFileExist( const Pathname & filename ) const
363 {
364     return MediaCurl::getDoesFileExist(filename);
365 }
366
367 bool MediaAria2c::doGetDoesFileExist( const Pathname & filename ) const
368 {
369     return MediaCurl::doGetDoesFileExist(filename);
370 }
371     
372 void MediaAria2c::getDir( const Pathname & dirname, bool recurse_r ) const
373 {
374     MediaCurl::getDir(dirname, recurse_r);
375 }
376
377 bool MediaAria2c::authenticate(const std::string & availAuthTypes, bool firstTry) const
378 {
379     return false;
380 }
381
382
383 void MediaAria2c::getDirInfo( std::list<std::string> & retlist,
384                                const Pathname & dirname, bool dots ) const
385 {
386   getDirectoryYast( retlist, dirname, dots );
387 }
388
389 void MediaAria2c::getDirInfo( filesystem::DirContent & retlist,
390                             const Pathname & dirname, bool dots ) const
391 {
392   getDirectoryYast( retlist, dirname, dots );
393 }
394
395 std::string MediaAria2c::getAria2cVersion()
396 {
397     const char* argv[] =
398     {
399         _aria2cPath.c_str(),
400       "--version",
401       NULL
402     };
403
404     ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
405
406     std::string vResponse = aria.receiveLine();
407     aria.close();
408     return str::trim(vResponse);
409 }
410
411 #define ARIA_DEFAULT_BINARY "/usr/bin/aria2c"
412
413 Pathname MediaAria2c::whereisAria2c()
414 {
415     Pathname aria2cPathr(ARIA_DEFAULT_BINARY);
416
417     const char* argv[] =
418     {
419       "which",
420       "aria2c",
421       NULL
422     };
423
424     ExternalProgram aria(argv, ExternalProgram::Stderr_To_Stdout);
425
426     std::string ariaResponse( aria.receiveLine());
427     int code = aria.close();
428
429     if( code == 0 )
430     {
431         aria2cPathr = str::trim(ariaResponse);
432         MIL << "We will use aria2c located here:  " << aria2cPathr << endl;
433     }
434     else
435     {
436         MIL << "We don't know were is ari2ac binary. We will use aria2c located here:  " << aria2cPathr << endl;
437     }
438
439     return aria2cPathr;
440 }
441
442 } // namespace media
443 } // namespace zypp
444 //