bmc-tools 0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin-other/autobackup +70 -0
- data/bin-other/autobackup.inc +157 -0
- data/bin-other/bob/ExtractPagesFromPDF +0 -0
- data/bin-other/bob/backupsync +46 -0
- data/bin-other/bob/cachesync +58 -0
- data/bin-other/bob/deb +55 -0
- data/bin-other/bob/debmirror +2551 -0
- data/bin-other/bob/debmirror.marlin +2551 -0
- data/bin-other/bob/exif_rotate.sh +34 -0
- data/bin-other/bob/exif_rotate_dates.sh +35 -0
- data/bin-other/bob/git-big-objects +85 -0
- data/bin-other/bob/git-commit-details +22 -0
- data/bin-other/bob/git-commit-sizes +16 -0
- data/bin-other/bob/git-show-biggest.sh +33 -0
- data/bin-other/bob/git_remove_history.sh +24 -0
- data/bin-other/bob/git_staged_status.sh +29 -0
- data/bin-other/bob/identify_extra_raws +137 -0
- data/bin-other/bob/wallpaper_restore.sh +1 -0
- data/bin-other/bob/watch_olsr.sh +1 -0
- data/bin-other/bob/watch_rbpm_node_status +1 -0
- data/bin-other/bob/watermark_bmphoto_large.sh +32 -0
- data/bin-other/bob/watermark_bmphoto_small.sh +32 -0
- data/bin-other/bubbles/deb +42 -0
- data/bin-other/bubbles/firewall.sh +134 -0
- data/bin-other/bubbles/kernel-mirror.sh +15 -0
- data/bin-other/deb +42 -0
- data/bin-other/exif_dates.sh +35 -0
- data/bin-other/git-large-files +62 -0
- data/bin-other/git_add_upto.sh +54 -0
- data/bin-other/git_find_big.sh +33 -0
- data/bin-other/git_staged_status.sh +29 -0
- data/bin-other/image_resize +43 -0
- data/bin-other/kernel-mirror.sh +15 -0
- data/bin-other/marlin/deb +42 -0
- data/bin-other/marlin/firewall.sh +134 -0
- data/bin-other/mysql2svn.sh +36 -0
- data/bin-other/syno-cleanup.sh +31 -0
- data/bin/dockerize +35 -23
- data/bin/image_exif +30 -0
- data/bin/image_process +156 -0
- data/bin/image_process_wname +138 -0
- data/bin/tgv_to_pdf +206 -0
- data/bmc-tools.gemspec +1 -1
- data/lib/cli.rb +8 -0
- data/lib/constants.rb +1 -0
- data/lib/docker.rb +15 -0
- data/lib/git.rb +21 -0
- data/lib/runner.rb +19 -0
- metadata +52 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 585ca55e79ca690c1970ceabb2500440f12c3b4e
|
4
|
+
data.tar.gz: adc8f4f7cabd9c44d3822d29d7e4b5bd74b8d2ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: caa10b3b8a000bdf44336ff8f7872294f8e8e7776b72f22729dc53c506f44bd27e2430cb4ac05fd55d25639e97636ac4391f8b0c55c1ee7a28d541f11bd07bdb
|
7
|
+
data.tar.gz: 255678b5358f59853eef8f5e94affebd9e2b37ab40987c95dd5d97330299fb90a323291653b493a814286434f8d3aa88e0e79ec12d6bfd922cacbc447bcd9196
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
##
|
4
|
+
## Idée de feature: apellé sans "group", ne fait que les update rsync => utile pour
|
5
|
+
## sync régulièrement et alléger le snapshot
|
6
|
+
##
|
7
|
+
#set -x
|
8
|
+
|
9
|
+
# Global config
|
10
|
+
BACKUPDIR="/volume1/backup/autobackup/"
|
11
|
+
RSYNC_OPT="--links --times --recursive --delete --hard-links --chmod=g+rw --exclude '.svn' --delete-excluded"
|
12
|
+
RSYNC_VERBOSE=0
|
13
|
+
RSYNC_COMPRESS=1
|
14
|
+
RSYNC_BWLIMIT=0
|
15
|
+
|
16
|
+
MYSQL_OPT="-e --lock-tables --complete-insert --extended-insert --hex-blob "
|
17
|
+
|
18
|
+
MYSQL_REMOTE_TEMPFILE="/tmp/mysqldump.sql.tmp"
|
19
|
+
LOGFILE="$BACKUPDIR/backup.log"
|
20
|
+
|
21
|
+
# Basic startup
|
22
|
+
. $(dirname $0)/autobackup.inc
|
23
|
+
group="$1"
|
24
|
+
dfull=$(date +%Y%m%d-%H%M%S)
|
25
|
+
dyear=$(date +%Y)
|
26
|
+
dmonth=$(date +%m\ %B)
|
27
|
+
dday=$(date +%d\ %A)
|
28
|
+
if [ -z "$group" ] ; then
|
29
|
+
terminate "must specify group name"
|
30
|
+
exit
|
31
|
+
fi
|
32
|
+
|
33
|
+
# Define the file creation mask to rw-rw----
|
34
|
+
umask 007
|
35
|
+
|
36
|
+
|
37
|
+
# backup_rsync bob.etc bob::etc
|
38
|
+
# exit 0
|
39
|
+
|
40
|
+
# ESXi datastore
|
41
|
+
#backup_rsync chum.esxi rsync://chum.bmconseil.com/ds
|
42
|
+
#exit 0
|
43
|
+
|
44
|
+
# SVN & GIT marlin -> bubbles
|
45
|
+
backup_rsync bubbles.git rsync://fo1.bmconseil.com:5061/git
|
46
|
+
backup_rsync bloat.svn rsync://localhost/svn
|
47
|
+
|
48
|
+
# mail without archives
|
49
|
+
backup_rsync bubbles.mail-light rsync://fo1.bmconseil.com:5061/mail-light
|
50
|
+
|
51
|
+
# nemo: config, /opt, photo
|
52
|
+
#backup_rsync nemo.etc localhost::etc
|
53
|
+
#backup_rsync nemo.opt localhost::opt
|
54
|
+
#backup_rsync nemo.photo localhost::photo
|
55
|
+
#backup_rsync nemo.raid localhost::raid
|
56
|
+
|
57
|
+
# Configuration
|
58
|
+
#backup_rsync marlin.etc marlin.bmconseil.com::etc
|
59
|
+
#backup_rsync bubbles.etc rsync://pearl.bmconseil.com:8730/etc
|
60
|
+
#backup_rsync crush.etc crush.bmconseil.com::etc
|
61
|
+
|
62
|
+
# databases
|
63
|
+
#backup_mysql crush.db backup@crush.bmconseil.com backup
|
64
|
+
#backup_mysql marlin.db backup@marlin.bmconseil.com backup
|
65
|
+
|
66
|
+
# mail archives
|
67
|
+
backup_rsync bubbles.mail-arch rsync://fo1.bmconseil.com:5061/mail-arch
|
68
|
+
|
69
|
+
# ESXi datastore
|
70
|
+
backup_rsync chum.esxi rsync://chum.bmconseil.com/ds
|
@@ -0,0 +1,157 @@
|
|
1
|
+
backup_rsync () {
|
2
|
+
# initializing
|
3
|
+
local name="$1"
|
4
|
+
local source="$2"
|
5
|
+
local rsyncopt_common="$RSYNC_OPT"
|
6
|
+
local rsyncopt_remote="$RSYNC_OPT --progress"
|
7
|
+
local latest_dir="$BACKUPDIR/$name/latest"
|
8
|
+
local target_dir="$BACKUPDIR/$name/$group/$(date +%Y.%m\ %B)/$name.$dfull"
|
9
|
+
local temp_dir="$target_dir.tmp"
|
10
|
+
|
11
|
+
status ""
|
12
|
+
status "######################################################"
|
13
|
+
status "## BACKUP"
|
14
|
+
status "## name $name"
|
15
|
+
status "## group $group"
|
16
|
+
status "## source $source"
|
17
|
+
status "## snap $latest_dir"
|
18
|
+
status "## target $target_dir"
|
19
|
+
status "######################################################"
|
20
|
+
|
21
|
+
# rsync parameters
|
22
|
+
if [ $RSYNC_VERBOSE = 1 ]; then
|
23
|
+
rsyncopt_remote="$rsyncopt_remote --verbose --progress"
|
24
|
+
fi
|
25
|
+
if [ $RSYNC_COMPRESS = 1 ]; then
|
26
|
+
rsyncopt_remote="$rsyncopt_remote --compress"
|
27
|
+
fi
|
28
|
+
if [ $RSYNC_BWLIMIT != 0 ]; then
|
29
|
+
rsyncopt_remote="$rsyncopt_remote --bwlimit=$RSYNC_BWLIMIT"
|
30
|
+
fi
|
31
|
+
|
32
|
+
# Make target dir
|
33
|
+
status "making temp directory <$temp_dir>"
|
34
|
+
mkdir -p "$temp_dir" ||
|
35
|
+
#terminate "error creating target directory" || return;
|
36
|
+
|
37
|
+
# Update local snapshot
|
38
|
+
status "snapshotting $name/$group ($source)... "
|
39
|
+
echo -n "Snapshotting $name/$group ($source)... "
|
40
|
+
echo -n "updating... "
|
41
|
+
#echo -n "updating [rsync <$source> <$latest_dir>] "
|
42
|
+
(rsync "$source" "$latest_dir" $rsyncopt_remote >> $LOGFILE) ||
|
43
|
+
terminate "snapshot: rsync error" || return
|
44
|
+
|
45
|
+
# make final snapshot
|
46
|
+
status "replicating final snapshot"
|
47
|
+
echo -n "snapshotting... "
|
48
|
+
#echo -n "snapshotting [rsync <$latest_dir> <$temp_dir> ] "
|
49
|
+
mkdir -p "$temp_dir" ||
|
50
|
+
terminate "error creating target dir" || return
|
51
|
+
# > $LOGFILE
|
52
|
+
(rsync "$latest_dir"/ --link-dest="$latest_dir"/ "$temp_dir"/ $rsyncopt_common) ||
|
53
|
+
terminate "replication: rsync error" || return
|
54
|
+
|
55
|
+
# renaming final snapshot
|
56
|
+
status "renaming final snapshot"
|
57
|
+
echo -n "renaming... "
|
58
|
+
#echo -n "renaming [mv <$temp_dir> <$target_dir> ] "
|
59
|
+
mv "$temp_dir" "$target_dir"
|
60
|
+
|
61
|
+
|
62
|
+
# end
|
63
|
+
status "terminated"
|
64
|
+
echo "done!"
|
65
|
+
return 0;
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
backup_mysql () {
|
70
|
+
# initializing
|
71
|
+
local name="$1"
|
72
|
+
local target="$2"
|
73
|
+
local user="$3"
|
74
|
+
local extra="$4"
|
75
|
+
#local latest_dir="$BACKUPDIR/$name/latest"
|
76
|
+
#local target_dir="$BACKUPDIR/$name/$group/$(date +%Y.%m\ %B)/$name.$dfull"
|
77
|
+
#local temp_dir="$target_dir.tmp"
|
78
|
+
local temp_file="/tmp/mysqldump-$(date +%Y%m%d-%H%M%S).tmp"
|
79
|
+
local target_file="`getbackupbase $name $group`.sql"
|
80
|
+
local target_dir="`dirname \"$target_file\"`"
|
81
|
+
|
82
|
+
|
83
|
+
status ""
|
84
|
+
status "######################################################"
|
85
|
+
status "## BACKUP"
|
86
|
+
status "## name $name"
|
87
|
+
status "## group $group"
|
88
|
+
status "## user $user"
|
89
|
+
status "## dir $target_dir"
|
90
|
+
status "## temp $temp_file"
|
91
|
+
status "## target $target_file"
|
92
|
+
status "######################################################"
|
93
|
+
#return;
|
94
|
+
|
95
|
+
# Make target dir
|
96
|
+
status "making target directory"
|
97
|
+
mkdir -p "$target_dir" ||
|
98
|
+
terminate "error creating target directory" || return;
|
99
|
+
|
100
|
+
# dump database
|
101
|
+
echo -n "Dumping database $name/$group... "
|
102
|
+
echo -n "dumping... "
|
103
|
+
status "dumping remote database"
|
104
|
+
(ssh -C $target mysqldump -c -e -A -u $user $MYSQL_OPT > $temp_file) ||
|
105
|
+
terminate "mysqldump error" || return
|
106
|
+
|
107
|
+
# rename temp file
|
108
|
+
echo -n "renaming... "
|
109
|
+
status "renaming temp file"
|
110
|
+
(mv "$temp_file" "$target_file") ||
|
111
|
+
terminate "rename error" || return
|
112
|
+
|
113
|
+
# compress the dump
|
114
|
+
status "compressing file"
|
115
|
+
echo -n "compressing... "
|
116
|
+
bzip2 "$target_file" ||
|
117
|
+
terminate "bzip2 failed" || return
|
118
|
+
|
119
|
+
# end
|
120
|
+
status "terminated"
|
121
|
+
echo "done."
|
122
|
+
return 0;
|
123
|
+
}
|
124
|
+
|
125
|
+
|
126
|
+
getbackupbase () {
|
127
|
+
dfull=$(date +%Y%m%d-%H%M%S)
|
128
|
+
dyear=$(date +%Y)
|
129
|
+
dmonth=$(date +%m\ %B)
|
130
|
+
dday=$(date +%d\ %A)
|
131
|
+
name=$1
|
132
|
+
group=$2
|
133
|
+
echo "$BACKUPDIR/$name/$group/$(date +%Y.%m\ %B)/$name.$(date +%Y%m%d-%H%M%S)"
|
134
|
+
|
135
|
+
}
|
136
|
+
|
137
|
+
|
138
|
+
terminate () {
|
139
|
+
echo "$0 quitting: $1"
|
140
|
+
return 1;
|
141
|
+
}
|
142
|
+
|
143
|
+
status () {
|
144
|
+
echo "* $1" >> $LOGFILE
|
145
|
+
}
|
146
|
+
|
147
|
+
tunnel () {
|
148
|
+
remote=$1
|
149
|
+
localport=$2
|
150
|
+
status "Building SSH tunnel ($remote, $localport)"
|
151
|
+
ssh $remote -L:$localport:localhost:873 -C -o BatchMode=yes -f sleep 10
|
152
|
+
if [ "${?}" -ne "0" ]; then
|
153
|
+
terminate "ssh tunnel: unable to establish tunnel"
|
154
|
+
exit 1
|
155
|
+
fi
|
156
|
+
}
|
157
|
+
|
Binary file
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
set -er
|
3
|
+
TASK=$1
|
4
|
+
|
5
|
+
# Config
|
6
|
+
LOCAL="/Users/bruno/backup/"
|
7
|
+
EXTRA=""
|
8
|
+
RSYNC="rsync --recursive --times --progress --links --hard-links --fuzzy --delete-before --delete-excluded --exclude @eaDir"
|
9
|
+
|
10
|
+
# Do the job !
|
11
|
+
|
12
|
+
case $TASK in
|
13
|
+
|
14
|
+
svn|git|mail)
|
15
|
+
REMOTE=marlin.bmconseil.com
|
16
|
+
;;
|
17
|
+
|
18
|
+
# svn|git)
|
19
|
+
# REMOTE=marlin.bmconsel
|
20
|
+
# ;;
|
21
|
+
|
22
|
+
photo)
|
23
|
+
REMOTE=nemo
|
24
|
+
EXTRA="\
|
25
|
+
--exclude photos1997 --exclude photos1998 --exclude photos1999 \
|
26
|
+
--exclude photos2000 --exclude photos2001 --exclude photos2002 \
|
27
|
+
--exclude photos2003 --exclude photos2004 --exclude photos2006 \
|
28
|
+
--exclude photos2007 \
|
29
|
+
"
|
30
|
+
;;
|
31
|
+
|
32
|
+
*)
|
33
|
+
echo "usage: `basename $0` {svn|git|mail}"
|
34
|
+
exit 1
|
35
|
+
;;
|
36
|
+
esac
|
37
|
+
|
38
|
+
|
39
|
+
echo "####################################################"
|
40
|
+
echo "Syncing <$TASK> ..."
|
41
|
+
echo "####################################################"
|
42
|
+
$RSYNC "$REMOTE"::"$TASK"/ "$LOCAL/$TASK" $EXTRA
|
43
|
+
echo "####################################################"
|
44
|
+
echo "Done !"
|
45
|
+
echo "####################################################"
|
46
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
set -er
|
3
|
+
TASK=$1
|
4
|
+
|
5
|
+
# Config
|
6
|
+
LOCALDIR="/Users/bruno/cache/"
|
7
|
+
EXTRA=""
|
8
|
+
EXCLUDE_BASE="/Users/bruno/cache/exclude-"
|
9
|
+
RSYNC="rsync --recursive --times --progress --links --hard-links --fuzzy --delete-before --iconv=UTF8-MAC,UTF-8 --exclude @eaDir --exclude '.git' --delete-excluded --itemize-changes"
|
10
|
+
#RSYNC="rsync --recursive --times --progress --links --hard-links --fuzzy --delete-before --exclude @eaDir --exclude '.git' --delete-excluded"
|
11
|
+
MODULE=$TASK
|
12
|
+
|
13
|
+
# Do the job with defaults !
|
14
|
+
LOCALPART="$TASK"
|
15
|
+
#MODULE="$TASK"
|
16
|
+
TARGET="${LOCALDIR}/${TASK}"
|
17
|
+
|
18
|
+
case ${TASK} in
|
19
|
+
bsvn|bgit)
|
20
|
+
REMOTE="nemo::${TASK}"
|
21
|
+
;;
|
22
|
+
|
23
|
+
photo|debian|music|misc)
|
24
|
+
REMOTE="nemo::${TASK}"
|
25
|
+
;;
|
26
|
+
|
27
|
+
msvn|mgit)
|
28
|
+
REMOTE="marlin.bmconseil.com::${TASK}"
|
29
|
+
;;
|
30
|
+
|
31
|
+
#dmusic)
|
32
|
+
# REMOTE="nemo::music"
|
33
|
+
# TARGET="/Volumes/DESIRE-32GB/BRUNO/music-clean/"
|
34
|
+
# ;;
|
35
|
+
|
36
|
+
*)
|
37
|
+
echo "usage: `basename $0` {photo|misc|music|debian|bsvn|bgit|dmusic}"
|
38
|
+
exit 1
|
39
|
+
;;
|
40
|
+
esac
|
41
|
+
|
42
|
+
# Add exclude files if exists, WARNING: path and task name should be filename-safe
|
43
|
+
#EXCLUDES=$(echo "$EXCLUDE_BASE$TASK" | sed -e "s/\ /\\\\\ /g")
|
44
|
+
EXCLUDES="$EXCLUDE_BASE$TASK"
|
45
|
+
if [ -f "$EXCLUDES" ]; then
|
46
|
+
EXTRA="$EXTRA --exclude-from $EXCLUDES"
|
47
|
+
fi
|
48
|
+
|
49
|
+
# Now, do the job!
|
50
|
+
#set -x
|
51
|
+
echo "####################################################"
|
52
|
+
echo "Syncing <$TASK> = <$REMOTE::$MODULE> ..."
|
53
|
+
echo "####################################################"
|
54
|
+
#echo EXTRA: $EXTRA
|
55
|
+
$RSYNC "${REMOTE}"/ "${TARGET}"/ $EXTRA
|
56
|
+
echo "####################################################"
|
57
|
+
echo "Done !"
|
58
|
+
echo "####################################################"
|
data/bin-other/bob/deb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
set -ere
|
3
|
+
TASK=$1
|
4
|
+
HOST=$2
|
5
|
+
|
6
|
+
|
7
|
+
# Config
|
8
|
+
#HTTP_MIRROR=gulus.usherbrooke.ca
|
9
|
+
#HTTP_MIRROR=non-us.debian.org
|
10
|
+
#HTTP_MIRROR=ftp.proxad.net
|
11
|
+
HTTP_MIRROR=mir2.ovh.net
|
12
|
+
#HTTP_MIRROR=ftp.thaios.net
|
13
|
+
#HTTP_MIRROR=ftp.be.debian.org
|
14
|
+
#HTTP_MIRROR=ftp.fr.debian.org
|
15
|
+
#HTTP_MIRROR=debian.ens-cachan.fr
|
16
|
+
LOCALDIR=/Users/bruno/cache/debian/
|
17
|
+
#LOCALDIR=/Volumes/debian_nfs/
|
18
|
+
RSYNC_UPDATE_ATTRIBS="--recursive --times --progress --delete --links --copy-dirlinks --hard-links --fuzzy --delete-before"
|
19
|
+
|
20
|
+
# Do the job !
|
21
|
+
umask 002
|
22
|
+
case $TASK in
|
23
|
+
|
24
|
+
from)
|
25
|
+
rsync "$HOST"::debian/ "$LOCALDIR" $RSYNC_UPDATE_ATTRIBS
|
26
|
+
;;
|
27
|
+
|
28
|
+
to)
|
29
|
+
rsync "$LOCALDIR" "$HOST"::debian/ $RSYNC_UPDATE_ATTRIBS
|
30
|
+
;;
|
31
|
+
|
32
|
+
update | up)
|
33
|
+
debmirror "$LOCALDIR" \
|
34
|
+
--host=$HTTP_MIRROR \
|
35
|
+
--method=http \
|
36
|
+
--progress --cleanup --no-source --getcontents \
|
37
|
+
--dist=testing,unstable,experimental -arch=amd64 \
|
38
|
+
--exclude-deb-section=embedded,hamradio \
|
39
|
+
--section=main,contrib,non-free,non-us,main/debian-installer \
|
40
|
+
--ignore-release-gpg \
|
41
|
+
--exclude='openarena.*' \
|
42
|
+
--exclude='supertuxkart' \
|
43
|
+
--exclude='wesnoth'
|
44
|
+
;;
|
45
|
+
#--ignore-release-gpg --pdiff=mirror -\
|
46
|
+
|
47
|
+
*)
|
48
|
+
echo "usage: $0 {update|up|from|to} {nemo|hector|geantvert}"
|
49
|
+
exit 1
|
50
|
+
;;
|
51
|
+
esac
|
52
|
+
|
53
|
+
# --dist=testing,unstable,lenny,sid/non-US -arch=i386 \
|
54
|
+
|
55
|
+
|
@@ -0,0 +1,2551 @@
|
|
1
|
+
#!/usr/bin/perl -w
|
2
|
+
|
3
|
+
# TODO: I'd like to be able to tell it to get some extra files, by name.
|
4
|
+
# I'm thinking Contents files. It would be really nice if it could pull
|
5
|
+
# a whole directory -- think project/trace, or disks-i386...
|
6
|
+
# TODO: It would probably be cleaner and easier to learn if it took
|
7
|
+
# apt-style lines to tell where to mirror from and what portions to use.
|
8
|
+
|
9
|
+
=head1 NAME
|
10
|
+
|
11
|
+
debmirror - Debian partial mirror script, with ftp, http, hftp or
|
12
|
+
rsync and package pool support
|
13
|
+
|
14
|
+
=head1 SYNOPSIS
|
15
|
+
|
16
|
+
debmirror [options] <mirrordir>
|
17
|
+
|
18
|
+
=head1 DESCRIPTION
|
19
|
+
|
20
|
+
This program downloads and maintains a partial local Debian mirror. It can
|
21
|
+
mirror any combination of architectures, distributions, and sections. Files
|
22
|
+
are transferred by ftp, and package pools are fully supported. It also does
|
23
|
+
locking and updates trace files.
|
24
|
+
|
25
|
+
To support package pools, this program mirrors in three steps.
|
26
|
+
|
27
|
+
=over 4
|
28
|
+
|
29
|
+
=item 1. download Packages and Sources files
|
30
|
+
|
31
|
+
First it downloads all Packages and Sources files for the subset of Debian it
|
32
|
+
was instructed to get.
|
33
|
+
|
34
|
+
=item 2. clean up unknown files
|
35
|
+
|
36
|
+
Any files and directories on the local mirror that are not in the list are
|
37
|
+
removed.
|
38
|
+
|
39
|
+
=item 3. download everything else
|
40
|
+
|
41
|
+
The Packages and Sources files are scanned, to build up a list of all the
|
42
|
+
files they refer to. A few other miscellaneous files are added to the list.
|
43
|
+
Then the program makes sure that each file in the list is present on the
|
44
|
+
local mirror and is up-to-date, using file size (and optionally md5sum) checks.
|
45
|
+
Any necessary files are downloaded.
|
46
|
+
|
47
|
+
=back
|
48
|
+
|
49
|
+
=cut
|
50
|
+
|
51
|
+
sub usage {
|
52
|
+
warn join(" ", @_)."\n" if @_;
|
53
|
+
warn <<EOF;
|
54
|
+
Usage: $0 [--progress] [--verbose] [--debug] [--dry-run] [--help]
|
55
|
+
[--host=remotehost] [--root=directory]
|
56
|
+
[--method=ftp|hftp|http|rsync] [--passive]
|
57
|
+
[--user=remoteusername] [--passwd=remoteuserpassword]
|
58
|
+
[--proxy=http://user:pass\@url:port/]
|
59
|
+
[--dist=foo[,bar,..] ...] [--omit-suite-symlinks]
|
60
|
+
[--section=foo[,bar,..] ...] [--arch=foo[,bar,..] ...]
|
61
|
+
[--adddir=directory] [--rsync-extra=foo[,bar,..] ...]
|
62
|
+
[--di-dist=foo[,bar,..] ...] [--di-arch=foo[,bar,..] ...]
|
63
|
+
[--source|--nosource] [--i18n] [--getcontents] [--md5sums]
|
64
|
+
[--ignore-missing-release] [--ignore-release-gpg]
|
65
|
+
[--ignore=regex] [--exclude=regex] [--include=regex]
|
66
|
+
[--exclude-deb-section=regex] [--limit-priority=regex]
|
67
|
+
[--timeout=seconds] [--max-batch=number]
|
68
|
+
[--rsync-batch=number] [--rsync-options=options]
|
69
|
+
[--postcleanup|--cleanup|--nocleanup] [--skippackages]
|
70
|
+
[--diff=use|mirror|none] [--state-cache-days=number]
|
71
|
+
[--ignore-small-errors] [--allow-dist-rename]
|
72
|
+
<mirrordir>
|
73
|
+
|
74
|
+
For details, see man page.
|
75
|
+
EOF
|
76
|
+
exit(1);
|
77
|
+
}
|
78
|
+
|
79
|
+
=head1 OPTIONS
|
80
|
+
|
81
|
+
=over 4
|
82
|
+
|
83
|
+
=item <mirrordir>
|
84
|
+
|
85
|
+
This required parameter specifies where the local mirror directory is. If the
|
86
|
+
directory does not exist, it will be created. Be careful; telling this
|
87
|
+
program that your home directory is the mirrordir is guaranteed to replace
|
88
|
+
your home directory with a Debian mirror!
|
89
|
+
|
90
|
+
=item --progress -p
|
91
|
+
|
92
|
+
Displays progress bars as files are downloaded.
|
93
|
+
|
94
|
+
=item --verbose -v
|
95
|
+
|
96
|
+
Displays progress between file downloads.
|
97
|
+
|
98
|
+
=item --debug
|
99
|
+
|
100
|
+
Enables verbose debug output, including ftp protocol dump.
|
101
|
+
|
102
|
+
=item --dry-run
|
103
|
+
|
104
|
+
Simulate a mirror run. This will still download the meta files to the
|
105
|
+
F<./.temp> working directory, but won't replace the old meta files, won't
|
106
|
+
download debs and source files and only simulates cleanup.
|
107
|
+
|
108
|
+
=item --help
|
109
|
+
|
110
|
+
Display a usage summary.
|
111
|
+
|
112
|
+
=item --host=remotehost -h
|
113
|
+
|
114
|
+
Specify the remote host to mirror from. Defaults to 'ftp.debian.org',
|
115
|
+
you are strongly encouraged to find a closer mirror.
|
116
|
+
|
117
|
+
=item --root=directory -r directory
|
118
|
+
|
119
|
+
Specifies the directory on the remote host that is the root of the Debian
|
120
|
+
archive. Defaults to "debian", which will work for most mirrors. The root
|
121
|
+
directory has a F<./dists> subdirectory.
|
122
|
+
|
123
|
+
=item --method=ftp|hftp|http|rsync -e
|
124
|
+
|
125
|
+
Specify the method to download files. Currently, supported methods are
|
126
|
+
ftp, hftp (ftp over http proxy), http or rsync.
|
127
|
+
|
128
|
+
Note: starting with version 1.1 it is no longer needed to add a ':' prefix
|
129
|
+
for the root directory.
|
130
|
+
|
131
|
+
=item --passive
|
132
|
+
|
133
|
+
Download in passive mode.
|
134
|
+
|
135
|
+
=item --user=remoteusername -u
|
136
|
+
|
137
|
+
Specify the remote user name to use to log to the remote host. Helpful when
|
138
|
+
dealing with brain damaged proxy servers. Defaults to anonymous.
|
139
|
+
|
140
|
+
=item --passwd=remoteuserpassword
|
141
|
+
|
142
|
+
Specify the remote user password to use to log into the remote ftp host.
|
143
|
+
It is used with --user and defaults to anonymous@.
|
144
|
+
|
145
|
+
=item --proxy=http://user:pass@url:port/
|
146
|
+
|
147
|
+
Specifies the http proxy (like Squid) to use for http and hftp method.
|
148
|
+
|
149
|
+
=item --dist=foo[,bar,..] -d foo
|
150
|
+
|
151
|
+
Specify the distribution (etch, lenny, squeeze, sid) of Debian to
|
152
|
+
mirror. This switch may be used multiple times, and multiple
|
153
|
+
distributions may be specified at once, separated by commas. Using the
|
154
|
+
links (stable, testing, unstable) does not have the expected results
|
155
|
+
but you may add those links manually. Defaults to mirroring sid.
|
156
|
+
|
157
|
+
=item --omit-suite-symlinks
|
158
|
+
|
159
|
+
With this option set, debmirror will not create the 'S<suite -E<gt> codename>'
|
160
|
+
symlink. This is needed for example when mirroring archived Debian
|
161
|
+
releases as they will all have either 'stable' or 'oldstable' as
|
162
|
+
suite in their F<Release> files.
|
163
|
+
|
164
|
+
=item --section=foo[,bar,..] -s foo
|
165
|
+
|
166
|
+
Specify the section of Debian to mirror. Defaults to
|
167
|
+
main,contrib,non-free,main/debian-installer.
|
168
|
+
|
169
|
+
=item --arch=foo[,bar,..] -a foo
|
170
|
+
|
171
|
+
Specify the architectures to mirror. The default is --arch=i386.
|
172
|
+
Specifying --arch=none will mirror no archs.
|
173
|
+
|
174
|
+
=item --adddir directory
|
175
|
+
|
176
|
+
Also download Packages and Sources files from the specified directory
|
177
|
+
on the remote host (the directory is relative to the root of the
|
178
|
+
Debian archive). This feature is now obsolete and may be removed in
|
179
|
+
a future release.
|
180
|
+
|
181
|
+
=item --rsync-extra=foo[,bar,..]
|
182
|
+
|
183
|
+
Allows to also mirror files from a number of directories that are not part
|
184
|
+
of the package archive itself. Debmirror will B<always> use rsync for the
|
185
|
+
transfer of these files, irrespective of what transfer method is specified
|
186
|
+
in the --method option.
|
187
|
+
|
188
|
+
This option can therefore not be used if your remote mirror does not support
|
189
|
+
rsync, or if the mirror needs a different --root option for rsync than for
|
190
|
+
the main transfer method specified with --method. Excluding individual files
|
191
|
+
in the directories is not supported.
|
192
|
+
|
193
|
+
The following values are supported.
|
194
|
+
|
195
|
+
=over 2
|
196
|
+
|
197
|
+
=item doc
|
198
|
+
|
199
|
+
Download all files and subdirectories in F<./doc> directory, and all README
|
200
|
+
files in the root directory of the archive.
|
201
|
+
|
202
|
+
=item indices
|
203
|
+
|
204
|
+
Download all files and subdirectories in F<./indices> directory. Note that
|
205
|
+
this directory can contain some rather large files; don't include this
|
206
|
+
type unless you know you need these files.
|
207
|
+
|
208
|
+
=item tools
|
209
|
+
|
210
|
+
Download all files and subdirectories in F<./tools> directory.
|
211
|
+
|
212
|
+
=item trace
|
213
|
+
|
214
|
+
Download the remote mirror's trace files for the archive (F<./project/trace/*>).
|
215
|
+
|
216
|
+
=back
|
217
|
+
|
218
|
+
If specified, the update of trace files will be done at the beginning of
|
219
|
+
the mirror run; the other types are done near the end.
|
220
|
+
|
221
|
+
This switch may be used multiple times, and multiple values may be specified
|
222
|
+
at once, separated by comma's; unknown values are ignored.
|
223
|
+
|
224
|
+
=item --di-dist=dists | foo[,bar,..]
|
225
|
+
|
226
|
+
Mirror "current" Debian Installer images for the specified dists.
|
227
|
+
See further the section L<Mirroring Debian Installer images> below.
|
228
|
+
|
229
|
+
=item --di-arch=arches | foo[,bar,..]
|
230
|
+
|
231
|
+
Mirror "current" Debian Installer images for the specified architectures.
|
232
|
+
See further the section L<Mirroring Debian Installer images> below.
|
233
|
+
|
234
|
+
=item --source
|
235
|
+
|
236
|
+
Include source in the mirror (default).
|
237
|
+
|
238
|
+
=item --nosource
|
239
|
+
|
240
|
+
Do not include source.
|
241
|
+
|
242
|
+
=item --i18n
|
243
|
+
|
244
|
+
Additionally download F<Translation-E<lt>langE<gt>.bz2> files, which contain
|
245
|
+
translations of package descriptions. Selection of specific translations is
|
246
|
+
possible using the --include and --exclude options.
|
247
|
+
|
248
|
+
=item --getcontents
|
249
|
+
|
250
|
+
Additionally download F<Contents.E<lt>archE<gt>.gz> files. Note that these
|
251
|
+
files can be relatively big and can change frequently, especially for the
|
252
|
+
testing and unstable suites. Use of the available diff files is strongly
|
253
|
+
recommended (see the --diff option).
|
254
|
+
|
255
|
+
=item --md5sums -m
|
256
|
+
|
257
|
+
Use md5sums to determine if files on the local mirror that are the correct
|
258
|
+
size actually have the correct content. Not enabled by default, because
|
259
|
+
it is too paranoid, and too slow.
|
260
|
+
|
261
|
+
When the state cache is used, debmirror will only check md5sums during runs
|
262
|
+
where the cache has expired or been invalidated, so it is worth considering
|
263
|
+
to use these two options together.
|
264
|
+
|
265
|
+
=item --ignore-missing-release
|
266
|
+
|
267
|
+
Don't fail if the F<Release> file is missing.
|
268
|
+
|
269
|
+
=item --ignore-release-gpg
|
270
|
+
|
271
|
+
Don't fail if the F<Release.gpg> file is missing.
|
272
|
+
|
273
|
+
=item --ignore=regex
|
274
|
+
|
275
|
+
Never delete any files whose filenames match the regex. May be used multiple times.
|
276
|
+
|
277
|
+
=item --exclude=regex
|
278
|
+
|
279
|
+
Never download any files whose filenames match the regex. May be used multiple times.
|
280
|
+
|
281
|
+
=item --include=regex
|
282
|
+
|
283
|
+
Don't exclude any files whose filenames match the regex. May be used multiple times.
|
284
|
+
|
285
|
+
=item --exclude-deb-section=regex
|
286
|
+
|
287
|
+
Never download any files whose Debian Section (games, doc, oldlibs,
|
288
|
+
science, ...) match the regex. May be used multiple times.
|
289
|
+
|
290
|
+
=item --limit-priority=regex
|
291
|
+
|
292
|
+
Limit download to files whose Debian Priority (required, extra,
|
293
|
+
optional, ...) match the regex. May be used multiple times.
|
294
|
+
|
295
|
+
=item --timeout=seconds -t
|
296
|
+
|
297
|
+
Specifies the timeout to use for network operations (either FTP or rsync).
|
298
|
+
Set this to a higher value if you experience failed downloads. Defaults
|
299
|
+
to 300 seconds.
|
300
|
+
|
301
|
+
=item --max-batch=number
|
302
|
+
|
303
|
+
Download at most max-batch number of files (and ignore rest).
|
304
|
+
|
305
|
+
=item --rsync-batch=number
|
306
|
+
|
307
|
+
Download at most number of files with each rsync call and then loop.
|
308
|
+
|
309
|
+
=item --rsync-options=options
|
310
|
+
|
311
|
+
Specify alternative rsync options to be used. Default options are
|
312
|
+
"-aIL --partial". Care must be taken when specifying alternative
|
313
|
+
options not to disrupt operations, it's best to only add to those
|
314
|
+
options.
|
315
|
+
|
316
|
+
The most likely option to add is "--bwlimit=x" to avoid saturating the
|
317
|
+
bandwidth of your link.
|
318
|
+
|
319
|
+
=item --postcleanup
|
320
|
+
|
321
|
+
Clean up the local mirror but only after mirroring is complete and
|
322
|
+
only if there was no error. This is the default.
|
323
|
+
|
324
|
+
=item --cleanup
|
325
|
+
|
326
|
+
Do clean up any unknown files and directories on the local mirror (see
|
327
|
+
step 2 above).
|
328
|
+
|
329
|
+
=item --nocleanup
|
330
|
+
|
331
|
+
Do not clean up the local mirror after mirroring is complete.
|
332
|
+
|
333
|
+
=item --skippackages
|
334
|
+
|
335
|
+
Don't re-download Packages and Sources files. Useful if you know they are
|
336
|
+
up-to-date.
|
337
|
+
|
338
|
+
=item --diff=use|mirror|none
|
339
|
+
|
340
|
+
If --diff=use is specified and the F<Release> file contains entries for
|
341
|
+
diff files, then debmirror will attempt to use them to update Packages,
|
342
|
+
Sources and Contents files (which can significantly reduce the download
|
343
|
+
size for meta files), but will not include them in the mirror. This is
|
344
|
+
the default behavior and avoids having time consuming diff files for a
|
345
|
+
fast local mirror.
|
346
|
+
|
347
|
+
Specifying --diff=mirror does the same as 'use', but will also include
|
348
|
+
the downloaded diff files in the local mirror. Specify --diff=none to
|
349
|
+
completely ignore diff files.
|
350
|
+
|
351
|
+
=item --state-cache-days=number
|
352
|
+
|
353
|
+
Save the state of the mirror in a cache file between runs. The cache will
|
354
|
+
expire after the specified number of days, at which time a full check and
|
355
|
+
cleanup of the mirror will be done. While the cache is valid, debmirror
|
356
|
+
will trust that the mirror is consistent with this cache.
|
357
|
+
|
358
|
+
The cache is only used for files that have a unique name, i.e. binary
|
359
|
+
packages and source files. If a mirror update fails for any reason, the
|
360
|
+
cache will be invalidated and the next run will include a full check.
|
361
|
+
|
362
|
+
Main advantage of using the state cache is that it avoids a large amount
|
363
|
+
of disk access while checking which files need to be fetched. It may also
|
364
|
+
reduce the time required for mirror updates.
|
365
|
+
|
366
|
+
=item --ignore-small-errors
|
367
|
+
|
368
|
+
Normally debmirror will report an error if any deb files or sources
|
369
|
+
fail to download and refuse to update the meta data to an inconsistent
|
370
|
+
mirror. Normally this is a good things as it indicates something went
|
371
|
+
wrong during download and should be retried. But sometimes the
|
372
|
+
upstream mirror actually is broken. Specifying --ignore-small-errors
|
373
|
+
causes debmirror to ignore missing or broken deb and source files but
|
374
|
+
still be pedantic about checking meta files.
|
375
|
+
|
376
|
+
=item --allow-dist-rename
|
377
|
+
|
378
|
+
The directory name for a dist should be equal to its Codename and not to
|
379
|
+
a Suite. If the local mirror currently has directories named after Suites,
|
380
|
+
debmirror can rename them automatically.
|
381
|
+
An existing symlink S<codename -E<gt> suite> will be removed, but debmirror
|
382
|
+
will automatically create a new symlink S<suite -E<gt> codename> (immediately
|
383
|
+
after moving meta files in place). This conversion should only be needed once.
|
384
|
+
|
385
|
+
=back
|
386
|
+
|
387
|
+
=head1 USING DEBMIRROR
|
388
|
+
|
389
|
+
=head2 Using regular expressions in options
|
390
|
+
|
391
|
+
Various options accept regular expressions that can be used to tune what
|
392
|
+
is included in the mirror. They can be any regular expression valid in
|
393
|
+
I<perl>, which also means that extended syntax is standard. Make sure to
|
394
|
+
anchor regular expressions appropriately: this is not done by debmirror.
|
395
|
+
|
396
|
+
The --include and --exclude options can be combined. This combination
|
397
|
+
for example will, if the --i18n option is used, exclude all F<Translation>
|
398
|
+
files, except for the ones for Portuguese (pt) and Brazillian (pt_BR):
|
399
|
+
|
400
|
+
--exclude='/Translation-.*\.bz2$' --include='/Translation-pt.*\.bz2$'
|
401
|
+
|
402
|
+
=head2 Mirroring Debian Installer images
|
403
|
+
|
404
|
+
Debmirror will only mirror the "current" images that are on the remote
|
405
|
+
mirror. At least one of the options --di-dist or --di-arch must be
|
406
|
+
passed to enable mirroring of the images.
|
407
|
+
|
408
|
+
The special values "dists" resp. "arches" can be used to tell debmirror
|
409
|
+
to use the same dists and architectures for D-I images as for the archive,
|
410
|
+
but it is also possible to specify different values. If either option is
|
411
|
+
not set, it will default to the same values as for the archive.
|
412
|
+
|
413
|
+
If you wish to create custom CD images using for example I<debian-cd>,
|
414
|
+
you will probably also want add the option "--rsync-extra=doc,tools".
|
415
|
+
|
416
|
+
B<Limitations>
|
417
|
+
|
418
|
+
There are no progress updates displayed for D-I images.
|
419
|
+
|
420
|
+
=head2 Archive size
|
421
|
+
|
422
|
+
The tables in the file F</usr/share/doc/debmirror/archive_size> give an
|
423
|
+
indication of the space needed to mirror the Debian archive. They are
|
424
|
+
particularly useful if you wish to set up a partial mirror.
|
425
|
+
Only the size of source and binary packages is included. You should allow
|
426
|
+
for around 1-4 GB of meta data (in F<./dists/E<lt>distE<gt>>) per suite
|
427
|
+
(depending in your settings). Plus whatever space is needed for extra
|
428
|
+
directories (e.g. F<tools>, F<doc>) you wish to mirror.
|
429
|
+
|
430
|
+
The tables also show how much additional space is required if you add
|
431
|
+
a release on top of its predecessor. Note that the additional space
|
432
|
+
needed for testing and (to a lesser extend) unstable varies during the
|
433
|
+
development cycle of a release. The additional space needed for testing
|
434
|
+
is zero immediately after a stable release and grows from that time
|
435
|
+
onwards.
|
436
|
+
|
437
|
+
B<Note>
|
438
|
+
Debmirror keeps an extra copy of all meta data. This is necessary to
|
439
|
+
guarantee that the local mirror stays consistent while debmirror is
|
440
|
+
running.
|
441
|
+
|
442
|
+
=head1 EXAMPLES
|
443
|
+
|
444
|
+
Simply make a mirror in F</srv/mirror/debian>, using all defaults (or the
|
445
|
+
settings defined in F<debmirror.conf>):
|
446
|
+
|
447
|
+
debmirror /srv/mirror/debian
|
448
|
+
|
449
|
+
Make a mirror of i386 and sparc binaries, main only, and include both unstable
|
450
|
+
and testing versions of Debian; download from 'ftp.kernel.org':
|
451
|
+
|
452
|
+
debmirror -a i386,sparc -d sid -d etch -s main --nosource \
|
453
|
+
-h ftp.nl.debian.org --progress $HOME/mirror/debian
|
454
|
+
|
455
|
+
Make a mirror using rsync (rsync server is 'ftp.debian.org::debian'),
|
456
|
+
excluding the section 'debug' and the package 'foo-doc':
|
457
|
+
|
458
|
+
debmirror -e rsync $HOME/mirror/debian --exclude='/foo-doc_' \
|
459
|
+
--exclude-deb-section='^debug$'
|
460
|
+
|
461
|
+
=head1 FILES
|
462
|
+
|
463
|
+
/etc/debmirror.conf
|
464
|
+
~/.debmirror.conf
|
465
|
+
|
466
|
+
Debmirror will look for the presence of these files and load them
|
467
|
+
in the indicated order if they exist.
|
468
|
+
See the example in /usr/share/doc/debmirror/examples for syntax.
|
469
|
+
|
470
|
+
~/.gnupg/trustedkeys.gpg
|
471
|
+
|
472
|
+
Debmirror uses gpgv to verify Release and Release.gpg using the
|
473
|
+
default keying ~/.gnupg/trustedkeys.gpg. This can be changed by
|
474
|
+
exporting GNUPGHOME resulting in $GNUPGHOME/trustedkeys.gpg being
|
475
|
+
used.
|
476
|
+
|
477
|
+
To add the right key to this keyring you can import it from the
|
478
|
+
debian keyring (in case of the debian archive) using:
|
479
|
+
|
480
|
+
gpg --keyring /usr/share/keyrings/debian-archive-keyring.gpg --export \
|
481
|
+
| gpg --no-default-keyring --keyring trustedkeys.gpg --import
|
482
|
+
|
483
|
+
or download the key from a keyserver:
|
484
|
+
|
485
|
+
gpg --no-default-keyring --keyring trustedkeys.gpg \
|
486
|
+
--keyserver keyring.debian.org --recv-keys <key ID>
|
487
|
+
|
488
|
+
The <key ID> can be found in the gpgv error message in debmirror:
|
489
|
+
gpgv: Signature made Tue Jan 23 09:07:53 2007 CET using DSA key ID 2D230C5F
|
490
|
+
|
491
|
+
=cut
|
492
|
+
|
493
|
+
use strict;
|
494
|
+
use Cwd;
|
495
|
+
use Storable qw(nstore retrieve);
|
496
|
+
use Net::FTP;
|
497
|
+
use Getopt::Long;
|
498
|
+
use File::Temp qw/ tempfile /;
|
499
|
+
use LockFile::Simple;
|
500
|
+
use Compress::Zlib;
|
501
|
+
use Digest::MD5;
|
502
|
+
use Digest::SHA1;
|
503
|
+
use LWP::UserAgent;
|
504
|
+
|
505
|
+
# Yeah, I use too many global variables in this program.
|
506
|
+
our ($debug, $progress, $verbose, $passive, $skippackages, $getcontents, $i18n);
|
507
|
+
our ($ua, $proxy);
|
508
|
+
our (@dists, @sections, @arches, @extra_dirs, @ignores, @excludes, @includes);
|
509
|
+
our (@excludes_deb_section, @limit_priority);
|
510
|
+
our (@di_dists, @di_arches, @rsync_extra);
|
511
|
+
our $state_cache_days = 0;
|
512
|
+
our $check_md5sums = 0;
|
513
|
+
our $check_downloads = 0;
|
514
|
+
our $cleanup=0;
|
515
|
+
our $post_cleanup=1;
|
516
|
+
our $no_cleanup=0;
|
517
|
+
our $do_source=1;
|
518
|
+
our $host="ftp.debian.org";
|
519
|
+
our $user="anonymous";
|
520
|
+
our $passwd="anonymous@";
|
521
|
+
our $remoteroot="debian";
|
522
|
+
our $download_method="ftp";
|
523
|
+
our $timeout=300;
|
524
|
+
our $max_batch=0;
|
525
|
+
our $rsync_batch=200;
|
526
|
+
our $num_errors=0;
|
527
|
+
our $bytes_to_get=0;
|
528
|
+
our $bytes_gotten=0;
|
529
|
+
our $bytes_meta=0;
|
530
|
+
our $doing_meta=1;
|
531
|
+
our $ignore_release=0;
|
532
|
+
our $ignore_release_gpg=0;
|
533
|
+
our $start_time = time;
|
534
|
+
our $dry_run=0;
|
535
|
+
our $dry_run_var=0;
|
536
|
+
our $rsync_options="-aIL --partial";
|
537
|
+
our $ignore_small_errors=0;
|
538
|
+
our $diff_mode="use";
|
539
|
+
our $omit_suite_symlinks=0;
|
540
|
+
our $allow_dist_rename=0;
|
541
|
+
my @errlog;
|
542
|
+
my $HOME;
|
543
|
+
($HOME = $ENV{'HOME'}) or die "HOME not defined in environment!\n";
|
544
|
+
|
545
|
+
# Load in config files
|
546
|
+
require "/etc/debmirror.conf" if -r "/etc/debmirror.conf";
|
547
|
+
require "$HOME/.debmirror.conf" if -r "$HOME/.debmirror.conf";
|
548
|
+
|
549
|
+
# This hash contains the releases to mirror. If both codename and suite can be
|
550
|
+
# determined from the Release file, the codename is used in the key. If not,
|
551
|
+
# it can also be a suite (or whatever was requested by the user).
|
552
|
+
# The hash has tree subtypes:
|
553
|
+
# - suite: if both codename and suite could be determined from the Release file,
|
554
|
+
# the codename is the key and the value is the name of the suitei - used to
|
555
|
+
# update the suite -> codename symlinks;
|
556
|
+
# - mirror: set to 1 if the package archive should be mirrored for the dist;
|
557
|
+
# - d-i: set to 1 if D-I images should be mirrored for the dist.
|
558
|
+
# For the last two subtypes the key can also include a subdir.
|
559
|
+
my %distset=();
|
560
|
+
|
561
|
+
# This hash holds all the files we know about. Values are:
|
562
|
+
# - -1: file was not on mirror and download attempt failed
|
563
|
+
# - 0: file was not on mirror and either needs downloading or was
|
564
|
+
# downloaded this run
|
565
|
+
# - 1: file is on mirror and wanted according to meta data
|
566
|
+
# - 2: file is on mirror and listed in state cache, but not (yet)
|
567
|
+
# verified as wanted according to meta data
|
568
|
+
# Values -1 and 2 can occur in the state cache; see $files_cache_version
|
569
|
+
# below! Filenames should be relative to $mirrordir.
|
570
|
+
my %files;
|
571
|
+
|
572
|
+
# Hash to record size and md5sums of meta files and package files (from the
|
573
|
+
# Release file and Source/Packages files).
|
574
|
+
my %file_lists;
|
575
|
+
|
576
|
+
# Hash to record which Translation files needs download. Contains size and sha1
|
577
|
+
# info. Files also get registered in %files.
|
578
|
+
my %i18n_get;
|
579
|
+
|
580
|
+
# Separate hash for files belonging to Debian Installer images.
|
581
|
+
# This data is not cached.
|
582
|
+
my %di_files;
|
583
|
+
|
584
|
+
## State cache meta-data
|
585
|
+
my $use_cache = 0;
|
586
|
+
my $state_cache_exptime;
|
587
|
+
# Next variable *must* be changed if the structure of the %files hash is
|
588
|
+
# changed in a way that makes old state-cache files incompatible.
|
589
|
+
my $files_cache_version = "1.0";
|
590
|
+
|
591
|
+
my $help;
|
592
|
+
GetOptions('debug' => \$debug,
|
593
|
+
'progress|p' => \$progress,
|
594
|
+
'verbose|v' => \$verbose,
|
595
|
+
'source!' => \$do_source,
|
596
|
+
'md5sums|m' => \$check_md5sums,
|
597
|
+
'nomd5sums' => \$check_downloads,
|
598
|
+
'passive!' => \$passive,
|
599
|
+
'host|h=s' => \$host,
|
600
|
+
'user|u=s' => \$user,
|
601
|
+
'passwd=s' => \$passwd,
|
602
|
+
'root|r=s' => \$remoteroot,
|
603
|
+
'dist|d=s' => \@dists,
|
604
|
+
'section|s=s' => \@sections,
|
605
|
+
'arch|a=s' => \@arches,
|
606
|
+
'adddir=s' => \@extra_dirs,
|
607
|
+
'di-dist=s' => \@di_dists,
|
608
|
+
'di-arch=s' => \@di_arches,
|
609
|
+
'rsync-extra=s' => \@rsync_extra,
|
610
|
+
'cleanup' => \$cleanup,
|
611
|
+
'postcleanup' => \$post_cleanup,
|
612
|
+
'nocleanup' => \$no_cleanup,
|
613
|
+
'ignore=s' => \@ignores,
|
614
|
+
'exclude=s' => \@excludes,
|
615
|
+
'exclude-deb-section=s' => \@excludes_deb_section,
|
616
|
+
'limit-priority=s' => \@limit_priority,
|
617
|
+
'include=s' => \@includes,
|
618
|
+
'skippackages' => \$skippackages,
|
619
|
+
'i18n' => \$i18n,
|
620
|
+
'getcontents' => \$getcontents,
|
621
|
+
'method|e=s' => \$download_method,
|
622
|
+
'timeout|t=s' => \$timeout,
|
623
|
+
'max-batch=s' => \$max_batch,
|
624
|
+
'rsync-batch=s' => \$rsync_batch,
|
625
|
+
'state-cache-days=s' => \$state_cache_days,
|
626
|
+
'ignore-missing-release' => \$ignore_release,
|
627
|
+
'ignore-release-gpg' => \$ignore_release_gpg,
|
628
|
+
'dry-run' => \$dry_run_var,
|
629
|
+
'proxy=s' => \$proxy,
|
630
|
+
'rsync-options=s' => \$rsync_options,
|
631
|
+
'ignore-small-errors' => \$ignore_small_errors,
|
632
|
+
'diff=s' => \$diff_mode,
|
633
|
+
'omit-suite-symlinks' => \$omit_suite_symlinks,
|
634
|
+
'allow-dist-rename' => \$allow_dist_rename,
|
635
|
+
'help' => \$help,
|
636
|
+
) or usage;
|
637
|
+
usage if $help;
|
638
|
+
|
639
|
+
# This parameter is so important that it is the only required parameter.
|
640
|
+
my $mirrordir=shift or usage("mirrordir not specified");
|
641
|
+
|
642
|
+
# Check for patch binary if needed
|
643
|
+
if (!($diff_mode eq "none")) {
|
644
|
+
if (system("patch --version 2>/dev/null >/dev/null")) {
|
645
|
+
say("Patch binary missing, falling back to --diff=none");
|
646
|
+
push (@errlog,"Patch binary missing, falling back to --diff=none\n");
|
647
|
+
$diff_mode = "none";
|
648
|
+
}
|
649
|
+
if (system("which ed 2>/dev/null >/dev/null")) {
|
650
|
+
say("Ed binary missing, falling back to --diff=none");
|
651
|
+
push (@errlog,"Ed binary missing, falling back to --diff=none\n");
|
652
|
+
$diff_mode = "none";
|
653
|
+
}
|
654
|
+
}
|
655
|
+
|
656
|
+
# Backwards compatibility: remote root dir no longer needs prefix
|
657
|
+
$remoteroot =~ s%^[:/]%%;
|
658
|
+
|
659
|
+
# Post-process arrays. Allow commas to separate values the user entered.
|
660
|
+
# If the user entered nothing, provide defaults.
|
661
|
+
@dists=split(/,/,join(',',@dists));
|
662
|
+
@dists=qw(sid) unless @dists;
|
663
|
+
@sections=split(/,/,join(',',@sections));
|
664
|
+
@sections=qw(main contrib non-free main/debian-installer) unless @sections;
|
665
|
+
@arches=split(/,/,join(',',@arches));
|
666
|
+
@arches=qw(i386) unless @arches;
|
667
|
+
@arches=() if (join(',',@arches) eq "none");
|
668
|
+
@di_dists=split(/,/,join(',',@di_dists));
|
669
|
+
@di_arches=split(/,/,join(',',@di_arches));
|
670
|
+
@rsync_extra=split(/,/,join(',',@rsync_extra));
|
671
|
+
if (@di_dists) {
|
672
|
+
@di_dists = @dists if ($di_dists[0] eq "dists");
|
673
|
+
@di_arches = @arches if (!@di_arches || $di_arches[0] eq "arches");
|
674
|
+
} elsif (@di_arches) {
|
675
|
+
@di_dists = @dists if (!@di_dists);
|
676
|
+
@di_arches = @arches if ($di_arches[0] eq "arches");
|
677
|
+
}
|
678
|
+
$cleanup=0 if ($no_cleanup);
|
679
|
+
$post_cleanup=0 if ($no_cleanup);
|
680
|
+
$post_cleanup=0 if ($cleanup);
|
681
|
+
|
682
|
+
# Display configuration.
|
683
|
+
$|=1 if $debug;
|
684
|
+
if ($passwd eq "anonymous@") {
|
685
|
+
if ($download_method eq "http") {
|
686
|
+
say("Mirroring to $mirrordir from $download_method://$host/$remoteroot/");
|
687
|
+
} else {
|
688
|
+
say("Mirroring to $mirrordir from $download_method://$user\@$host/$remoteroot/");
|
689
|
+
}
|
690
|
+
} else {
|
691
|
+
say("Mirroring to $mirrordir from $download_method://$user:XXX\@$host/$remoteroot/");
|
692
|
+
}
|
693
|
+
say("Arches: ".join(",", @arches));
|
694
|
+
say("Dists: ".join(",", @dists));
|
695
|
+
say("Sections: ".join(",", @sections));
|
696
|
+
say("Including source.") if $do_source;
|
697
|
+
say("D-I arches: ".join(",", @di_arches)) if @di_arches;
|
698
|
+
say("D-I dists: ".join(",", @di_dists)) if @di_dists;
|
699
|
+
say("Pdiff mode: $diff_mode");
|
700
|
+
say("Checking md5sums.") if $check_md5sums;
|
701
|
+
say("Passive mode on.") if $passive;
|
702
|
+
say("Proxy: $proxy") if $proxy;
|
703
|
+
say("Download at most $max_batch files.") if ($max_batch > 0);
|
704
|
+
say("Download at most $rsync_batch files per rsync call.") if ($download_method eq "rsync");
|
705
|
+
if ($post_cleanup) {
|
706
|
+
say("Will clean up AFTER mirroring.");
|
707
|
+
} else {
|
708
|
+
say("Will NOT clean up.") unless $cleanup;
|
709
|
+
}
|
710
|
+
say("Dry run.") if $dry_run_var;
|
711
|
+
|
712
|
+
my $md5;
|
713
|
+
$md5=Digest::MD5->new;
|
714
|
+
|
715
|
+
# Set up mirror directory and resolve $mirrordir to a full path for
|
716
|
+
# locking and rsync
|
717
|
+
make_dir($mirrordir) if (! -d $mirrordir);
|
718
|
+
die "You need write permissions on $mirrordir" if (! -w $mirrordir);
|
719
|
+
chdir($mirrordir) or die "chdir $mirrordir: $!";
|
720
|
+
$mirrordir = cwd();
|
721
|
+
|
722
|
+
# Handle the lock file. This is the same method used by official
|
723
|
+
# Debian push mirrors.
|
724
|
+
my $hostname=`hostname -f 2>/dev/null || hostname`;
|
725
|
+
chomp $hostname;
|
726
|
+
my $lockfile="Archive-Update-in-Progress-$hostname";
|
727
|
+
say("Attempting to get lock, this might take 2 minutes before it fails.");
|
728
|
+
my $lockmgr = LockFile::Simple->make(-format => "%f/$lockfile", -max => 12,
|
729
|
+
-delay => 10, -nfs => 1, -autoclean => 1,
|
730
|
+
-warn => 1, -stale => 1, -hold => 0);
|
731
|
+
my $lock = $lockmgr->lock("$mirrordir")
|
732
|
+
or die "$lockfile exists or you lack proper permissions; aborting";
|
733
|
+
$SIG{INT}=sub { $lock->release; exit 1 };
|
734
|
+
$SIG{TERM}=sub { $lock->release; exit 1 };
|
735
|
+
|
736
|
+
# Create tempdir if missing
|
737
|
+
my $tempdir=".temp";
|
738
|
+
make_dir($tempdir) if (! -d $tempdir);
|
739
|
+
die "You need write permissions on $tempdir" if (! -w $tempdir);
|
740
|
+
|
741
|
+
# Load the state cache.
|
742
|
+
load_state_cache() if $state_cache_days;
|
743
|
+
|
744
|
+
# Register the trace and lock files.
|
745
|
+
my $tracefile="project/trace/$hostname";
|
746
|
+
$files{$tracefile}=1;
|
747
|
+
$files{$lockfile}=1;
|
748
|
+
|
749
|
+
# Start up ftp.
|
750
|
+
my $ftp;
|
751
|
+
my %opts = (Debug => $debug, Passive => $passive, Timeout => $timeout);
|
752
|
+
|
753
|
+
my $rsynctempfile;
|
754
|
+
END { unlink $rsynctempfile if $rsynctempfile }
|
755
|
+
|
756
|
+
sub init_connection {
|
757
|
+
$_ = $download_method;
|
758
|
+
|
759
|
+
/^hftp$/ && do {
|
760
|
+
# LWP stuff
|
761
|
+
$ua = new LWP::UserAgent;
|
762
|
+
if ($proxy) {
|
763
|
+
$ua->proxy('ftp', $proxy);
|
764
|
+
} elsif ($ENV{ftp_proxy}) {
|
765
|
+
$ua->proxy('ftp', $ENV{ftp_proxy});
|
766
|
+
} else {
|
767
|
+
die("hftp method needs a proxy.");
|
768
|
+
}
|
769
|
+
return;
|
770
|
+
};
|
771
|
+
|
772
|
+
/^http$/ && do {
|
773
|
+
# LWP stuff[
|
774
|
+
$ua = new LWP::UserAgent(keep_alive => 1);
|
775
|
+
$ua->proxy('http', $ENV{http_proxy}) if ($ENV{http_proxy});
|
776
|
+
$ua->proxy('http', $proxy) if ($proxy);
|
777
|
+
return;
|
778
|
+
};
|
779
|
+
|
780
|
+
/^ftp$/ && do {
|
781
|
+
$ftp=Net::FTP->new($host, %opts) or die "$@\n";
|
782
|
+
$ftp->login($user, $passwd) or die "login failed"; # anonymous
|
783
|
+
$ftp->binary or die "could not set binary mode";
|
784
|
+
$ftp->cwd("/$remoteroot") or die "cwd to /$remoteroot failed";
|
785
|
+
$ftp->hash(\*STDOUT,102400) if $progress;
|
786
|
+
return;
|
787
|
+
};
|
788
|
+
|
789
|
+
/^rsync$/ && do {
|
790
|
+
return;
|
791
|
+
};
|
792
|
+
|
793
|
+
usage("unknown download method: $_");
|
794
|
+
}
|
795
|
+
init_connection;
|
796
|
+
|
797
|
+
# determine remote root for rsync transfers
|
798
|
+
my $rsyncremote = "$host\:\:$remoteroot/";
|
799
|
+
if (! ($user eq 'anonymous')) {
|
800
|
+
$rsyncremote = "$user\@$rsyncremote";
|
801
|
+
}
|
802
|
+
|
803
|
+
# Update the remote trace files; also update ignores for @rsync_extra.
|
804
|
+
rsync_extra(1, @rsync_extra);
|
805
|
+
|
806
|
+
say("Get Release files.");
|
807
|
+
# Get Release files without caching for http
|
808
|
+
$ua->default_header( "Cache-Control" => "max-age=0" ) if ($ua);
|
809
|
+
foreach my $dist (@dists) {
|
810
|
+
my $tdir="$tempdir/.tmp/dists/$dist";
|
811
|
+
my $have_release = get_release($tdir, $dist);
|
812
|
+
next unless ($have_release || $ignore_release);
|
813
|
+
my ($codename, $suite, $dist_sdir) = name_release("mirror", $tdir, $dist);
|
814
|
+
|
815
|
+
if ($have_release) {
|
816
|
+
make_dir ("dists/$codename$dist_sdir");
|
817
|
+
make_dir ("$tempdir/dists/$codename$dist_sdir");
|
818
|
+
rename("$tdir/Release", "$tempdir/dists/$codename$dist_sdir/Release")
|
819
|
+
or die "Error while moving $tdir/Release: $!\n";
|
820
|
+
rename("$tdir/Release.gpg", "$tempdir/dists/$codename$dist_sdir/Release.gpg")
|
821
|
+
or die "Error while moving $tdir/Release.gpg: $!\n";
|
822
|
+
$files{"dists/$codename$dist_sdir/Release"}=1;
|
823
|
+
$files{$tempdir."/"."dists/$codename$dist_sdir/Release"}=1;
|
824
|
+
$files{"dists/$codename$dist_sdir/Release.gpg"}=1;
|
825
|
+
$files{$tempdir."/"."dists/$codename$dist_sdir/Release.gpg"}=1;
|
826
|
+
}
|
827
|
+
}
|
828
|
+
|
829
|
+
# Check that @di_dists contains valid codenames
|
830
|
+
di_check_dists() if @di_dists;
|
831
|
+
|
832
|
+
foreach my $dist (keys %distset) {
|
833
|
+
next unless exists $distset{$dist}{mirror};
|
834
|
+
# Parse the Release
|
835
|
+
if (open RELEASE, "<$tempdir/dists/$dist/Release") {
|
836
|
+
while (<RELEASE>) {
|
837
|
+
last if /^MD5Sum:/;
|
838
|
+
}
|
839
|
+
$_ = <RELEASE>;
|
840
|
+
while (defined $_ && $_ =~ /^ /) {
|
841
|
+
my ($md5sum, $size, $filename) =
|
842
|
+
(/ ([a-z0-9]+) +(\d+) +(.*)$/);
|
843
|
+
$file_lists{"$tempdir/dists/$dist/$filename"}{md5} = $md5sum;
|
844
|
+
$file_lists{"$tempdir/dists/$dist/$filename"}{size} = $size;
|
845
|
+
$_ = <RELEASE>;
|
846
|
+
}
|
847
|
+
close RELEASE;
|
848
|
+
}
|
849
|
+
}
|
850
|
+
|
851
|
+
if ($num_errors != 0 && $ignore_release) {
|
852
|
+
say("Ignoring failed Release files.");
|
853
|
+
push (@errlog,"Ignoring failed Release files\n");
|
854
|
+
$num_errors = 0;
|
855
|
+
}
|
856
|
+
|
857
|
+
if ($num_errors != 0) {
|
858
|
+
print "Errors:\n ".join(" ",@errlog) if (@errlog);
|
859
|
+
die "Failed to download some Release or Release.gpg files!\n";
|
860
|
+
}
|
861
|
+
|
862
|
+
# Enable caching again for http
|
863
|
+
init_connection if ($ua);
|
864
|
+
|
865
|
+
# Calculate expected downloads for meta files
|
866
|
+
# As we don't actually download most of the meta files (due to getting
|
867
|
+
# only one compression variant or using diffs), we keep a separate count
|
868
|
+
# of the actual downloaded amount of data in $bytes_meta.
|
869
|
+
|
870
|
+
# The root Release files have already been downloaded
|
871
|
+
$bytes_to_get = $bytes_meta;
|
872
|
+
$bytes_gotten = $bytes_meta;
|
873
|
+
|
874
|
+
sub add_bytes {
|
875
|
+
my $name=shift;
|
876
|
+
$bytes_to_get += $file_lists{"$tempdir/$name"}{size} if exists $file_lists{"$tempdir/$name"};
|
877
|
+
}
|
878
|
+
foreach my $dist (keys %distset) {
|
879
|
+
next unless exists $distset{$dist}{mirror};
|
880
|
+
foreach my $section (@sections) {
|
881
|
+
foreach my $arch (@arches) {
|
882
|
+
add_bytes("dists/$dist/$section/binary-$arch/Packages");
|
883
|
+
add_bytes("dists/$dist/$section/binary-$arch/Packages.gz");
|
884
|
+
add_bytes("dists/$dist/$section/binary-$arch/Packages.bz2");
|
885
|
+
add_bytes("dists/$dist/$section/binary-$arch/Release");
|
886
|
+
add_bytes("dists/$dist/$section/binary-$arch/Packages.diff/Index") unless ($diff_mode eq "none");
|
887
|
+
}
|
888
|
+
# d-i does not have separate source sections
|
889
|
+
if ($do_source && $section !~ /debian-installer/) {
|
890
|
+
add_bytes("dists/$dist/$section/source/Sources");
|
891
|
+
add_bytes("dists/$dist/$section/source/Sources.gz");
|
892
|
+
add_bytes("dists/$dist/$section/source/Sources.bz2");
|
893
|
+
add_bytes("dists/$dist/$section/source/Release");
|
894
|
+
add_bytes("dists/$dist/$section/source/Sources.diff/Index") unless ($diff_mode eq "none");
|
895
|
+
}
|
896
|
+
add_bytes("dists/$dist/$section/i18n/Index") if $i18n;
|
897
|
+
}
|
898
|
+
}
|
899
|
+
foreach (@extra_dirs) {
|
900
|
+
add_bytes("$_/Packages");
|
901
|
+
add_bytes("$_/Packages.gz");
|
902
|
+
add_bytes("$_/Packages.bz2");
|
903
|
+
add_bytes("$_/Release");
|
904
|
+
add_bytes("$_/Packages.diff/Index");
|
905
|
+
if ($do_source) {
|
906
|
+
add_bytes("$_/Sources");
|
907
|
+
add_bytes("$_/Sources.gz");
|
908
|
+
add_bytes("$_/Sources.bz2");
|
909
|
+
add_bytes("$_/Sources.diff/Index");
|
910
|
+
}
|
911
|
+
}
|
912
|
+
|
913
|
+
# Get and parse MD5SUMS files for D-I images.
|
914
|
+
di_add_files() if @di_dists;
|
915
|
+
|
916
|
+
say("Get Packages and Sources files and other miscellany.");
|
917
|
+
# Get Packages and Sources files and other miscellany.
|
918
|
+
my (@package_files, @source_files);
|
919
|
+
foreach my $dist (keys %distset) {
|
920
|
+
next unless exists $distset{$dist}{mirror};
|
921
|
+
foreach my $section (@sections) {
|
922
|
+
# no d-i in woody
|
923
|
+
next if ($section =~ /debian-installer/ && $dist eq "woody");
|
924
|
+
next if ($section =~ /debian-installer/ && $dist eq "experimental");
|
925
|
+
next if ($section =~ /debian-installer/ && $dist =~ /.*-proposed-updates/);
|
926
|
+
next if ($section =~ /debian-installer/ && $dist =~ /.*breezy-updates/ );
|
927
|
+
next if ($section =~ /debian-installer/ && $dist eq "breezy-security" );
|
928
|
+
foreach my $arch (@arches) {
|
929
|
+
get_index("dists/$dist/$section/binary-$arch", "Packages");
|
930
|
+
}
|
931
|
+
# d-i does not have separate source sections
|
932
|
+
if ($do_source && $section !~ /debian-installer/) {
|
933
|
+
get_index("dists/$dist/$section/source", "Sources");
|
934
|
+
}
|
935
|
+
get_i18n_index("dists/$dist/$section/i18n") if $i18n;
|
936
|
+
}
|
937
|
+
}
|
938
|
+
foreach (@extra_dirs) {
|
939
|
+
get_packages($_, "Packages");
|
940
|
+
get_sources($_, "Sources") if ($do_source);
|
941
|
+
}
|
942
|
+
|
943
|
+
# Set download size for meta files to actual values
|
944
|
+
$doing_meta=0;
|
945
|
+
$bytes_to_get=$bytes_meta;
|
946
|
+
$bytes_gotten=$bytes_meta;
|
947
|
+
|
948
|
+
# Sanity check. I once nuked a mirror because of this..
|
949
|
+
if (@arches && ! @package_files) {
|
950
|
+
print "Errors:\n ".join(" ",@errlog) if (@errlog);
|
951
|
+
die "Failed to download any Packages files!\n";
|
952
|
+
}
|
953
|
+
if ($do_source && ! @source_files) {
|
954
|
+
print "Errors:\n ".join(" ",@errlog) if (@errlog);
|
955
|
+
die "Failed to download any Sources files!\n";
|
956
|
+
}
|
957
|
+
|
958
|
+
if ($num_errors != 0) {
|
959
|
+
print "Errors:\n ".join(" ",@errlog) if (@errlog);
|
960
|
+
die "Failed to download some Package, Sources or Release files!\n";
|
961
|
+
}
|
962
|
+
|
963
|
+
# Really set dry-run option now if it was given. This delay is needed
|
964
|
+
# for the ftp method.
|
965
|
+
$dry_run = $dry_run_var;
|
966
|
+
|
967
|
+
# Determine size of Contents and Translation files to get.
|
968
|
+
if ($getcontents) {
|
969
|
+
# Updates of Contents files using diffs are done here; only full downloads
|
970
|
+
# are delayed.
|
971
|
+
say("Update Contents files.") if ($diff_mode ne "none");
|
972
|
+
foreach my $dist (keys %distset) {
|
973
|
+
next unless exists $distset{$dist}{mirror};
|
974
|
+
foreach my $arch (@arches) {
|
975
|
+
next if $dist=~/experimental/;
|
976
|
+
next if $dist=~/.*-proposed-updates/;
|
977
|
+
next if $arch=~/source/;
|
978
|
+
if ($diff_mode ne "none") {
|
979
|
+
if (!update_contents("dists/$dist", "Contents-$arch")) {
|
980
|
+
add_bytes("dists/$dist/Contents-$arch.gz");
|
981
|
+
}
|
982
|
+
} elsif (!check_lists ("$tempdir/dists/$dist/Contents-$arch.gz")) {
|
983
|
+
add_bytes("dists/$dist/Contents-$arch.gz");
|
984
|
+
}
|
985
|
+
}
|
986
|
+
}
|
987
|
+
}
|
988
|
+
if ($i18n) {
|
989
|
+
foreach my $dist (keys %distset) {
|
990
|
+
next unless exists $distset{$dist}{mirror};
|
991
|
+
foreach my $section (@sections) {
|
992
|
+
parse_i18n_index("dists/$dist/$section/i18n");
|
993
|
+
}
|
994
|
+
}
|
995
|
+
}
|
996
|
+
|
997
|
+
# close ftp connection to avoid timeouts, will reopen later
|
998
|
+
if ($download_method eq 'ftp') { $ftp->quit; }
|
999
|
+
|
1000
|
+
say("Parse Packages and Sources files and add to the file list everything therein.");
|
1001
|
+
{
|
1002
|
+
local $/="\n\n"; # Set input separator to read entire package
|
1003
|
+
|
1004
|
+
my ($filename, $size, $md5sum, $directory, $exclude, $include,
|
1005
|
+
$architecture, $exclude_deb_section, $limit_priority, $deb_section,
|
1006
|
+
$deb_priority);
|
1007
|
+
my $empty_mirror = 1;
|
1008
|
+
|
1009
|
+
my %arches = map { $_ => 1 } (@arches, "all");
|
1010
|
+
|
1011
|
+
$include = "(".join("|", @includes).")" if @includes;
|
1012
|
+
$exclude = "(".join("|", @excludes).")" if @excludes;
|
1013
|
+
$exclude_deb_section =
|
1014
|
+
"(".join("|", @excludes_deb_section).")" if @excludes_deb_section;
|
1015
|
+
$limit_priority = "(".join("|", @limit_priority).")" if @limit_priority;
|
1016
|
+
foreach my $file (@package_files) {
|
1017
|
+
next if (!-f $file);
|
1018
|
+
open(FILE, "<", $file) or die "$file: $!";
|
1019
|
+
for (;;) {
|
1020
|
+
my $buf;
|
1021
|
+
unless (defined( $buf = <FILE> )) {
|
1022
|
+
last if eof;
|
1023
|
+
die "$file: $!" if $!;
|
1024
|
+
}
|
1025
|
+
$_ = $buf;
|
1026
|
+
($filename)=m/^Filename:\s+(.*)/im;
|
1027
|
+
$filename=~s:/+:/:; # remove redundant slashes in paths
|
1028
|
+
($deb_section)=m/^Section:\s+(.*)/im;
|
1029
|
+
($deb_priority)=m/^Priority:\s+(.*)/im;
|
1030
|
+
($architecture)=m/^Architecture:\s+(.*)/im;
|
1031
|
+
next if (!$arches{$architecture});
|
1032
|
+
if(!(defined($include) && ($filename=~/$include/o))) {
|
1033
|
+
next if (defined($exclude) && $filename=~/$exclude/o);
|
1034
|
+
next if (defined($exclude_deb_section) && defined($deb_section)
|
1035
|
+
&& $deb_section=~/$exclude_deb_section/o);
|
1036
|
+
next if (defined($limit_priority) && defined($deb_priority)
|
1037
|
+
&& ! ($deb_priority=~/$limit_priority/o));
|
1038
|
+
}
|
1039
|
+
# File was listed in state cache, or file occurs multiple times
|
1040
|
+
if (exists $files{$filename}) {
|
1041
|
+
if ($files{$filename} >= 0) {
|
1042
|
+
$files{$filename} = 1 if $files{$filename} == 2;
|
1043
|
+
$empty_mirror = 0;
|
1044
|
+
next;
|
1045
|
+
} else { # download failed previous run, retry
|
1046
|
+
$files{$filename} = 0;
|
1047
|
+
}
|
1048
|
+
}
|
1049
|
+
($size)=m/^Size:\s+(\d+)/im;
|
1050
|
+
($md5sum)=m/^MD5sum:\s+([A-Za-z0-9]+)/im;
|
1051
|
+
if (check_file($filename, $size, $md5sum)) {
|
1052
|
+
$files{$filename} = 1;
|
1053
|
+
} else {
|
1054
|
+
$files{$filename} = 0;
|
1055
|
+
$file_lists{$filename}{md5} = $md5sum;
|
1056
|
+
$file_lists{$filename}{size} = $size;
|
1057
|
+
$bytes_to_get += $size;
|
1058
|
+
}
|
1059
|
+
$empty_mirror = 0;
|
1060
|
+
}
|
1061
|
+
close(FILE);
|
1062
|
+
}
|
1063
|
+
foreach my $file (@source_files) {
|
1064
|
+
next if (!-f $file);
|
1065
|
+
open(FILE, "<", $file) or die "$file: $!";
|
1066
|
+
for (;;) {
|
1067
|
+
my $buf = "";
|
1068
|
+
unless (defined( $buf = <FILE> )) {
|
1069
|
+
last if eof;
|
1070
|
+
die "$file: $!" if $!;
|
1071
|
+
}
|
1072
|
+
$_ = $buf;
|
1073
|
+
($directory) = m/^Directory:\s+(.*)/im;
|
1074
|
+
($deb_section)=m/^Section:\s+(.*)/im;
|
1075
|
+
($deb_priority)=m/^Priority:\s+(.*)/im;
|
1076
|
+
next if (defined($exclude_deb_section) && defined($deb_section)
|
1077
|
+
&& $deb_section=~/$exclude_deb_section/o);
|
1078
|
+
next if (defined($limit_priority) && defined($deb_priority)
|
1079
|
+
&& ! ($deb_priority=~/$limit_priority/o));
|
1080
|
+
while (m/^ ([A-Za-z0-9]{32} .*)/mg) {
|
1081
|
+
($md5sum, $size, $filename)=split(' ', $1, 3);
|
1082
|
+
$filename="$directory/$filename";
|
1083
|
+
$filename=~s:/+:/:; # remove redundant slashes in paths
|
1084
|
+
if(!(defined($include) && $filename=~/$include/o)) {
|
1085
|
+
next if (defined($exclude) && $filename=~/$exclude/o);
|
1086
|
+
}
|
1087
|
+
# File was listed in state cache, or file occurs multiple times
|
1088
|
+
if (exists $files{$filename}) {
|
1089
|
+
if ($files{$filename} >= 0) {
|
1090
|
+
$files{$filename} = 1 if $files{$filename} == 2;
|
1091
|
+
$empty_mirror = 0;
|
1092
|
+
next;
|
1093
|
+
} else { # download failed previous run, retry
|
1094
|
+
$files{$filename} = 0;
|
1095
|
+
}
|
1096
|
+
}
|
1097
|
+
if (check_file($filename, $size, $md5sum)) {
|
1098
|
+
$files{$filename} = 1;
|
1099
|
+
} else {
|
1100
|
+
$files{$filename} = 0;
|
1101
|
+
$file_lists{$filename}{md5} = $md5sum;
|
1102
|
+
$file_lists{$filename}{size} = $size;
|
1103
|
+
$bytes_to_get += $size;
|
1104
|
+
}
|
1105
|
+
}
|
1106
|
+
$empty_mirror = 0;
|
1107
|
+
}
|
1108
|
+
close(FILE);
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
# Sanity check to avoid completely nuking a mirror.
|
1112
|
+
if ($empty_mirror) {
|
1113
|
+
print "Errors:\n ".join(" ",@errlog) if (@errlog);
|
1114
|
+
die "No packages after parsing Packages and Sources files!\n";
|
1115
|
+
}
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
# Pre-mirror cleanup
|
1119
|
+
cleanup_unknown_files() if ($cleanup && ! $post_cleanup);
|
1120
|
+
|
1121
|
+
say("Download all files that we need to get (".print_dl_size($bytes_to_get - $bytes_gotten).").");
|
1122
|
+
init_connection;
|
1123
|
+
|
1124
|
+
# Download Contents and Translation files.
|
1125
|
+
get_contents_files() if ($getcontents);
|
1126
|
+
get_i18n_files() if ($i18n);
|
1127
|
+
|
1128
|
+
# Download all package files that we need to get.
|
1129
|
+
say("Get package files.");
|
1130
|
+
DOWNLOAD: {
|
1131
|
+
$_ = $download_method;
|
1132
|
+
|
1133
|
+
# hftp (ftp using http mirror) method
|
1134
|
+
/^hftp$/ && do {
|
1135
|
+
# LWP stuff
|
1136
|
+
my $dirname;
|
1137
|
+
my $i=0;
|
1138
|
+
foreach my $file (sort keys %files) {
|
1139
|
+
if (!$files{$file}) {
|
1140
|
+
if (($dirname) = $file =~ m:(.*)/:) {
|
1141
|
+
make_dir($dirname);
|
1142
|
+
}
|
1143
|
+
hftp_get($file);
|
1144
|
+
if ($max_batch > 0 && ++$i >= $max_batch) {
|
1145
|
+
push (@errlog,"Batch limit exceeded, mirror run was partial\n");
|
1146
|
+
$num_errors++;
|
1147
|
+
last;
|
1148
|
+
}
|
1149
|
+
}
|
1150
|
+
}
|
1151
|
+
last DOWNLOAD;
|
1152
|
+
};
|
1153
|
+
|
1154
|
+
# http method
|
1155
|
+
/^http$/ && do {
|
1156
|
+
# LWP stuff
|
1157
|
+
my $dirname;
|
1158
|
+
my $i=0;
|
1159
|
+
foreach my $file (sort keys %files) {
|
1160
|
+
if (!$files{$file}) {
|
1161
|
+
if (($dirname) = $file =~ m:(.*)/:) {
|
1162
|
+
make_dir($dirname);
|
1163
|
+
}
|
1164
|
+
http_get($file);
|
1165
|
+
if ($max_batch > 0 && ++$i >= $max_batch) {
|
1166
|
+
push (@errlog,"Batch limit exceeded, mirror run was partial\n");
|
1167
|
+
$num_errors++;
|
1168
|
+
last;
|
1169
|
+
}
|
1170
|
+
}
|
1171
|
+
}
|
1172
|
+
last DOWNLOAD;
|
1173
|
+
};
|
1174
|
+
|
1175
|
+
# ftp method
|
1176
|
+
/^ftp$/ && do {
|
1177
|
+
my $dirname;
|
1178
|
+
my $i=0;
|
1179
|
+
foreach my $file (sort keys %files) {
|
1180
|
+
if (!$files{$file}) {
|
1181
|
+
if (($dirname) = $file =~ m:(.*)/:) {
|
1182
|
+
make_dir($dirname);
|
1183
|
+
}
|
1184
|
+
ftp_get($file);
|
1185
|
+
if ($max_batch > 0 && ++$i >= $max_batch) {
|
1186
|
+
push (@errlog,"Batch limit exceeded, mirror run was partial\n");
|
1187
|
+
$num_errors++;
|
1188
|
+
last;
|
1189
|
+
}
|
1190
|
+
}
|
1191
|
+
}
|
1192
|
+
last DOWNLOAD;
|
1193
|
+
};
|
1194
|
+
|
1195
|
+
# rsync method
|
1196
|
+
/^rsync$/ && do {
|
1197
|
+
my $opt=$rsync_options;
|
1198
|
+
my $fh;
|
1199
|
+
my @result;
|
1200
|
+
my $i=0;
|
1201
|
+
my $j=0;
|
1202
|
+
$opt = "$opt --progress" if $progress;
|
1203
|
+
$opt = "$opt -v" if $verbose;
|
1204
|
+
$opt = "$opt -v" if $debug;
|
1205
|
+
$opt = "$opt -n" if $dry_run;
|
1206
|
+
foreach my $file (sort keys %files) {
|
1207
|
+
if (!$files{$file}) {
|
1208
|
+
my $dirname;
|
1209
|
+
my @dir;
|
1210
|
+
($dirname) = $file =~ m:(.*/):;
|
1211
|
+
@dir= split(/\//, $dirname);
|
1212
|
+
for (0..$#dir) {
|
1213
|
+
push (@result, "" . join('/', @dir[0..$_]) . "/");
|
1214
|
+
}
|
1215
|
+
push (@result, "$file");
|
1216
|
+
if (++$j >= $rsync_batch) {
|
1217
|
+
$j = 0;
|
1218
|
+
($fh, $rsynctempfile) = tempfile();
|
1219
|
+
if (@result) {
|
1220
|
+
@result = sort(@result);
|
1221
|
+
my $prev = "not equal to $result[0]";
|
1222
|
+
@result = grep($_ ne $prev && ($prev = $_, 1), @result);
|
1223
|
+
for (@result) {
|
1224
|
+
print $fh "$_\n";
|
1225
|
+
}
|
1226
|
+
}
|
1227
|
+
system ("rsync --timeout=$timeout $opt $rsyncremote --include-from=$rsynctempfile --exclude='*' $mirrordir");
|
1228
|
+
close $fh;
|
1229
|
+
unlink $rsynctempfile;
|
1230
|
+
foreach my $dest (@result) {
|
1231
|
+
if (-f $dest) {
|
1232
|
+
if (!check_lists($dest)) {
|
1233
|
+
say("$dest failed md5sum check");
|
1234
|
+
$num_errors++;
|
1235
|
+
}
|
1236
|
+
} elsif (!-d $dest) {
|
1237
|
+
say("$dest missing");
|
1238
|
+
$num_errors++;
|
1239
|
+
}
|
1240
|
+
}
|
1241
|
+
@result = ();
|
1242
|
+
}
|
1243
|
+
if ($max_batch > 0 && ++$i >= $max_batch) {
|
1244
|
+
print "Batch limit exceeded, mirror run will be partial\n";
|
1245
|
+
push (@errlog,"Batch limit exceeded, mirror run was partial\n");
|
1246
|
+
$num_errors++;
|
1247
|
+
last;
|
1248
|
+
}
|
1249
|
+
}
|
1250
|
+
}
|
1251
|
+
($fh, $rsynctempfile) = tempfile();
|
1252
|
+
if (@result) {
|
1253
|
+
@result = sort(@result);
|
1254
|
+
my $prev = "not equal to $result[0]";
|
1255
|
+
@result = grep($_ ne $prev && ($prev = $_, 1), @result);
|
1256
|
+
for (@result) {
|
1257
|
+
print $fh "$_\n";
|
1258
|
+
}
|
1259
|
+
system ("rsync --timeout=$timeout $opt $rsyncremote --include-from=$rsynctempfile --exclude='*' $mirrordir");
|
1260
|
+
close $fh;
|
1261
|
+
foreach my $dest (@result) {
|
1262
|
+
if (-f $dest) {
|
1263
|
+
if (!check_lists($dest)) {
|
1264
|
+
say("$dest failed md5sum check");
|
1265
|
+
$num_errors++;
|
1266
|
+
}
|
1267
|
+
} elsif (!-d $dest) {
|
1268
|
+
say("$dest missing");
|
1269
|
+
$num_errors++;
|
1270
|
+
}
|
1271
|
+
}
|
1272
|
+
}
|
1273
|
+
last DOWNLOAD;
|
1274
|
+
};
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
if (! @di_dists) {
|
1278
|
+
download_finished();
|
1279
|
+
}
|
1280
|
+
|
1281
|
+
say("Everything OK. Moving meta files.");
|
1282
|
+
chdir($tempdir) or die "unable to chdir($tempdir): $!\n";
|
1283
|
+
my $res=0;
|
1284
|
+
foreach my $file (`find . -type f`) {
|
1285
|
+
chomp $file;
|
1286
|
+
$file=~s:^\./::;
|
1287
|
+
# this skips diff files if unwanted
|
1288
|
+
next if (!exists $files{$file});
|
1289
|
+
print("Moving $file\n") if ($debug);
|
1290
|
+
if (! $dry_run) {
|
1291
|
+
$res &= unlink($mirrordir."/".$file) if ($mirrordir."/".$file);
|
1292
|
+
"$file" =~ m,(^.*)/,;
|
1293
|
+
make_dir("$mirrordir/$1");
|
1294
|
+
if (!link($file, $mirrordir."/".$file)) {
|
1295
|
+
$res &= system("cp $file $mirrordir/$file");
|
1296
|
+
}
|
1297
|
+
}
|
1298
|
+
}
|
1299
|
+
chdir($mirrordir) or die "chdir $mirrordir: $!";
|
1300
|
+
|
1301
|
+
# Get optional directories using rsync.
|
1302
|
+
rsync_extra(0, @rsync_extra);
|
1303
|
+
|
1304
|
+
# Download D-I images.
|
1305
|
+
if (@di_dists) {
|
1306
|
+
di_get_files();
|
1307
|
+
download_finished();
|
1308
|
+
}
|
1309
|
+
|
1310
|
+
# Update suite->codename symlinks
|
1311
|
+
if (! $omit_suite_symlinks && ! $dry_run) {
|
1312
|
+
my %suites;
|
1313
|
+
opendir (DIR, 'dists') or die "Can't open dists/: $!\n";
|
1314
|
+
foreach my $file (grep (!/^\.\.?$/, readdir (DIR))) {
|
1315
|
+
if (-l "dists/$file") {
|
1316
|
+
my $cur = readlink("dists/$file") or die "Error reading symlink dists/$file: $!";
|
1317
|
+
if (exists $distset{$cur}{suite} &&
|
1318
|
+
($file eq $distset{$cur}{suite} || $file eq "stable-$distset{$cur}{suite}")) {
|
1319
|
+
$suites{$file} = "ok";
|
1320
|
+
} else {
|
1321
|
+
unlink("dists/$file") or die "Failed to remove symlink dists/$file: $!";
|
1322
|
+
}
|
1323
|
+
}
|
1324
|
+
}
|
1325
|
+
closedir (DIR);
|
1326
|
+
|
1327
|
+
foreach my $dist (keys %distset) {
|
1328
|
+
next if (! exists $distset{$dist}{suite});
|
1329
|
+
next if (!-d "dists/$dist");
|
1330
|
+
my $suite = $distset{$dist}{suite};
|
1331
|
+
if (! exists $suites{$suite}) {
|
1332
|
+
symlink("$dist", "dists/$suite") or die "Failed to create symlink dists/$suite: $!";
|
1333
|
+
}
|
1334
|
+
if ($suite eq "proposed-updates"&& !exists $suites{"stable-$suite"}) {
|
1335
|
+
symlink("$dist", "dists/stable-$suite") or die "Failed to create symlink dists/stable-$suite: $!";
|
1336
|
+
}
|
1337
|
+
}
|
1338
|
+
}
|
1339
|
+
|
1340
|
+
# Write out trace file.
|
1341
|
+
if (! $dry_run) {
|
1342
|
+
make_dir("project/trace");
|
1343
|
+
open OUT, ">$tracefile" or die "$tracefile: $!";
|
1344
|
+
print OUT `date -u`;
|
1345
|
+
close OUT;
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
# Post mirror cleanup
|
1349
|
+
cleanup_unknown_files() if ($post_cleanup);
|
1350
|
+
|
1351
|
+
# mirror cleanup for directories
|
1352
|
+
if (! $use_cache && ($cleanup || $post_cleanup)) {
|
1353
|
+
# Remove all empty directories. Not done as part of main cleanup
|
1354
|
+
# to prevent race problems with pool download code, which
|
1355
|
+
# makes directories.. Sort so they are removable in bottom-up
|
1356
|
+
# order.
|
1357
|
+
chdir($mirrordir) or die "chdir $mirrordir: $!";
|
1358
|
+
system("find . -depth -type d ! -name . ! -name .. -print0 | xargs -0 rmdir 2>/dev/null") if (! $dry_run);
|
1359
|
+
}
|
1360
|
+
|
1361
|
+
if ($res != 0) {
|
1362
|
+
die("Failed to move some meta files.");
|
1363
|
+
}
|
1364
|
+
|
1365
|
+
# Save the state cache.
|
1366
|
+
save_state_cache() if $state_cache_days && !$dry_run;
|
1367
|
+
|
1368
|
+
say("All done.");
|
1369
|
+
$lock->release;
|
1370
|
+
print "Errors:\n ".join(" ",@errlog) if (@errlog);
|
1371
|
+
if ($num_errors != 0) {
|
1372
|
+
print "Failed to download files ($num_errors errors)!\n";
|
1373
|
+
exit 1 if (!$ignore_small_errors);
|
1374
|
+
}
|
1375
|
+
|
1376
|
+
exit;
|
1377
|
+
|
1378
|
+
sub print_dl_size {
|
1379
|
+
my $size=shift;
|
1380
|
+
my $unit;
|
1381
|
+
if ($size >= 10*1000*1024) {
|
1382
|
+
$size=int($size/1024/1024);
|
1383
|
+
$unit="MiB";
|
1384
|
+
} elsif ($size >= 10*1000) {
|
1385
|
+
$size=int($size/1024);
|
1386
|
+
$unit="kiB";
|
1387
|
+
} else {
|
1388
|
+
$unit="B";
|
1389
|
+
}
|
1390
|
+
return "$size $unit";
|
1391
|
+
}
|
1392
|
+
|
1393
|
+
sub add_bytes_gotten {
|
1394
|
+
my $size=shift;
|
1395
|
+
$bytes_gotten += $size;
|
1396
|
+
if ($doing_meta) {
|
1397
|
+
$bytes_meta += $size;
|
1398
|
+
}
|
1399
|
+
}
|
1400
|
+
|
1401
|
+
# Pass this function a filename, a file size (bytes), and a md5sum (hex).
|
1402
|
+
# Size is always checked; checking the md5sum is optional. However, if
|
1403
|
+
# a value of -1 is passed for size, a check of the md5sum is forced.
|
1404
|
+
# It will return true if the tests show the file matches.
|
1405
|
+
sub check_file {
|
1406
|
+
my ($filename, $size, $md5sum)=@_;
|
1407
|
+
if (-f $filename and ($size == -s _ || $size == -1)) {
|
1408
|
+
if ($check_md5sums || $size == -1) {
|
1409
|
+
open HANDLE, $filename or
|
1410
|
+
die "$filename: $!";
|
1411
|
+
$md5->addfile(*HANDLE);
|
1412
|
+
my $digest = $md5->hexdigest;
|
1413
|
+
return ($md5sum eq $digest);
|
1414
|
+
}
|
1415
|
+
else {
|
1416
|
+
# Assume it is ok, w/o md5 check.
|
1417
|
+
return 1;
|
1418
|
+
}
|
1419
|
+
}
|
1420
|
+
return 0;
|
1421
|
+
}
|
1422
|
+
|
1423
|
+
# Always checks both file size and sha1 as the files get updated (this is
|
1424
|
+
# similar to what is done in check_lists, which forces check_md5sums).
|
1425
|
+
sub check_i18n {
|
1426
|
+
my ($filename, $size, $sha1)=@_;
|
1427
|
+
my $digest = Digest::SHA1->new;
|
1428
|
+
my $ret = 0;
|
1429
|
+
|
1430
|
+
if (-f "$filename" and ($size == -s _)) {
|
1431
|
+
open HANDLE, $filename or die "$filename: $!";
|
1432
|
+
$digest->addfile(*HANDLE);
|
1433
|
+
$ret = ($sha1 eq $digest->hexdigest);
|
1434
|
+
}
|
1435
|
+
return $ret;
|
1436
|
+
}
|
1437
|
+
|
1438
|
+
# Check uncompressed diff content against sha1sum from Index file.
|
1439
|
+
sub check_diff {
|
1440
|
+
my ($filename, $size, $sha1) = @_;
|
1441
|
+
my $digest = Digest::SHA1->new;
|
1442
|
+
my $ret = 0;
|
1443
|
+
|
1444
|
+
if (-f "$filename.gz") {
|
1445
|
+
system_redirect_io("gzip -d", "$filename.gz", "$filename");
|
1446
|
+
if ($size == -s $filename) {
|
1447
|
+
open HANDLE, $filename or die "$filename: $!";
|
1448
|
+
$digest->addfile(*HANDLE);
|
1449
|
+
$ret = ($sha1 eq $digest->hexdigest);
|
1450
|
+
}
|
1451
|
+
unlink ($filename);
|
1452
|
+
}
|
1453
|
+
return $ret;
|
1454
|
+
}
|
1455
|
+
|
1456
|
+
# Check file against md5sum and size from the Release file.
|
1457
|
+
# It will return true if the md5sum matches.
|
1458
|
+
sub check_lists {
|
1459
|
+
my $file = shift;
|
1460
|
+
my $t = $check_md5sums;
|
1461
|
+
my $ret = 1;
|
1462
|
+
$check_md5sums = 1;
|
1463
|
+
if (exists $file_lists{$file}) {
|
1464
|
+
$ret = check_file($file, $file_lists{$file}{size}, $file_lists{$file}{md5});
|
1465
|
+
}
|
1466
|
+
$check_md5sums = $t;
|
1467
|
+
return $ret;
|
1468
|
+
}
|
1469
|
+
|
1470
|
+
sub remote_get {
|
1471
|
+
my $file=shift;
|
1472
|
+
my $tdir=shift;
|
1473
|
+
my $res;
|
1474
|
+
return 1 if ($skippackages);
|
1475
|
+
$tdir=$tempdir unless $tdir;
|
1476
|
+
chdir($tdir) or die "unable to chdir($tdir): $!\n";
|
1477
|
+
|
1478
|
+
METHOD: {
|
1479
|
+
$_ = $download_method;
|
1480
|
+
|
1481
|
+
/^hftp$/ && do {
|
1482
|
+
$res=hftp_get($file);
|
1483
|
+
$res=$res && check_lists($file);
|
1484
|
+
if (!$res) {
|
1485
|
+
say("$file failed md5sum check, removing");
|
1486
|
+
unlink($file) if (-f $file);
|
1487
|
+
}
|
1488
|
+
};
|
1489
|
+
|
1490
|
+
/^http$/ && do {
|
1491
|
+
$res=http_get($file);
|
1492
|
+
$res=$res && check_lists($file);
|
1493
|
+
if (!$res) {
|
1494
|
+
say("$file failed md5sum check, removing");
|
1495
|
+
unlink($file) if (-f $file);
|
1496
|
+
}
|
1497
|
+
};
|
1498
|
+
|
1499
|
+
/^ftp$/ && do {
|
1500
|
+
$res=ftp_get($file);
|
1501
|
+
$res=$res && check_lists($file);
|
1502
|
+
if (!$res) {
|
1503
|
+
say("$file failed md5sum check, removing");
|
1504
|
+
unlink($file) if (-f $file);
|
1505
|
+
}
|
1506
|
+
};
|
1507
|
+
|
1508
|
+
/^rsync$/ && do {
|
1509
|
+
$res=rsync_get($file);
|
1510
|
+
$res=$res && check_lists($file);
|
1511
|
+
if (!$res) {
|
1512
|
+
say("$file failed md5sum check");
|
1513
|
+
# FIXME: make sure the size doesn't match so it gets retried
|
1514
|
+
}
|
1515
|
+
};
|
1516
|
+
}
|
1517
|
+
|
1518
|
+
chdir($mirrordir) or die "unable to chdir($mirrordir): $!\n";
|
1519
|
+
return $res;
|
1520
|
+
}
|
1521
|
+
|
1522
|
+
# Get a file via hftp, first displaying its filename if progress is on.
|
1523
|
+
sub hftp_get {
|
1524
|
+
my $oldautoflush = $|;
|
1525
|
+
$| = 1;
|
1526
|
+
my $file=shift;
|
1527
|
+
my $url="ftp://${host}/${remoteroot}/${file}";
|
1528
|
+
my $ret=1;
|
1529
|
+
|
1530
|
+
print "$url => " if ($debug);
|
1531
|
+
if ($progress || $verbose) {
|
1532
|
+
print "Getting: $file... ";
|
1533
|
+
}
|
1534
|
+
if (! $dry_run) {
|
1535
|
+
unlink($file) if (-f $file);
|
1536
|
+
$ret = $ua->mirror($url, $file);
|
1537
|
+
print $ret->status_line . "\n" if ($debug);
|
1538
|
+
if ($ret->is_error) {
|
1539
|
+
$files{$file} = -1;
|
1540
|
+
warn "$file failed " . $ret->status_line . "\n" if ($progress or $verbose);
|
1541
|
+
push (@errlog,"Download of $file failed: ".$ret->status_line."\n");
|
1542
|
+
$num_errors++;
|
1543
|
+
} elsif ($progress || $verbose) {
|
1544
|
+
print "ok\n";
|
1545
|
+
}
|
1546
|
+
$ret = not ( $ret->is_error );
|
1547
|
+
} elsif ($progress || $verbose) {
|
1548
|
+
print "ok\n";
|
1549
|
+
}
|
1550
|
+
$| = $oldautoflush;
|
1551
|
+
return $ret;
|
1552
|
+
}
|
1553
|
+
|
1554
|
+
# Get a file via http, first displaying its filename if progress is on.
|
1555
|
+
sub http_get {
|
1556
|
+
my $oldautoflush = $|;
|
1557
|
+
$| = 1;
|
1558
|
+
my $file=shift;
|
1559
|
+
my $percent = 0;
|
1560
|
+
my $url="http://${host}/${remoteroot}/${file}";
|
1561
|
+
my $ret=1;
|
1562
|
+
$percent = sprintf("%3.0f",(($bytes_gotten/$bytes_to_get)*100)) unless($bytes_to_get == 0);
|
1563
|
+
|
1564
|
+
print "$url => " if ($debug);
|
1565
|
+
if ($progress || $verbose) {
|
1566
|
+
print "[$percent%] Getting: $file... ";
|
1567
|
+
}
|
1568
|
+
if (! $dry_run) {
|
1569
|
+
unlink($file) if (-f $file);
|
1570
|
+
$ret = $ua->mirror($url, $file);
|
1571
|
+
print $ret->status_line . "\n" if ($debug);
|
1572
|
+
if ($ret->is_error) {
|
1573
|
+
$files{$file} = -1;
|
1574
|
+
warn "$file failed " . $ret->status_line . "\n" if ($progress or $verbose);
|
1575
|
+
push (@errlog,"Download of $file failed: ".$ret->status_line."\n");
|
1576
|
+
$num_errors++;
|
1577
|
+
} elsif ($progress || $verbose) {
|
1578
|
+
print "ok\n";
|
1579
|
+
}
|
1580
|
+
$ret = not ( $ret->is_error );
|
1581
|
+
} elsif ($progress || $verbose) {
|
1582
|
+
print "ok\n";
|
1583
|
+
}
|
1584
|
+
# Account for actual bytes gotten
|
1585
|
+
my @stat = stat $file;
|
1586
|
+
add_bytes_gotten($stat[7]) if (@stat);
|
1587
|
+
|
1588
|
+
$| = $oldautoflush;
|
1589
|
+
return $ret;
|
1590
|
+
}
|
1591
|
+
|
1592
|
+
# Get a file via ftp, first displaying its filename if progress is on.
|
1593
|
+
# I should just be able to subclass Net::Ftp and override the get method,
|
1594
|
+
# but it's late.
|
1595
|
+
sub ftp_get {
|
1596
|
+
my $oldautoflush = $|;
|
1597
|
+
$| = 1;
|
1598
|
+
my $file=shift;
|
1599
|
+
my $percent = 0;
|
1600
|
+
my $mtime;
|
1601
|
+
$percent = sprintf("%3.0f",(($bytes_gotten/$bytes_to_get)*100)) unless($bytes_to_get == 0);
|
1602
|
+
|
1603
|
+
my @stat = stat $file;
|
1604
|
+
if (@stat) { # already have the file?
|
1605
|
+
my $size = $ftp->size($file);
|
1606
|
+
my $mtime = $ftp->mdtm($file);
|
1607
|
+
if ($mtime && $size
|
1608
|
+
&& $size == $stat[7]
|
1609
|
+
&& $mtime == $stat[9]) { # size and time match
|
1610
|
+
print "[$percent%] Keeping: $file\n" if ($progress || $verbose);
|
1611
|
+
add_bytes_gotten($size);
|
1612
|
+
return 1;
|
1613
|
+
}
|
1614
|
+
}
|
1615
|
+
if ($progress) {
|
1616
|
+
print "[$percent%] Getting: $file\t #";
|
1617
|
+
} elsif ($verbose) {
|
1618
|
+
print "[$percent%] Getting: $file";
|
1619
|
+
}
|
1620
|
+
my $ret=1;
|
1621
|
+
if (! $dry_run) {
|
1622
|
+
unlink($file) if (-f $file);
|
1623
|
+
$ret = $ftp->get($file, $file);
|
1624
|
+
if ($ret) {
|
1625
|
+
my $mtime=$ftp->mdtm($file);
|
1626
|
+
utime($mtime, $mtime, $file) if defined $mtime;
|
1627
|
+
} else {
|
1628
|
+
$files{$file} = -1;
|
1629
|
+
warn " failed:".$ftp->message if ($progress or $verbose);
|
1630
|
+
push (@errlog,"Download of $file failed: ".$ftp->message."\n");
|
1631
|
+
$num_errors++;
|
1632
|
+
}
|
1633
|
+
}
|
1634
|
+
my $size=$ftp->size($file);
|
1635
|
+
add_bytes_gotten($size) if $size;
|
1636
|
+
$| = $oldautoflush;
|
1637
|
+
print "\n" if (($verbose and not $progress) or ($dry_run and $progress));
|
1638
|
+
return $ret;
|
1639
|
+
}
|
1640
|
+
|
1641
|
+
sub rsync_get {
|
1642
|
+
my $file=shift;
|
1643
|
+
my $opt=$rsync_options;
|
1644
|
+
(my $dirname) = $file =~ m:(.*/):;
|
1645
|
+
my @dir= split(/\//, $dirname);
|
1646
|
+
for (0..$#dir) {
|
1647
|
+
$opt = "$opt --include=" . join('/', @dir[0..$_]) . "/";
|
1648
|
+
}
|
1649
|
+
$opt = "$opt --progress" if $progress;
|
1650
|
+
$opt = "$opt -v" if $debug;
|
1651
|
+
system ("rsync --timeout=$timeout $opt $rsyncremote --include=$file --exclude='*' .");
|
1652
|
+
if ($? == 0 && -f $file) {
|
1653
|
+
return 1;
|
1654
|
+
} else {
|
1655
|
+
$files{$file} = -1;
|
1656
|
+
push (@errlog,"Download of $file failed\n");
|
1657
|
+
$num_errors++;
|
1658
|
+
return 0;
|
1659
|
+
}
|
1660
|
+
}
|
1661
|
+
|
1662
|
+
sub rsync_extra {
|
1663
|
+
my ($early, @extras) = @_;
|
1664
|
+
my @includes;
|
1665
|
+
|
1666
|
+
# @ignores is updated during $early to prevent removal of files
|
1667
|
+
# if cleanup is done early.
|
1668
|
+
for my $type (@extras) {
|
1669
|
+
if ($early) {
|
1670
|
+
if ($type eq "trace") {
|
1671
|
+
push(@includes, "- /project/trace/$hostname");
|
1672
|
+
push(@includes, "/project/trace/*");
|
1673
|
+
push(@ignores, "^project/trace/");
|
1674
|
+
say("Update remote trace files (using rsync).");
|
1675
|
+
} elsif ($type eq "doc") {
|
1676
|
+
push(@ignores, "^doc/");
|
1677
|
+
push(@ignores, "^README*");
|
1678
|
+
} elsif ($type eq "tools") {
|
1679
|
+
push(@ignores, "^tools/");
|
1680
|
+
} elsif ($type eq "indices") {
|
1681
|
+
push(@ignores, "^indices/");
|
1682
|
+
}
|
1683
|
+
} else {
|
1684
|
+
if ($type eq "doc") {
|
1685
|
+
push(@includes, "/doc/***");
|
1686
|
+
push(@includes, "/README*");
|
1687
|
+
} elsif ($type eq "tools") {
|
1688
|
+
push(@includes, "/tools/***");
|
1689
|
+
} elsif ($type eq "indices") {
|
1690
|
+
push(@includes, "/indices/***");
|
1691
|
+
}
|
1692
|
+
}
|
1693
|
+
}
|
1694
|
+
return if (! @includes);
|
1695
|
+
if (! $early) {
|
1696
|
+
@extras = grep(!/^trace$/, @extras); # drop 'trace' from list
|
1697
|
+
say("Update extra files (using rsync): @extras.");
|
1698
|
+
}
|
1699
|
+
rsync_extra_get(@includes);
|
1700
|
+
}
|
1701
|
+
|
1702
|
+
sub rsync_extra_get {
|
1703
|
+
my @includes = @_;
|
1704
|
+
my $fh;
|
1705
|
+
my @result;
|
1706
|
+
|
1707
|
+
my $opt=$rsync_options;
|
1708
|
+
$opt = "$opt --progress" if $progress;
|
1709
|
+
$opt = "$opt -v" if $verbose;
|
1710
|
+
$opt = "$opt -v" if $debug;
|
1711
|
+
$opt = "$opt -n" if $dry_run;
|
1712
|
+
|
1713
|
+
($fh, $rsynctempfile) = tempfile();
|
1714
|
+
foreach my $line (@includes) {
|
1715
|
+
if ($line !~ /^- /) {
|
1716
|
+
my $dirname;
|
1717
|
+
my @dir;
|
1718
|
+
($dirname) = ($line =~ m:(.*/):);
|
1719
|
+
@dir= split(/\//, $dirname);
|
1720
|
+
for (1..$#dir) {
|
1721
|
+
push (@result, "" . join('/', @dir[0..$_]) . "/");
|
1722
|
+
}
|
1723
|
+
}
|
1724
|
+
push (@result, "$line");
|
1725
|
+
}
|
1726
|
+
for (@result) {
|
1727
|
+
print $fh "$_\n";
|
1728
|
+
}
|
1729
|
+
system ("rsync --timeout=$timeout $opt $rsyncremote --delete --include-from=$rsynctempfile --exclude='*' $mirrordir");
|
1730
|
+
close $fh;
|
1731
|
+
unlink $rsynctempfile;
|
1732
|
+
}
|
1733
|
+
|
1734
|
+
# run system() with stdin and stdout redirected to files
|
1735
|
+
# unlinks stdout target file first to break hard links
|
1736
|
+
sub system_redirect_io {
|
1737
|
+
my ($command, $fromfile, $tofile) = @_;
|
1738
|
+
|
1739
|
+
if (-f $tofile) {
|
1740
|
+
unlink($tofile) or die "unlink($tofile) failed: $!";
|
1741
|
+
}
|
1742
|
+
system("$command <$fromfile >$tofile");
|
1743
|
+
}
|
1744
|
+
|
1745
|
+
sub split_dist {
|
1746
|
+
my $dist = shift;
|
1747
|
+
my ($dist_raw) = ($dist =~ m:^([^/]+)/?:);
|
1748
|
+
$dist =~ m:^[^/]+(/.*)?$:;
|
1749
|
+
my $dist_sdir = $1 // "";
|
1750
|
+
return ($dist_raw, $dist_sdir);
|
1751
|
+
}
|
1752
|
+
|
1753
|
+
sub get_release {
|
1754
|
+
my ($tdir, $dist) = @_;
|
1755
|
+
|
1756
|
+
make_dir ("$tdir");
|
1757
|
+
return 0 unless remote_get("dists/$dist/Release", "$tempdir/.tmp");
|
1758
|
+
my $t = $num_errors;
|
1759
|
+
return 0 unless remote_get("dists/$dist/Release.gpg", "$tempdir/.tmp");
|
1760
|
+
# Check for gpg
|
1761
|
+
if (!$ignore_release_gpg) {
|
1762
|
+
if (system("gpgv --version >/dev/null 2>/dev/null")) {
|
1763
|
+
say("gpgv failed: --ignore-release-gpg or gpgv binary missing?");
|
1764
|
+
push (@errlog,"gpgv failed: --ignore-release-gpg or gpgv binary missing?\n");
|
1765
|
+
$num_errors++;
|
1766
|
+
}
|
1767
|
+
# Verify Release signature
|
1768
|
+
if (-f "$tdir/Release.gpg" || -f "$tdir/Release") {
|
1769
|
+
my $gpgv_res="failed";
|
1770
|
+
open GPGV, "gpgv 2>/dev/null --status-fd 1 $tdir/Release.gpg $tdir/Release|";
|
1771
|
+
while (<GPGV>) {
|
1772
|
+
$gpgv_res="valid" if /^\[GNUPG:\] VALIDSIG/;
|
1773
|
+
}
|
1774
|
+
close GPGV;
|
1775
|
+
if ($gpgv_res eq "failed" || $debug) {
|
1776
|
+
system("gpgv --status-fd 1 $tdir/Release.gpg $tdir/Release");
|
1777
|
+
}
|
1778
|
+
if ($verbose && !$debug) {
|
1779
|
+
system("gpgv --status-fd 1 $tdir/Release.gpg $tdir/Release >/dev/null");
|
1780
|
+
}
|
1781
|
+
if ($gpgv_res eq "failed") {
|
1782
|
+
say("Release signature does not verify.");
|
1783
|
+
push (@errlog,"Release signature does not verify\n");
|
1784
|
+
$num_errors++;
|
1785
|
+
}
|
1786
|
+
} else {
|
1787
|
+
say("Release signature does not verify, file missing.");
|
1788
|
+
push (@errlog,"Release signature does not verify\n");
|
1789
|
+
$num_errors++;
|
1790
|
+
}
|
1791
|
+
}
|
1792
|
+
$num_errors=$t if ($ignore_release_gpg);
|
1793
|
+
return 1
|
1794
|
+
}
|
1795
|
+
|
1796
|
+
sub name_release {
|
1797
|
+
my ($type, $tdir, $dist) = @_;
|
1798
|
+
my ($buf, $origin, $codename, $suite);
|
1799
|
+
|
1800
|
+
if (-f "$tdir/Release") {
|
1801
|
+
if (open RELEASE, "<$tdir/Release") {
|
1802
|
+
while (<RELEASE>) {
|
1803
|
+
last if /^MD5Sum:/;
|
1804
|
+
$buf = $buf . $_;
|
1805
|
+
}
|
1806
|
+
close RELEASE;
|
1807
|
+
}
|
1808
|
+
|
1809
|
+
$_ = $buf;
|
1810
|
+
($origin) = m/^Origin:\s+(.*)/im;
|
1811
|
+
($codename) = m/^Codename:\s+(.*)/im;
|
1812
|
+
($suite) = m/^Suite:\s+(.*)/im;
|
1813
|
+
} elsif ($ignore_release) {
|
1814
|
+
$origin = "none";
|
1815
|
+
}
|
1816
|
+
|
1817
|
+
# Allow for for example "<codename|suite>/updates"; split into the
|
1818
|
+
# raw dist (codename or suite) and the subdirectory.
|
1819
|
+
my ($dist_raw, $dist_sdir) = split_dist($dist);
|
1820
|
+
|
1821
|
+
if ($origin eq "none") {
|
1822
|
+
$codename = $dist_raw;
|
1823
|
+
} elsif ($origin eq "Ubuntu") {
|
1824
|
+
if ($suite) {
|
1825
|
+
say("Ubuntu Release file: using Suite ($suite).");
|
1826
|
+
$codename = $suite;
|
1827
|
+
} else {
|
1828
|
+
say("Invalid Ubuntu Release file.");
|
1829
|
+
push (@errlog,"Invalid Ubuntu Release file.\n");
|
1830
|
+
$num_errors++;
|
1831
|
+
next;
|
1832
|
+
}
|
1833
|
+
} elsif ($codename) {
|
1834
|
+
if ($dist_raw ne $codename && $dist_raw ne $suite) {
|
1835
|
+
say("Broken Release file: neither Codename nor Suite matches $dist.");
|
1836
|
+
push (@errlog,"Broken Release file: neither Codename nor Suite matches $dist\n");
|
1837
|
+
$num_errors++;
|
1838
|
+
next;
|
1839
|
+
}
|
1840
|
+
} elsif ($suite) {
|
1841
|
+
say("Release file does not contain Codename; using Suite ($suite).");
|
1842
|
+
$codename = $suite;
|
1843
|
+
} else {
|
1844
|
+
say("Release file contains neither Codename nor Suite; using $dist.");
|
1845
|
+
$codename = $dist_raw;
|
1846
|
+
}
|
1847
|
+
# For experimental the suite is the same as the codename
|
1848
|
+
$suite = "" if (! $suite || $suite eq $codename);
|
1849
|
+
|
1850
|
+
die("Duplicate dist $codename$dist_sdir.\n")
|
1851
|
+
if exists $distset{"$codename$dist_sdir"}{$type};
|
1852
|
+
$distset{"$codename$dist_sdir"}{$type} = 1;
|
1853
|
+
die("Conflicting suites '$suite' and '$distset{$codename}{suite}' for $codename.\n")
|
1854
|
+
if (exists $distset{"$codename"}{suite} && ($suite ne $distset{$codename}{suite}));
|
1855
|
+
$distset{$codename}{suite} = "$suite" if ($suite);
|
1856
|
+
|
1857
|
+
# This should be a one-time conversion only
|
1858
|
+
if ($suite) {
|
1859
|
+
if (-d "$tempdir/dists/$suite" && !-l "$tempdir/dists/$suite") {
|
1860
|
+
rename_distdir("$tempdir/dists", $codename, $suite);
|
1861
|
+
}
|
1862
|
+
if (-d "dists/$suite" && !-l "dists/$suite") {
|
1863
|
+
rename_distdir("dists", $codename, $suite);
|
1864
|
+
}
|
1865
|
+
}
|
1866
|
+
|
1867
|
+
return ($codename, $suite, $dist_sdir);
|
1868
|
+
}
|
1869
|
+
|
1870
|
+
# Get Index file in the passed subdirectory.
|
1871
|
+
sub get_index {
|
1872
|
+
my $subdir=shift;
|
1873
|
+
my $file=shift;
|
1874
|
+
make_dir($subdir);
|
1875
|
+
make_dir("$tempdir/$subdir");
|
1876
|
+
|
1877
|
+
if (!($diff_mode eq "none") && exists $file_lists{"$tempdir/$subdir/$file.diff/Index"}) {
|
1878
|
+
if (!check_lists ("$tempdir/$subdir/$file.diff/Index")) {
|
1879
|
+
make_dir("$tempdir/$subdir/$file.diff");
|
1880
|
+
say("$subdir/$file.diff/Index needs fetch");
|
1881
|
+
if (!remote_get("$subdir/$file.diff/Index")) {
|
1882
|
+
push (@errlog,"$subdir/$file.diff/Index failed md5sum check, removing\n");
|
1883
|
+
} else {
|
1884
|
+
fetch_and_apply_diffs(0, $subdir, $file);
|
1885
|
+
if (check_lists ("$tempdir/$subdir/$file")) {
|
1886
|
+
system_redirect_io("gzip -9 -n", "$tempdir/$subdir/$file", "$tempdir/$subdir/$file.gz");
|
1887
|
+
system_redirect_io("bzip2", "$tempdir/$subdir/$file", "$tempdir/$subdir/$file.bz2");
|
1888
|
+
}
|
1889
|
+
}
|
1890
|
+
} else {
|
1891
|
+
$bytes_gotten += $file_lists{"$tempdir/$subdir/$file.diff/Index"}{size};
|
1892
|
+
fetch_and_apply_diffs(0, $subdir, "$file");
|
1893
|
+
if (check_lists ("$tempdir/$subdir/$file")) {
|
1894
|
+
system_redirect_io("gzip -9 -n", "$tempdir/$subdir/$file", "$tempdir/$subdir/$file.gz");
|
1895
|
+
system_redirect_io("bzip2", "$tempdir/$subdir/$file", "$tempdir/$subdir/$file.bz2");
|
1896
|
+
}
|
1897
|
+
}
|
1898
|
+
$files{"$subdir/$file.diff/Index"}=1 if ($diff_mode eq "mirror");
|
1899
|
+
$files{"$tempdir/$subdir/$file.diff/Index"}=1;
|
1900
|
+
}
|
1901
|
+
|
1902
|
+
if (exists $file_lists{"$tempdir/$subdir/$file.gz"}{size}) {
|
1903
|
+
if (!check_lists ("$tempdir/$subdir/$file.gz")) {
|
1904
|
+
say("$subdir/$file.gz needs fetch");
|
1905
|
+
if (remote_get("$subdir/$file.gz")) {
|
1906
|
+
system_redirect_io("gzip -d", "$tempdir/$subdir/$file.gz", "$tempdir/$subdir/$file");
|
1907
|
+
system_redirect_io("bzip2", "$tempdir/$subdir/$file", "$tempdir/$subdir/$file.bz2");
|
1908
|
+
} else {
|
1909
|
+
push (@errlog,"$subdir/$file.gz failed md5sum check\n");
|
1910
|
+
$num_errors++;
|
1911
|
+
}
|
1912
|
+
} else {
|
1913
|
+
$bytes_gotten += $file_lists{"$tempdir/$subdir/$file.gz"}{size};
|
1914
|
+
}
|
1915
|
+
} elsif ($ignore_release) {
|
1916
|
+
say("Ignoring missing Release file for $subdir/$file.gz");
|
1917
|
+
push (@errlog,"Ignoring missing Release file for $subdir/$file.gz\n");
|
1918
|
+
say("$subdir/$file.gz needs fetch");
|
1919
|
+
if (remote_get("$subdir/$file.gz")) {
|
1920
|
+
system_redirect_io("gzip -d", "$tempdir/$subdir/$file.gz", "$tempdir/$subdir/$file");
|
1921
|
+
}
|
1922
|
+
} else {
|
1923
|
+
if (-f "$subdir/$file.gz") {
|
1924
|
+
say("$subdir/$file.gz exists locally but not in Release");
|
1925
|
+
die "Won't mirror without $subdir/$file.gz signature in Release";
|
1926
|
+
} else {
|
1927
|
+
say("$subdir/$file.gz does not exist locally or in Release, skipping.") if ($debug);
|
1928
|
+
}
|
1929
|
+
}
|
1930
|
+
if (exists $file_lists{"$tempdir/$subdir/$file"}) {
|
1931
|
+
if (!check_lists ("$tempdir/$subdir/$file")) {
|
1932
|
+
say("$subdir/$file needs fetch");
|
1933
|
+
if (remote_get("$subdir/$file")) {
|
1934
|
+
system_redirect_io("bzip2", "$tempdir/$subdir/$file", "$tempdir/$subdir/$file.bz2");
|
1935
|
+
} else {
|
1936
|
+
push (@errlog,"$subdir/$file failed md5sum check\n");
|
1937
|
+
$num_errors++;
|
1938
|
+
}
|
1939
|
+
} else {
|
1940
|
+
$bytes_gotten += $file_lists{"$tempdir/$subdir/$file"}{size};
|
1941
|
+
}
|
1942
|
+
}
|
1943
|
+
if (exists $file_lists{"$tempdir/$subdir/$file.bz2"}) {
|
1944
|
+
if (!check_lists ("$tempdir/$subdir/$file.bz2")) {
|
1945
|
+
say("$subdir/$file.bz2 needs fetch");
|
1946
|
+
if (!remote_get("$subdir/$file.bz2")) {
|
1947
|
+
push (@errlog,"$subdir/$file.bz2 failed md5sum check, removing\n");
|
1948
|
+
}
|
1949
|
+
} else {
|
1950
|
+
$bytes_gotten += $file_lists{"$tempdir/$subdir/$file.bz2"}{size};
|
1951
|
+
}
|
1952
|
+
}
|
1953
|
+
if (exists $file_lists{"$tempdir/$subdir/Release"}) {
|
1954
|
+
if (!check_lists ("$tempdir/$subdir/Release")) {
|
1955
|
+
say("$subdir/Release needs fetch");
|
1956
|
+
if (!remote_get("$subdir/Release")) {
|
1957
|
+
push (@errlog,"$subdir/Release failed md5sum check, removing\n");
|
1958
|
+
}
|
1959
|
+
} else {
|
1960
|
+
$bytes_gotten += $file_lists{"$tempdir/$subdir/Release"}{size};
|
1961
|
+
}
|
1962
|
+
}
|
1963
|
+
if ($file eq "Packages") {
|
1964
|
+
push @package_files, "$tempdir/$subdir/$file";
|
1965
|
+
} else {
|
1966
|
+
if ($file eq "Sources") {
|
1967
|
+
push @source_files, "$tempdir/$subdir/$file";
|
1968
|
+
} else {
|
1969
|
+
die "get_index called with unknown type $file\n";
|
1970
|
+
}
|
1971
|
+
}
|
1972
|
+
$files{"$subdir/$file.gz"}=1;
|
1973
|
+
$files{"$subdir/$file.bz2"}=1;
|
1974
|
+
# Uncompressed files are no longer kept on the mirrors
|
1975
|
+
$files{"$subdir/$file"}=1 unless exists $file_lists{"$tempdir/$subdir/$file.gz"};
|
1976
|
+
$files{"$subdir/Release"}=1;
|
1977
|
+
$files{"$tempdir/$subdir/$file.gz"}=1;
|
1978
|
+
$files{"$tempdir/$subdir/$file.bz2"}=1;
|
1979
|
+
$files{"$tempdir/$subdir/$file"}=1;
|
1980
|
+
$files{"$tempdir/$subdir/Release"}=1;
|
1981
|
+
}
|
1982
|
+
|
1983
|
+
sub update_contents {
|
1984
|
+
my ($subdir, $file) = @_;
|
1985
|
+
|
1986
|
+
my $file_ok = check_lists("$tempdir/$subdir/$file.gz");
|
1987
|
+
|
1988
|
+
# Get the Index file for the diffs
|
1989
|
+
if (exists $file_lists{"$tempdir/$subdir/$file.diff/Index"}) {
|
1990
|
+
if (!check_lists ("$tempdir/$subdir/$file.diff/Index")) {
|
1991
|
+
make_dir("$tempdir/$subdir/$file.diff");
|
1992
|
+
say("$subdir/$file.diff/Index needs fetch");
|
1993
|
+
if (!remote_get("$subdir/$file.diff/Index")) {
|
1994
|
+
push (@errlog,"$subdir/$file.diff/Index failed md5sum check, removing\n");
|
1995
|
+
return $file_ok;
|
1996
|
+
}
|
1997
|
+
#FIXME: before download
|
1998
|
+
if (-f "$tempdir/$subdir/$file.diff/Index") {
|
1999
|
+
$bytes_to_get += -s "$tempdir/$subdir/$file.diff/Index";
|
2000
|
+
}
|
2001
|
+
}
|
2002
|
+
$files{"$subdir/$file.diff/Index"}=1 if ($diff_mode eq "mirror");
|
2003
|
+
$files{"$tempdir/$subdir/$file.diff/Index"}=1;
|
2004
|
+
} else {
|
2005
|
+
return $file_ok;
|
2006
|
+
}
|
2007
|
+
|
2008
|
+
if (! -f "$tempdir/$subdir/$file.gz" || $file_ok) {
|
2009
|
+
# fetch diffs only
|
2010
|
+
fetch_and_apply_diffs(1, $subdir, $file);
|
2011
|
+
return $file_ok;
|
2012
|
+
}
|
2013
|
+
|
2014
|
+
# Uncompress the Contents file
|
2015
|
+
system_redirect_io("gzip -d", "$tempdir/$subdir/$file.gz", "$tempdir/$subdir/$file");
|
2016
|
+
# Update it
|
2017
|
+
fetch_and_apply_diffs(0, $subdir, $file);
|
2018
|
+
# And compress it again
|
2019
|
+
if (-f "$tempdir/$subdir/$file") {
|
2020
|
+
system_redirect_io("gzip -9 -n", "$tempdir/$subdir/$file", "$tempdir/$subdir/$file.gz");
|
2021
|
+
unlink "$tempdir/$subdir/$file";
|
2022
|
+
}
|
2023
|
+
|
2024
|
+
return check_lists("$tempdir/$subdir/$file.gz");
|
2025
|
+
}
|
2026
|
+
|
2027
|
+
sub get_contents_files {
|
2028
|
+
my $first = 1;
|
2029
|
+
foreach my $dist (keys %distset) {
|
2030
|
+
next unless exists $distset{$dist}{mirror};
|
2031
|
+
foreach my $arch (@arches) {
|
2032
|
+
next if $dist=~/experimental/;
|
2033
|
+
next if $dist=~/.*-proposed-updates/;
|
2034
|
+
next if $arch=~/source/;
|
2035
|
+
if (!check_lists ("$tempdir/dists/$dist/Contents-$arch.gz")) {
|
2036
|
+
if ($first) {
|
2037
|
+
say("Get Contents files.");
|
2038
|
+
$first = 0;
|
2039
|
+
}
|
2040
|
+
remote_get("dists/$dist/Contents-$arch.gz");
|
2041
|
+
}
|
2042
|
+
$files{"dists/$dist/Contents-$arch.gz"}=1;
|
2043
|
+
$files{$tempdir."/"."dists/$dist/Contents-$arch.gz"}=1;
|
2044
|
+
}
|
2045
|
+
}
|
2046
|
+
}
|
2047
|
+
|
2048
|
+
sub get_i18n_index {
|
2049
|
+
my $subdir=shift;
|
2050
|
+
if (exists $file_lists{"$tempdir/$subdir/Index"}) {
|
2051
|
+
make_dir($subdir);
|
2052
|
+
make_dir("$tempdir/$subdir");
|
2053
|
+
|
2054
|
+
if (!check_lists ("$tempdir/$subdir/Index")) {
|
2055
|
+
say("$subdir/Release needs fetch");
|
2056
|
+
if (!remote_get("$subdir/Index")) {
|
2057
|
+
push (@errlog,"$subdir/Index failed md5sum check, removing\n");
|
2058
|
+
}
|
2059
|
+
} else {
|
2060
|
+
$bytes_gotten += $file_lists{"$tempdir/$subdir/Index"}{size};
|
2061
|
+
}
|
2062
|
+
$files{"$subdir/Index"}=1;
|
2063
|
+
$files{"$tempdir/$subdir/Index"}=1;
|
2064
|
+
}
|
2065
|
+
}
|
2066
|
+
|
2067
|
+
sub parse_i18n_index {
|
2068
|
+
my $subdir = shift;
|
2069
|
+
my ($sha1, $size, $filename);
|
2070
|
+
my $exclude = "(".join("|", @excludes).")" if @excludes;
|
2071
|
+
my $include = "(".join("|", @includes).")" if @includes;
|
2072
|
+
|
2073
|
+
# Parse the Index file
|
2074
|
+
if (open INDEX, "<$tempdir/$subdir/Index") {
|
2075
|
+
while (<INDEX>) {
|
2076
|
+
last if /^SHA1:/;
|
2077
|
+
}
|
2078
|
+
while (<INDEX>) {
|
2079
|
+
next unless /^ /;
|
2080
|
+
|
2081
|
+
my ($sha1, $size, $filename) = (/ ([a-z0-9]+) +(\d+) +(.*)$/);
|
2082
|
+
if(!(defined($include) && ($subdir."/".$filename)=~/$include/o)) {
|
2083
|
+
next if (defined($exclude) && ($subdir."/".$filename)=~/$exclude/o);
|
2084
|
+
}
|
2085
|
+
|
2086
|
+
$files{"$subdir/$filename"}=1;
|
2087
|
+
$files{$tempdir."/"."$subdir/$filename"}=1;
|
2088
|
+
if (! check_i18n("$tempdir/$subdir/$filename", $size, $sha1)) {
|
2089
|
+
$bytes_to_get += $size;
|
2090
|
+
$i18n_get{"$subdir/$filename"}{sha1} = $sha1;
|
2091
|
+
$i18n_get{"$subdir/$filename"}{size} = $size;
|
2092
|
+
}
|
2093
|
+
}
|
2094
|
+
close INDEX;
|
2095
|
+
}
|
2096
|
+
}
|
2097
|
+
|
2098
|
+
sub get_i18n_files {
|
2099
|
+
say("Get Translation files.");
|
2100
|
+
foreach my $file (sort keys %i18n_get) {
|
2101
|
+
if (! check_i18n("$tempdir/$file", $i18n_get{$file}{size}, $i18n_get{$file}{sha1})) {
|
2102
|
+
remote_get("$file");
|
2103
|
+
}
|
2104
|
+
}
|
2105
|
+
}
|
2106
|
+
|
2107
|
+
sub fetch_and_apply_diffs {
|
2108
|
+
my ($fetch_only, $subdir, $type) = @_;
|
2109
|
+
local (*INDEX, *FILE);
|
2110
|
+
my (%history_sha1, %history_size, %diff_sha1, %diff_size);
|
2111
|
+
my ($current_sha1, $current_size, $sha1, $size, $file, $digest, $ret);
|
2112
|
+
my $t = $num_errors;
|
2113
|
+
|
2114
|
+
# Parse DiffIndex file
|
2115
|
+
open(INDEX, "$tempdir/$subdir/$type.diff/Index") or die "$tempdir/$subdir/$type.diff/Index: $!";
|
2116
|
+
$_ = <INDEX>;
|
2117
|
+
while (defined($_)) {
|
2118
|
+
if (m/^SHA1-Current:/m) {
|
2119
|
+
($current_sha1, $current_size) = m/^SHA1-Current:\s+([A-Za-z0-9]+)\s+(\d+)/m;
|
2120
|
+
$_ = <INDEX>;
|
2121
|
+
}
|
2122
|
+
elsif (m/^SHA1-History:/m) {
|
2123
|
+
while (defined($_ = <INDEX>)) {
|
2124
|
+
last if (!m/^\s/m);
|
2125
|
+
($sha1, $size, $file) = m/^\s+([A-Za-z0-9]+)\s+(\d+)\s+(.*)/m;
|
2126
|
+
$history_sha1{$file} = $sha1;
|
2127
|
+
$history_size{$file} = $size;
|
2128
|
+
}
|
2129
|
+
}
|
2130
|
+
elsif (m/^SHA1-Patches:/m) {
|
2131
|
+
while (defined($_ = <INDEX>)) {
|
2132
|
+
last if (!m/^\s/m);
|
2133
|
+
($sha1, $size, $file) = m/^\s+([A-Za-z0-9]+)\s+(\d+)\s+(.*)/m;
|
2134
|
+
$diff_sha1{$file} = $sha1;
|
2135
|
+
$diff_size{$file} = $size;
|
2136
|
+
}
|
2137
|
+
}
|
2138
|
+
}
|
2139
|
+
close(INDEX);
|
2140
|
+
|
2141
|
+
# Download diff files as necessary
|
2142
|
+
$ret = 1;
|
2143
|
+
foreach $file (sort keys %diff_sha1) {
|
2144
|
+
if (!check_diff("$tempdir/$subdir/$type.diff/$file", $diff_size{$file}, $diff_sha1{$file})) {
|
2145
|
+
say("$subdir/$type.diff/$file.gz needs fetch");
|
2146
|
+
remote_get("$subdir/$type.diff/$file.gz");
|
2147
|
+
#FIXME: before download
|
2148
|
+
if (-f "$tempdir/$subdir/$type.diff/$file.gz") {
|
2149
|
+
$bytes_to_get += -s "$tempdir/$subdir/$type.diff/$file.gz";
|
2150
|
+
}
|
2151
|
+
if (!check_diff("$tempdir/$subdir/$type.diff/$file", $diff_size{$file}, $diff_sha1{$file})) {
|
2152
|
+
say("$subdir/$type.diff/$file.gz failed sha1sum check, removing");
|
2153
|
+
push (@errlog,"$subdir/$type.diff/$file.gz failed sha1sum check, removing\n");
|
2154
|
+
unlink "$tempdir/$subdir/$type.diff/$file.gz";
|
2155
|
+
$ret = 0;
|
2156
|
+
}
|
2157
|
+
}
|
2158
|
+
$files{"$subdir/$type.diff/$file.gz"}=1 if ($diff_mode eq "mirror");
|
2159
|
+
$files{"$tempdir/$subdir/$type.diff/$file.gz"}=1;
|
2160
|
+
}
|
2161
|
+
$num_errors = $t if ($ignore_small_errors);
|
2162
|
+
return if ($fetch_only || ! $ret);
|
2163
|
+
|
2164
|
+
# Apply diff files
|
2165
|
+
open(FILE, "$tempdir/$subdir/$type") or return;
|
2166
|
+
$digest = Digest::SHA1->new;
|
2167
|
+
$digest->addfile(*FILE);
|
2168
|
+
$sha1 = $digest->hexdigest;
|
2169
|
+
$size = -s "$tempdir/$subdir/$type";
|
2170
|
+
foreach $file (sort keys %history_sha1) {
|
2171
|
+
next unless ($sha1 eq $history_sha1{$file} && $size eq $history_size{$file});
|
2172
|
+
if (system("gzip -d < \"$tempdir/$subdir/$type.diff/$file.gz\" | patch --ed \"$tempdir/$subdir/$type\"")) {
|
2173
|
+
say("Patch $file failed, will fetch $subdir/$type file");
|
2174
|
+
unlink "$tempdir/$subdir/$type";
|
2175
|
+
return;
|
2176
|
+
}
|
2177
|
+
open(FILE, "$tempdir/$subdir/$type") or return;
|
2178
|
+
$digest = Digest::SHA1->new;
|
2179
|
+
$digest->addfile(*FILE);
|
2180
|
+
$sha1 = $digest->hexdigest;
|
2181
|
+
$size = -s "$tempdir/$subdir/$type";
|
2182
|
+
say("$subdir/$type patched with $subdir/$type.diff/$file.gz");
|
2183
|
+
}
|
2184
|
+
if (!($sha1 eq $current_sha1 && $size eq $current_size)) {
|
2185
|
+
say("$subdir/$type failed sha1sum check, removing");
|
2186
|
+
push (@errlog,"$subdir/$type failed sha1sum check, removing\n");
|
2187
|
+
unlink "$tempdir/$subdir/$type";
|
2188
|
+
}
|
2189
|
+
}
|
2190
|
+
|
2191
|
+
# Make a directory including all needed parents.
|
2192
|
+
{
|
2193
|
+
my %seen;
|
2194
|
+
|
2195
|
+
sub make_dir {
|
2196
|
+
my $dir=shift;
|
2197
|
+
|
2198
|
+
my @parts=split('/', $dir);
|
2199
|
+
my $current='';
|
2200
|
+
foreach my $part (@parts) {
|
2201
|
+
$current.="$part/";
|
2202
|
+
if (! $seen{$current}) {
|
2203
|
+
if (! -d $current) {
|
2204
|
+
mkdir ($current, 0755) or die "mkdir failed: $!";
|
2205
|
+
debug("Created directory: $current");
|
2206
|
+
}
|
2207
|
+
$seen{$current}=1;
|
2208
|
+
}
|
2209
|
+
}
|
2210
|
+
}
|
2211
|
+
}
|
2212
|
+
|
2213
|
+
# Mirror cleanup for unknown files that cannot be found in Packages files.
|
2214
|
+
# This subroutine is called on pre- and post-cleanup and takes no arguments.
|
2215
|
+
# It uses some global variables like $files, $mirrordir, @ignores
|
2216
|
+
sub cleanup_unknown_files {
|
2217
|
+
print("Cleanup mirror") if ($verbose or $progress);
|
2218
|
+
if ($use_cache) {
|
2219
|
+
say(": using cache.");
|
2220
|
+
foreach my $file (sort keys %files) {
|
2221
|
+
next if (@di_dists && $file =~ m:installer-\w+/current/images/:);
|
2222
|
+
if ($files{$file} == 2 && -f $file) {
|
2223
|
+
say("deleting $file") if ($verbose);
|
2224
|
+
if (! $dry_run) {
|
2225
|
+
unlink $file or die "unlink $file: $!";
|
2226
|
+
}
|
2227
|
+
}
|
2228
|
+
}
|
2229
|
+
} else {
|
2230
|
+
say($state_cache_days ? ": full." : ".");
|
2231
|
+
chdir($mirrordir) or die "chdir $mirrordir: $!";
|
2232
|
+
my $ignore;
|
2233
|
+
$ignore = "(".join("|", @ignores).")" if @ignores;
|
2234
|
+
# Remove all files in the mirror that we don't know about
|
2235
|
+
foreach my $file (`find . -type f`) {
|
2236
|
+
chomp $file;
|
2237
|
+
$file=~s:^\./::;
|
2238
|
+
next if (@di_dists && $file =~ m:installer-\w+/current/images/:);
|
2239
|
+
unless ((exists $files{$file} && $files{$file} != 2) or
|
2240
|
+
(defined($ignore) && $file=~/$ignore/o)) {
|
2241
|
+
say("deleting $file") if ($verbose);
|
2242
|
+
if (! $dry_run) {
|
2243
|
+
unlink $file or die "unlink $file: $!";
|
2244
|
+
}
|
2245
|
+
}
|
2246
|
+
}
|
2247
|
+
}
|
2248
|
+
# Clean up obsolete files of D-I images.
|
2249
|
+
di_cleanup() if @di_dists;
|
2250
|
+
}
|
2251
|
+
|
2252
|
+
# FIXME: does not work
|
2253
|
+
sub get_http_size {
|
2254
|
+
my $file = shift;
|
2255
|
+
my $url = "http://${host}/${remoteroot}/${file}";
|
2256
|
+
my ($type, $size);
|
2257
|
+
($type, $size) = $ua->head($url);
|
2258
|
+
say("$url -- $size");
|
2259
|
+
return $size;
|
2260
|
+
}
|
2261
|
+
|
2262
|
+
sub di_check_dists {
|
2263
|
+
say("Checking validity of D-I dists.");
|
2264
|
+
DI_DIST:
|
2265
|
+
for my $di_dist (@di_dists) {
|
2266
|
+
if (exists $distset{$di_dist}) {
|
2267
|
+
# Valid dist and also mirroring the archive itself
|
2268
|
+
$distset{$di_dist}{"d-i"} = 1;
|
2269
|
+
} else {
|
2270
|
+
foreach my $dist (keys %distset) {
|
2271
|
+
my ($dist_raw, $dist_sdir) = split_dist($dist);
|
2272
|
+
if ($di_dist eq $distset{$dist_raw}{suite}) {
|
2273
|
+
# Suite specified, use codename instead
|
2274
|
+
$distset{"$dist_raw$dist_sdir"}{"d-i"} = 1;
|
2275
|
+
next DI_DIST;
|
2276
|
+
}
|
2277
|
+
}
|
2278
|
+
# Only mirroring D-I images, not the archive itself
|
2279
|
+
my $tdir="$tempdir/.tmp/dists/$di_dist";
|
2280
|
+
next unless (get_release($tdir, $di_dist) || $ignore_release);
|
2281
|
+
name_release("d-i", $tdir, $di_dist);
|
2282
|
+
unlink "$tdir/Release";
|
2283
|
+
unlink "$tdir/Release.gpg";
|
2284
|
+
}
|
2285
|
+
}
|
2286
|
+
}
|
2287
|
+
|
2288
|
+
sub di_add_files {
|
2289
|
+
my $tdir = "$tempdir/d-i";
|
2290
|
+
my $exclude = "(".join("|", @excludes).")" if @excludes;
|
2291
|
+
my $include = "(".join("|", @includes).")" if @includes;
|
2292
|
+
|
2293
|
+
foreach my $dist (keys %distset) {
|
2294
|
+
next unless exists $distset{$dist}{"d-i"};
|
2295
|
+
foreach my $arch (@di_arches) {
|
2296
|
+
next if $arch =~ /kfreebsd-/;
|
2297
|
+
|
2298
|
+
my $image_dir = "dists/$dist/main/installer-$arch/current/images";
|
2299
|
+
make_dir ("$tdir/$image_dir");
|
2300
|
+
if (!remote_get("$image_dir/MD5SUMS", $tdir)) {
|
2301
|
+
say("Failed to download $image_dir/MD5SUMS; skipping.");
|
2302
|
+
return;
|
2303
|
+
}
|
2304
|
+
if (-f "$tdir/$image_dir/MD5SUMS") {
|
2305
|
+
$bytes_to_get += -s _; # As we did not have the size earlier
|
2306
|
+
}
|
2307
|
+
|
2308
|
+
local $/;
|
2309
|
+
undef $/; # Read whole file
|
2310
|
+
open(FILE, "<", "$tdir/$image_dir/MD5SUMS") or die "$tdir/$image_dir/MD5SUMS: $!";
|
2311
|
+
$_ = <FILE>;
|
2312
|
+
while (m/^([A-Za-z0-9]{32} .*)/mg) {
|
2313
|
+
my ($md5sum, $filename) = split(' ', $1, 3);
|
2314
|
+
$filename =~ s:^\./::;
|
2315
|
+
if(!(defined($include) && ($image_dir."/".$filename)=~/$include/o)) {
|
2316
|
+
next if (defined($exclude) && ($image_dir."/".$filename)=~/$exclude/o);
|
2317
|
+
}
|
2318
|
+
|
2319
|
+
$di_files{$image_dir}{$filename}{md5sum} = $md5sum;
|
2320
|
+
#$di_files{$image_dir}{$filename}{size} = get_http_size("$image_dir/$filename");
|
2321
|
+
|
2322
|
+
# Check against the version currently on the mirror
|
2323
|
+
if (check_file("$image_dir/$filename", -1, $md5sum)) {
|
2324
|
+
$di_files{$image_dir}{$filename}{status} = 1;
|
2325
|
+
} else {
|
2326
|
+
$di_files{$image_dir}{$filename}{status} = 0;
|
2327
|
+
}
|
2328
|
+
}
|
2329
|
+
close(FILE);
|
2330
|
+
}
|
2331
|
+
}
|
2332
|
+
}
|
2333
|
+
|
2334
|
+
# ToDo: for rsync maybe it would make sense to sync the images directly
|
2335
|
+
# into place, the whole $image_dir at a time.
|
2336
|
+
sub di_get_files {
|
2337
|
+
say("Getting Debian Installer images.");
|
2338
|
+
my $tdir = "$tempdir/d-i";
|
2339
|
+
|
2340
|
+
foreach my $image_dir (sort keys %di_files) {
|
2341
|
+
my $lres = 1;
|
2342
|
+
foreach my $file (sort keys %{ $di_files{$image_dir} }) {
|
2343
|
+
next unless $di_files{$image_dir}{$file}{status} == 0;
|
2344
|
+
# Fetch images into a temporary location
|
2345
|
+
$file =~ m:(^.*)/:;
|
2346
|
+
make_dir ("$tdir/$image_dir/$1") if $1;
|
2347
|
+
if (!remote_get("$image_dir/$file", $tdir) ||
|
2348
|
+
!check_file("$tdir/$image_dir/$file", -1, $di_files{$image_dir}{$file}{md5sum})) {
|
2349
|
+
$lres = 0;
|
2350
|
+
last if (! $dry_run);
|
2351
|
+
}
|
2352
|
+
if (-f "$tdir/$image_dir/$file") {
|
2353
|
+
$bytes_to_get += -s _; # As we did not have the size in add_di_files()
|
2354
|
+
}
|
2355
|
+
}
|
2356
|
+
|
2357
|
+
# Move images in place on mirror
|
2358
|
+
if ($lres && ! $dry_run) {
|
2359
|
+
foreach my $file (sort keys %{ $di_files{$image_dir} }) {
|
2360
|
+
next unless $di_files{$image_dir}{$file}{status} == 0;
|
2361
|
+
$file =~ m:(^.*)/:;
|
2362
|
+
make_dir ("$image_dir/$1") if $1;
|
2363
|
+
if (-f "$image_dir/$file") {
|
2364
|
+
unlink "$image_dir/$file";
|
2365
|
+
}
|
2366
|
+
link("$tdir/$image_dir/$file", "$image_dir/$file");
|
2367
|
+
}
|
2368
|
+
# Move the MD5SUMS file in place on mirror
|
2369
|
+
link("$tdir/$image_dir/MD5SUMS", "$image_dir/MD5SUMS");
|
2370
|
+
} elsif (! $dry_run) {
|
2371
|
+
say("Failed to download some files in $image_dir; not updating images.");
|
2372
|
+
}
|
2373
|
+
}
|
2374
|
+
}
|
2375
|
+
|
2376
|
+
sub di_cleanup {
|
2377
|
+
# Clean up obsolete files
|
2378
|
+
foreach my $image_dir (`find dists/ -type d -name images`) {
|
2379
|
+
next unless $image_dir =~ m:/installer-\w+/current/images$:;
|
2380
|
+
chomp $image_dir;
|
2381
|
+
chdir("$image_dir") or die "unable to chdir($image_dir): $!\n";
|
2382
|
+
foreach my $file (`find . -type f`) {
|
2383
|
+
chomp $file;
|
2384
|
+
$file=~s:^\./::;
|
2385
|
+
if (! exists $di_files{$image_dir} || ! exists $di_files{$image_dir}{$file}) {
|
2386
|
+
next if (exists $di_files{$image_dir} && $file eq "MD5SUMS");
|
2387
|
+
say("deleting $image_dir/$file") if ($verbose);
|
2388
|
+
if (! $dry_run) {
|
2389
|
+
unlink "$file" or die "unlink $image_dir/$file: $!\n";
|
2390
|
+
}
|
2391
|
+
}
|
2392
|
+
}
|
2393
|
+
chdir("$mirrordir") or die "unable to chdir($tempdir): $!\n";
|
2394
|
+
}
|
2395
|
+
# Clean up temporary D-I files (silently)
|
2396
|
+
if (-d "$tempdir/d-i") {
|
2397
|
+
chdir("$tempdir/d-i") or die "unable to chdir($tempdir/d-i): $!\n";
|
2398
|
+
foreach my $file (`find . -type f`) {
|
2399
|
+
chomp $file;
|
2400
|
+
$file=~s:^\./::;
|
2401
|
+
unlink "$file" or die "unlink $tempdir/d-i/$file: $!\n";
|
2402
|
+
}
|
2403
|
+
chdir("$mirrordir") or die "unable to chdir($mirrordir): $!\n";
|
2404
|
+
}
|
2405
|
+
}
|
2406
|
+
|
2407
|
+
sub download_finished {
|
2408
|
+
if ($download_method eq 'ftp') { $ftp->quit; }
|
2409
|
+
|
2410
|
+
my $total_time = time - $start_time;
|
2411
|
+
if ($download_method eq 'rsync' || $bytes_gotten == 0) {
|
2412
|
+
say("Download completed in ".$total_time."s.");
|
2413
|
+
} else {
|
2414
|
+
my $avg_speed = 0;
|
2415
|
+
$avg_speed = sprintf("%3.0f",($bytes_gotten / $total_time)) unless ($total_time == 0);
|
2416
|
+
say("Downloaded ".print_dl_size($bytes_gotten)." in ".$total_time."s at ".(int($avg_speed/1024*100)/100)." kiB/s.");
|
2417
|
+
}
|
2418
|
+
}
|
2419
|
+
|
2420
|
+
sub rename_distdir {
|
2421
|
+
my ($dir, $codename, $suite) = @_;
|
2422
|
+
say("The directory for a dist should be its codename, not a suite.");
|
2423
|
+
if (!$allow_dist_rename) {
|
2424
|
+
die("Use --allow-dist-rename to have debmirror do the conversion automatically.\n");
|
2425
|
+
}
|
2426
|
+
say("Starting conversion - renaming '$dir/$suite' to '$dir/$codename':");
|
2427
|
+
if (-l "$dir/$codename") {
|
2428
|
+
say(" removing symlink '$dir/$codename'; a new symlink for the suite will be created later");
|
2429
|
+
unlink "$dir/$codename";
|
2430
|
+
}
|
2431
|
+
if (-d "$dir/$codename") {
|
2432
|
+
die("Directory '$dir/$codename' already exists; aborting conversion.\n");
|
2433
|
+
}
|
2434
|
+
rename("$dir/$suite", "$dir/$codename");
|
2435
|
+
say(" conversion completed successfully");
|
2436
|
+
}
|
2437
|
+
|
2438
|
+
sub save_state_cache {
|
2439
|
+
my $cache_file = "$tempdir/debmirror_state.cache";
|
2440
|
+
say("Saving debmirror state cache.");
|
2441
|
+
foreach my $file (keys %files) {
|
2442
|
+
if ($files{$file} == 2) {
|
2443
|
+
delete $files{$file};
|
2444
|
+
} elsif ($files{$file} >= 0){
|
2445
|
+
$files{$file} = 2;
|
2446
|
+
}
|
2447
|
+
}
|
2448
|
+
# Add state cache meta data
|
2449
|
+
my $now = time();
|
2450
|
+
$files{cache_version} = $files_cache_version;
|
2451
|
+
if (! $state_cache_exptime) {
|
2452
|
+
$state_cache_exptime = $now + $state_cache_days * 24 * 60 * 60;
|
2453
|
+
}
|
2454
|
+
$files{cache_expiration_time} = $state_cache_exptime;
|
2455
|
+
if (! nstore(\%files, $cache_file)) {
|
2456
|
+
say("Failed to save state cache.");
|
2457
|
+
unlink $cache_file if -f $cache_file;
|
2458
|
+
} else {
|
2459
|
+
my $expires = int(($state_cache_exptime - $now) / (60 * 60)); # hours
|
2460
|
+
if ($expires > 0) {
|
2461
|
+
my $days = int($expires / 24);
|
2462
|
+
my $hours = $expires % 24;
|
2463
|
+
say("State cache will expire in " .
|
2464
|
+
($days ? "$days day(s)" : ($hours ? "" : "the next hour")) .
|
2465
|
+
($hours ? ($days ? " and " : "") . "$hours hour(s)" : "") . ".");
|
2466
|
+
} else {
|
2467
|
+
say("State cache expired during this run; next run will not use cache.");
|
2468
|
+
}
|
2469
|
+
}
|
2470
|
+
}
|
2471
|
+
|
2472
|
+
sub load_state_cache {
|
2473
|
+
my $cache_file = "$tempdir/debmirror_state.cache";
|
2474
|
+
if (! -f $cache_file) {
|
2475
|
+
say("State cache file does not exist; doing full mirroring.");
|
2476
|
+
return;
|
2477
|
+
}
|
2478
|
+
|
2479
|
+
my $rfiles;
|
2480
|
+
say("Loading debmirror state cache.");
|
2481
|
+
$rfiles = retrieve($cache_file);
|
2482
|
+
if (! defined $rfiles) {
|
2483
|
+
say("Failed to load state cache; doing full mirror check.");
|
2484
|
+
return
|
2485
|
+
}
|
2486
|
+
if (! exists $$rfiles{cache_version}) {
|
2487
|
+
say("Cache version missing in state cache; doing full mirroring.");
|
2488
|
+
return
|
2489
|
+
} elsif ($$rfiles{cache_version} ne $files_cache_version) {
|
2490
|
+
say("State cache is incompatible with this version of debmirror; doing full mirror check.");
|
2491
|
+
return
|
2492
|
+
} else {
|
2493
|
+
delete $$rfiles{cache_version};
|
2494
|
+
}
|
2495
|
+
if (! exists $$rfiles{cache_expiration_time}) {
|
2496
|
+
say("Expiration time missing in state cache; doing full mirror check.");
|
2497
|
+
return
|
2498
|
+
} elsif ($$rfiles{cache_expiration_time} < time()) {
|
2499
|
+
say("State cache has expired; doing full mirror check.");
|
2500
|
+
return
|
2501
|
+
} else {
|
2502
|
+
$state_cache_exptime = $$rfiles{cache_expiration_time};
|
2503
|
+
delete $$rfiles{cache_expiration_time};
|
2504
|
+
}
|
2505
|
+
|
2506
|
+
say("State cache loaded successfully; will use cache.");
|
2507
|
+
%files = %$rfiles;
|
2508
|
+
$use_cache = 1;
|
2509
|
+
# Preserve state cache during dry runs
|
2510
|
+
if ($dry_run_var) {
|
2511
|
+
$files{$cache_file} = 1;
|
2512
|
+
} else {
|
2513
|
+
unlink $cache_file if -f $cache_file;
|
2514
|
+
}
|
2515
|
+
}
|
2516
|
+
|
2517
|
+
sub say {
|
2518
|
+
print join(' ', @_)."\n" if ($verbose or $progress);
|
2519
|
+
}
|
2520
|
+
|
2521
|
+
sub debug {
|
2522
|
+
print $0.': '.join(' ', @_)."\n" if $debug;
|
2523
|
+
}
|
2524
|
+
|
2525
|
+
=head1 COPYRIGHT
|
2526
|
+
|
2527
|
+
This program is copyright 2001 by Joey Hess <joeyh@debian.org>, under
|
2528
|
+
the terms of the GNU GPL (either version 2 of the licence or, at your
|
2529
|
+
option, any later version), copyright 2001-2002 by Joerg Wendland
|
2530
|
+
<joergland@debian.org>, copyright 2003-2007 by Goswin von Brederlow
|
2531
|
+
<goswin-v-b@web.de> and copyright 2009 by Frans Pop <fjp@debian.org>.
|
2532
|
+
|
2533
|
+
The author disclaims any responsibility for any mangling of your system,
|
2534
|
+
unexpected bandwidth usage bills, meltdown of the Debian mirror network,
|
2535
|
+
etc, that this script may cause. See NO WARRANTY section of GPL.
|
2536
|
+
|
2537
|
+
=head1 AUTHOR
|
2538
|
+
|
2539
|
+
Current maintainer:
|
2540
|
+
Frans Pop <fjp@debian.org>
|
2541
|
+
|
2542
|
+
Previous authors:
|
2543
|
+
Joey Hess <joeyh@debian.org> (original author)
|
2544
|
+
Joerg Wendland <joergland@debian.org>
|
2545
|
+
Goswin von Brederlow <goswin-v-b@web.de>
|
2546
|
+
|
2547
|
+
=head1 MOTTO
|
2548
|
+
|
2549
|
+
Waste bandwith -- put a partial mirror on your laptop today!
|
2550
|
+
|
2551
|
+
=cut
|