Subversion Repositories

?revision_form?Rev ?revision_input??revision_submit??revision_endform?

Rev 5 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 magnus 1
package DebPool::Dirs;
2
 
3
###
4
#
5
# DebPool::Dirs - Module for dealing with directory related tasks
6
#
7
# Copyright 2003-2004 Joel Aelwyn. All rights reserved.
8
# 
9
# Redistribution and use in source and binary forms, with or without
10
# modification, are permitted provided that the following conditions
11
# are met:
12
# 1. Redistributions of source code must retain the above copyright
13
#    notice, this list of conditions and the following disclaimer.
14
# 2. Redistributions in binary form must reproduce the above copyright
15
#    notice, this list of conditions and the following disclaimer in the
16
#    documentation and/or other materials provided with the distribution.
17
# 3. Neither the name of the Author nor the names of any contributors
18
#    may be used to endorse or promote products derived from this software
19
#    without specific prior written permission.
20
# 
21
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
# SUCH DAMAGE.
32
#
33
# $Id: Dirs.pm 71 2006-06-26 21:16:01Z joel $
34
#
35
###
36
 
37
# We use 'our', so we must have at least Perl 5.6
38
 
39
require 5.006_000;
40
 
41
# Always good ideas.
42
 
43
use strict;
44
use warnings;
45
 
46
### Module setup
47
 
48
BEGIN {
49
    use Exporter ();
50
    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
51
 
52
    # Version checking
53
    $VERSION = '0.1.5';
54
 
55
    @ISA = qw(Exporter);
56
 
57
    @EXPORT = qw(
58
    );
59
 
60
    @EXPORT_OK = qw(
61
        &Archfile
62
        &Create_Tree
9 magnus 63
        &Tree_Mkdir
1 magnus 64
        &Monitor_Incoming
65
        &PoolBasePath
66
        &PoolDir
67
        &Scan_Changes
68
        &Scan_All
69
        &Strip_Subsection
70
    );
71
 
72
    %EXPORT_TAGS = (
9 magnus 73
        'functions' => [qw(&Archfile &Create_Tree &Tree_Mkdir &Monitor_Incoming
1 magnus 74
                           &PoolBasePath &PoolDir &Scan_Changes &Scan_All
75
                           &Strip_Subsection)],
76
        'vars' => [qw()],
77
    );
78
}
79
 
80
### Exported package globals
81
 
82
# None
83
 
84
### Non-exported package globals
85
 
86
# Thread-safe? What's that? Package global error value. We don't export
87
# this directly, because it would conflict with other modules.
88
 
89
our($Error);
90
 
91
### File lexicals
92
 
93
# None
94
 
95
### Constant functions
96
 
97
# None
98
 
99
### Meaningful functions
100
 
101
# Create_Tree()
102
#
103
# Creates a full directory tree based on the current directory values in
104
# %DebPool::Config::Options. Returns 1 on success, 0 on failure (and sets
105
# or propagates $Error).
106
 
107
sub Create_Tree {
108
    use DebPool::Config qw(:vars);
109
 
110
    # Basic directories - none of these are terribly exciting. We don't set
111
    # $Error on failure, because Tree_Mkdir will have already done so.
112
 
113
    if (!Tree_Mkdir($Options{'db_dir'}, $Options{'db_dir_mode'})) {
114
        return 0;
115
    }
116
 
117
    if (!Tree_Mkdir($Options{'incoming_dir'}, $Options{'incoming_dir_mode'})) {
118
        return 0;
119
    }
120
 
121
    if (!Tree_Mkdir($Options{'installed_dir'}, $Options{'installed_dir_mode'})) {
122
        return 0;
123
    }
124
 
125
    if (!Tree_Mkdir($Options{'reject_dir'}, $Options{'reject_dir_mode'})) {
126
        return 0;
127
    }
128
 
129
    # Now the distribution directory and subdirectories
130
 
131
    my($dists_dir) = $Options{'dists_dir'};
132
    my($dists_dir_mode) = $Options{'dists_dir_mode'};
133
 
134
    if (!Tree_Mkdir($dists_dir, $dists_dir_mode)) {
135
        return 0;
136
    }
137
 
138
    # Real distributions are the only ones that get directories.
139
 
140
    my($dist);
141
    foreach $dist (@{$Options{'realdists'}}) {
142
        if (!Tree_Mkdir("$dists_dir/$dist", $dists_dir_mode)) {
143
            return 0;
144
        }
145
 
146
        my($section);
147
        foreach $section (@{$Options{'sections'}}) {
148
            if (!Tree_Mkdir("$dists_dir/$dist/$section", $dists_dir_mode)) {
149
                return 0;
150
            }
151
 
152
            my($arch);
153
            foreach $arch (@{$Options{'archs'}}) {
154
                my($target) = "$dists_dir/$dist/$section/";
155
                if ('source' eq $arch) {
156
                    $target .= $arch;
157
                } else {
158
                    $target .= "binary-$arch";
159
                }
160
 
161
                if (!Tree_Mkdir($target, $dists_dir_mode)) {
162
                    return 0;
163
                }
164
            }
165
        }
166
    }
167
 
168
    # Go through all of the distributions looking for those that should be
169
    # symlinks, and creating them if necessary.
170
 
171
    foreach $dist (keys(%{$Options{'dists'}})) {
172
        # Check whether it should be a symlink. If so, make sure it is.
173
 
174
        if (!($dist eq $Options{'dists'}->{$dist})) { # Different names -> sym
175
            if (! -e "$dists_dir/$dist") {
176
                if (!symlink($Options{'dists'}->{$dist}, "$dists_dir/$dist")) {
177
                    $Error = "Couldn't create symlink $dists_dir/$dist -> ";
178
                    $Error .= "$Options{'dists'}->{$dist}: $!";
179
                }
180
            } elsif (! -l "$dists_dir/$dist") {
181
                $Error = "$dists_dir/$dist exists and isn't a symlink, ";
182
                $Error .= "but it should be";
183
                return 0;
184
            }
185
        }
186
    }
187
 
188
    # And, finally, the pool directories and their subdirectories
189
 
190
    my($pool_dir) = $Options{'pool_dir'};
191
    my($pool_dir_mode) = $Options{'pool_dir_mode'};
192
 
193
    if (!Tree_Mkdir($pool_dir, $pool_dir_mode)) {
194
        return 0;
195
    }
196
 
197
    # We can only get away with this because Debian pool directories are
198
    # named in ASCII...
199
 
200
    my($section);
201
    foreach $section (@{$Options{'sections'}}) {
202
        if (!Tree_Mkdir("$pool_dir/$section", $pool_dir_mode)) {
203
            return 0;
204
        }
205
    }
206
 
207
    return 1;
208
}
209
 
210
# Tree_Mkdir($directory, $mode)
211
#
212
# Creates $directory with $mode. Returns 0 and sets $Error on failure, or
213
# 1 on success.
214
#
215
# Internal support function for Create_Tree
216
 
217
sub Tree_Mkdir {
218
    my($dir, $mode) = @_;
219
 
220
    if (-d $dir) {
221
        return 1;
222
    };
223
 
224
    if (-e $dir) {
225
        $Error = "Couldn't create '$dir' - already exists as a non-directory.";
226
        return 0;
227
    }
228
 
229
    if (!mkdir($dir, $mode)) {
230
        $Error = "Couldn't create '$dir': $!";
231
        return 0;
232
    }
233
 
234
    if (!chmod($mode, $dir)) {
235
        $Error = "Couldn't chmod '$dir': $!";
236
        return 0;
237
    }
238
 
239
    return 1;
240
}
241
 
242
# Scan_Changes($directory)
243
#
244
# Scan the specified directory for changes files. Returns an array of
245
# filenames relative to the directory, or undef (and sets $Error) on an error.
246
 
247
sub Scan_Changes {
248
    my($directory) = @_;
249
 
250
    if (!opendir(INCOMING, $directory)) {
251
        $Error = "Couldn't open directory '$directory': $!";
252
        return undef;
253
    }
254
 
255
    # Perl magic - read the directory and grep it for *.changes all at one
256
    # shot.
257
 
258
    my(@changes) = grep(/\.changes$/, readdir(INCOMING));
259
    close(INCOMING);
260
 
261
    return @changes;
262
}
263
 
264
# Scan_All($directory)
265
#
266
# Scans the specified directory and all subdirectories for any files.
267
# Returns an arrayref pointing to an array of filepaths relative to
268
# $directory, or undef (and sets $Error) on failure. Ignores any hidden
269
# files or directories.
270
 
271
sub Scan_All {
272
    my($directory) = @_;
273
 
274
    if (!opendir(DIR, $directory)) {
275
        $Error = "Couldn't open directory '$directory'";
276
        return undef;
277
    }
278
 
279
    my($direntry);
280
    my(@entries) = grep(!/^\./, readdir(DIR));
281
 
282
    my(@return);
283
 
284
    foreach $direntry (@entries) {
285
        if (-f "$directory/$direntry") {
286
            push(@return, $direntry);
287
        } elsif (-d "$directory/$direntry") {
288
            my($recurse) = Scan_All("$directory/$direntry");
289
 
290
            if (!defined($recurse)) { # $Error is already set.
291
                return undef;
292
            }
293
 
294
            # I'd like to use map(), but Perl makes stooopid guesses.
295
 
296
            my($entry);
297
 
298
            foreach $entry (@{$recurse}) {
299
                push(@return, "$direntry/$entry");
300
            }
301
        }
302
    }
303
 
304
    return \@return;
305
}
306
 
307
# Monitor_Incoming()
308
#
309
# Monitors the incoming directory, looping until the directory is updated.
310
# Returns 1 on success, 0 on failure (and sets $Error).
311
 
312
sub Monitor_Incoming {
313
    use DebPool::Config;
314
    use DebPool::Logging qw(:functions :facility :level);
315
 
316
    # If this is ever false, we either shouldn't have been called in the
317
    # first place, or we've caught a signal and shouldn't do anything
318
    # further.
319
 
320
    if ($DebPool::Signal::Signal_Caught) {
321
        return 1;
322
    }
323
 
324
    my(@stat) = stat($Options{'incoming_dir'});
325
    if (!@stat) {
326
        $Error = "Couldn't stat incoming_dir '$Options{'incoming_dir'}'";
327
        return 0;
328
    }
329
    my($mtime) = $stat[9];
330
 
331
    do {
332
        Log_Message("Incoming monitor: sleeping for " .
333
            $Options{'sleep'} . " seconds", LOG_GENERAL, LOG_DEBUG);
334
        sleep($Options{'sleep'});
335
        @stat = stat($Options{'incoming_dir'});
336
        if (!@stat) {
337
            $Error = "Couldn't stat incoming_dir '$Options{'incoming_dir'}'";
338
            return 0;
339
        }
340
    } until (($stat[9] != $mtime) || ($DebPool::Signal::Signal_Caught));
341
 
342
    return 1;
343
}
344
 
345
# PoolDir($name, $section, $archive_base)
346
#
347
# Calculates a pool subdirectory name from the package name and the section
348
# (if provided; assumed to be 'main' if undefined or unrecognized).
349
 
350
sub PoolDir {
351
    my($name, $section, $archive_base) = @_;
352
 
353
    $section = Strip_Subsection($section);
354
 
355
    # Pool subdirectories are normally the first letter of the package
356
    # name, unless it is a lib* package, in which case the subdir is
357
    # lib<first letter>.
358
 
359
    if ($name =~ s/^lib//) { # lib(.).*
360
        return $section . '/' . 'lib' . substr($name, 0, 1);
361
    } else { # (.).*
362
        return $section . '/' . substr($name, 0, 1);
363
    }
364
}
365
 
366
# Strip_Subsection($section)
367
#
368
# This routine could, perhaps, better named. However, the purpose is to
369
# take a Section header as found in a package, and return the 'section'
370
# (rather than [section/]subsection) of it - that is, 'main', 'contrib', or
371
# 'non-free' (normally; it uses the configuration options to track this).
372
#
373
# Any unrecognized section is assumed to be 'main'; section values without
374
# *any* subsection portion succeed, as well (at least, assuming that they
375
# are otherwise valid).
376
 
377
sub Strip_Subsection {
378
    use DebPool::Config qw(:vars);
379
 
380
    my($section) = @_;
381
 
382
    if (!defined($section)) {
383
        return 'main';
384
    }
385
 
386
    my($check_section);
387
    foreach $check_section (@{$Options{'sections'}}) {
388
        if ($section =~ m/^$check_section(\/.+)?$/) {
389
            return $check_section;
390
        }
391
    }
392
 
393
    return 'main';
394
}
395
 
396
# PoolBasePath()
397
#
398
# Calculates the value of the relative path from archive_dir to pool_dir
399
# (this is primarily useful when having to provide file paths relative to
400
# archive_dir, such as in Packages/Sources files). This does assume that
401
# pool_dir is a subdirectory of archive_dir, but if that isn't true then
402
# things are royally screwed up *anyway*...
403
 
404
sub PoolBasePath {
405
    use DebPool::Config qw(:vars);
406
 
407
    my($path) = $Options{'pool_dir'};
408
    $path =~ s/^$Options{'archive_dir'}\///;
409
    return $path;
410
}
411
 
412
# Archfile($archive, $component, $architecture, $dironly)
413
#
414
# Returns the file name for the Packages/Sources file, or the directory
415
# name of the arch directory if $dironly is true, (from a base of
416
# dists_dir) for the specified archive, component, and architecture.
417
 
418
sub Archfile {
419
    my($archive) = shift(@_);
420
    my($component) = shift(@_);
421
    my($architecture) = shift(@_);
422
    my($dironly) = shift(@_);
423
 
424
    my($result) = "$archive/$component";
425
 
426
    my($type);
427
    if ('source' eq $architecture) {
428
        $result .= "/${architecture}";
429
        $type = "Sources";
430
    } else {
431
        $result .= "/binary-${architecture}";
432
        $type = "Packages";
433
    }
434
 
435
    if (!$dironly) {
436
        $result .= "/${type}";
437
    }
438
 
439
    return $result;
440
}
441
 
442
END {}
443
 
444
1;
445
 
446
__END__
447
 
448
# vim:set tabstop=4 expandtab: