1 # -*- coding: utf-8 -*-
2 # airpac - aria2c wrapper for pacman
3 # Copyright (C) 2009 Darwin M. Bautista
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 from tempfile
import NamedTemporaryFile
24 from subprocess
import Popen
, PIPE
27 BIN
= '/usr/bin/aria2c'
28 CONF
= '/etc/airpac.conf'
29 STATS
= '/var/lib/airpac.stats'
30 DIR
= '/var/lib/pacman/.airpac'
31 PACDIR
= '/var/lib/pacman'
33 # Obtained from /etc/rc.d/functions
34 C_MAIN
= '\033[1;37;40m' # main text
35 C_OTHER
= '\033[1;34;40m' # prefix & brackets
36 C_BUSY
= '\033[0;36;40m' # busy
37 C_FAIL
= '\033[1;31;40m' # failed
38 C_DONE
= '\033[1;37;40m' # completed
42 def log(width
, label
, msg
, cr
=True):
43 spaces
= ' ' * (width
- len(label
) - len(msg
) - 4)
50 print ' %s%s%s%s%s[%s%s%s]%s' % (C_MAIN
, label
, C_CLEAR
, spaces
, C_OTHER
, status
, msg
, C_OTHER
, C_CLEAR
),
56 def gen_input_file(uri
):
59 if not uri
.endswith('.db.tar.gz'):
60 with
open(CONF
, 'r') as conf
:
62 if line
.lstrip().startswith('split'):
63 max_mirrors
= 2 * int(line
.split('=')[1].strip())
64 fname
= os
.path
.basename(uri
)
65 base_uri
= os
.path
.dirname(uri
)
66 repo
= '[' + uri
.split('/')[-4] + ']'
69 with
open('/etc/pacman.conf', 'r') as conf
:
72 path
= line
.split('=')[-1].lstrip()
73 if line
.startswith('['):
74 found_repo
= (line
== repo
)
78 repo
= repo
.strip('[]')
81 if line
.startswith('Server'):
82 uris
.append('%s/%s\t' % (path
, fname
))
83 elif line
.startswith('Include'):
84 with
open(path
, 'r') as f
:
87 if line
.startswith('Server'):
88 path
= line
.split('=')[-1].lstrip().replace('$repo', repo
)
89 uris
.append('%s/%s\t' % (path
, fname
))
90 if len(uris
) >= max_mirrors
:
92 if len(uris
) >= max_mirrors
:
95 if base_uri
in path
and last_repo
is not None:
99 with
NamedTemporaryFile(mode
='w', prefix
='airpac-', delete
=False) as tmp
:
101 # Force 2 connections per server if the number of mirrors is less than the value of 'split'
102 mirrors
= len(''.join(uris
).split())
103 split
= max_mirrors
/ 2
104 if mirrors
< split
/ 2:
106 return tmp
.name
, str(split
)
109 def db_cache(fname
, store
=False):
110 if not os
.path
.isdir(DIR
):
120 shutil
.copy2(src
, dst
)
126 if not os
.access(BIN
, os
.X_OK
):
127 sys
.exit('aria2c not found_repo')
129 uri
, outfile
= sys
.argv
[1:]
131 sys
.exit('incorrect number of arguments')
133 outdir
= os
.path
.dirname(outfile
)
134 outfile
= os
.path
.basename(outfile
)
135 tempfile
= outfile
+ '.airpac'
136 infile
, num
= gen_input_file(uri
)
139 BIN
, '--conf-path=' + CONF
, '--remote-time=true', '--continue',
140 '--allow-overwrite=true', '--summary-interval=0', '--split=' + num
,
141 '--server-stat-if=' + STATS
, '--server-stat-of=' + STATS
,
142 '--dir=' + outdir
, '--out=' + tempfile
, '--input-file=' + infile
145 if uri
.endswith('.db.tar.gz'):
148 aria2c
= Popen(args
, stdout
=PIPE
)
150 def terminate(signum
=None, frame
=None, failed
=False):
151 asyncore
.socket_map
.clear()
154 if signum
is not None:
157 if signum
is not None or failed
:
160 signal
.signal(signal
.SIGINT
, terminate
)
161 signal
.signal(signal
.SIGTERM
, terminate
)
163 # An easy way of getting the terminal width
164 width
= int(Popen(['stty', 'size'], stdout
=PIPE
).communicate()[0].split()[1])
165 name
= os
.path
.basename(uri
).rsplit('.', 3)[0]
168 data
= aria2c
.stdout
.readline().strip()
169 if data
.startswith('[#1'):
170 log(width
, name
, data
.strip('[#1 ]'))
171 elif data
.startswith('(OK):'):
172 log(width
, name
, 'DONE', cr
=False)
174 elif data
.startswith('(ERR):'):
175 log(width
, name
, 'FAIL', cr
=False)
176 terminate(failed
=True)
178 asyncore
.file_dispatcher(aria2c
.stdout
).handle_read
= handle_read
181 if aria2c
.returncode
== 0:
182 if uri
.endswith('.db.tar.gz'):
183 db_cache(tempfile
, store
=True)
184 os
.rename(tempfile
, outfile
)