~vcs-imports/debconf/svn

« back to all changes in this revision

Viewing changes to src/debconf/Debconf/DbDriver/PackageDir.pm

  • Committer: joeyh
  • Date: 2011-02-02 00:33:44 UTC
  • Revision ID: svn-v4:a4a2c43b-8ac3-0310-8836-e0e880c912e2:trunk:2516
moved to git

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/perl -w
2
 
 
3
 
=head1 NAME
4
 
 
5
 
Debconf::DbDriver::PackageDir - store database in a directory
6
 
 
7
 
=cut
8
 
 
9
 
package Debconf::DbDriver::PackageDir;
10
 
use strict;
11
 
use Debconf::Log qw(:all);
12
 
use IO::File;
13
 
use Fcntl qw(:DEFAULT :flock);
14
 
use Debconf::Iterator;
15
 
use base 'Debconf::DbDriver::Directory';
16
 
 
17
 
=head1 DESCRIPTION
18
 
 
19
 
This is a debconf database driver that uses a plain text file for each
20
 
individual "subdirectory" of the internal tree.
21
 
 
22
 
It uses a Format module to handle reading and writing the files, so the
23
 
files can be of any format.
24
 
 
25
 
=head1 FIELDS
26
 
 
27
 
=over 4
28
 
 
29
 
=item directory
30
 
 
31
 
The directory to put the files in.
32
 
 
33
 
=item extension
34
 
 
35
 
An optional extension to tack on the end of each filename.
36
 
 
37
 
=item mode
38
 
 
39
 
The (octal) permissions to create the files with if they do not exist.
40
 
Defaults to 600, since the files could contain passwords in some 
41
 
circumstances.
42
 
 
43
 
=item format
44
 
 
45
 
The Format object to use for reading and writing files. 
46
 
 
47
 
In the config file, just the name of the format to use, such as '822' can
48
 
be specified. Default is 822.
49
 
 
50
 
=back
51
 
 
52
 
=head1 METHODS
53
 
 
54
 
=cut
55
 
 
56
 
use fields qw(mode _loaded);
57
 
 
58
 
=head2 init
59
 
 
60
 
On initialization, we ensure that the directory exists.
61
 
 
62
 
=cut
63
 
 
64
 
sub init {
65
 
        my $this=shift;
66
 
 
67
 
        if (exists $this->{mode}) {
68
 
                # Convert user input to octal.
69
 
                $this->{mode} = oct($this->{mode});
70
 
        }
71
 
        else {
72
 
                $this->{mode} = 0600;
73
 
        }
74
 
        $this->SUPER::init(@_);
75
 
}
76
 
 
77
 
=head2 loadfile(filename)
78
 
 
79
 
Loads up a file by name, after checking to make sure it's not ben loaded
80
 
already. Omit the directory from the filename.
81
 
 
82
 
=cut
83
 
 
84
 
sub loadfile {
85
 
        my $this=shift;
86
 
        my $file=$this->{directory}."/".shift;
87
 
 
88
 
        return if $this->{_loaded}->{$file};
89
 
        $this->{_loaded}->{$file}=1;
90
 
        
91
 
        debug "db $this->{name}" => "loading $file";
92
 
        return unless -e $file;
93
 
 
94
 
        my $fh=IO::File->new;
95
 
        open($fh, $file) or $this->error("$file: $!");
96
 
        my @item = $this->{format}->read($fh);
97
 
        while (@item) {
98
 
                $this->cacheadd(@item);
99
 
                @item = $this->{format}->read($fh);
100
 
        }
101
 
        close $fh;
102
 
}
103
 
 
104
 
=head2 load(itemname)
105
 
 
106
 
After checking the cache, find the file that contains the item, then use
107
 
the format object to load up the item (and all other items from that file,
108
 
which get cached).
109
 
 
110
 
=cut
111
 
 
112
 
sub load {
113
 
        my $this=shift;
114
 
        my $item=shift;
115
 
        $this->loadfile($this->filename($item));
116
 
}
117
 
 
118
 
=head2 filename(itemname)
119
 
 
120
 
Converts the item name into a filename. (Minus the base directory.)
121
 
 
122
 
=cut
123
 
 
124
 
sub filename {
125
 
        my $this=shift;
126
 
        my $item=shift;
127
 
 
128
 
        if ($item =~ m!^([^/]+)(?:/|$)!) {
129
 
                return $1.$this->{extension};
130
 
        }
131
 
        else {
132
 
                $this->error("failed parsing item name \"$item\"\n");
133
 
        }
134
 
}
135
 
 
136
 
=head2 iterator
137
 
 
138
 
This iterator is not very well written in general, as it loads up all files
139
 
that were not previously loaded, and then lets the super class iterate over
140
 
the populated cache. However, all iteration in debconf so far iterates over
141
 
the whole set, so it doesn't matter.
142
 
 
143
 
=cut
144
 
 
145
 
sub iterator {
146
 
        my $this=shift;
147
 
        
148
 
        my $handle;
149
 
        opendir($handle, $this->{directory}) ||
150
 
                $this->error("opendir: $!");
151
 
 
152
 
        while (my $file=readdir($handle)) {
153
 
                next if length $this->{extension} and
154
 
                        not $file=~m/$this->{extension}/;
155
 
                next unless -f $this->{directory}."/".$file;
156
 
                next if $file eq '.lock' || $file =~ /-old$/;
157
 
                $this->loadfile($file);
158
 
        }
159
 
 
160
 
        # grandparent's method; parent's does unwanted stuff
161
 
        $this->SUPER::iterator;
162
 
}
163
 
 
164
 
=head2 exists(itemname)
165
 
 
166
 
Check the cache first, then check to see if a file that might contain the
167
 
item exists, load it, and test existence. 
168
 
 
169
 
=cut
170
 
 
171
 
sub exists {
172
 
        my $this=shift;
173
 
        my $name=shift;
174
 
        # Check the cache first.
175
 
        my $incache=$this->Debconf::DbDriver::Cache::exists($name);
176
 
        return $incache if (!defined $incache or $incache);
177
 
        my $file=$this->{directory}.'/'.$this->filename($name);
178
 
        return unless -e $file;
179
 
 
180
 
        $this->load($name);
181
 
        
182
 
        # Now check the cache again; if it exists load will have put it
183
 
        # into the cache.
184
 
        return $this->Debconf::DbDriver::Cache::exists($name);
185
 
}
186
 
 
187
 
=head2 shutdown
188
 
 
189
 
This has to break the abstraction and access the underlying cache directly.
190
 
 
191
 
=cut
192
 
 
193
 
sub shutdown {
194
 
        my $this=shift;
195
 
 
196
 
        return if $this->{readonly};
197
 
 
198
 
        my (%files, %filecontents, %killfiles, %dirtyfiles);
199
 
        foreach my $item (keys %{$this->{cache}}) {
200
 
                my $file=$this->filename($item);
201
 
                $files{$file}++;
202
 
                
203
 
                if (! defined $this->{cache}->{$item}) {
204
 
                        $killfiles{$file}++;
205
 
                        delete $this->{cache}->{$item};
206
 
                }
207
 
                else {
208
 
                        push @{$filecontents{$file}}, $item;
209
 
                }
210
 
 
211
 
                if ($this->{dirty}->{$item}) {
212
 
                        $dirtyfiles{$file}++;
213
 
                        $this->{dirty}->{$item}=0;
214
 
                }
215
 
        }
216
 
 
217
 
        foreach my $file (keys %files) {
218
 
                if (! $filecontents{$file} && $killfiles{$file}) {
219
 
                        debug "db $this->{name}" => "removing $file";
220
 
                        my $filename=$this->{directory}."/".$file;
221
 
                        unlink $filename or
222
 
                                $this->error("unable to remove $filename: $!");
223
 
                        if (-e $filename."-old") {
224
 
                                unlink $filename."-old" or
225
 
                                        $this->error("unable to remove $filename-old: $!");
226
 
                        }
227
 
                }
228
 
                elsif ($dirtyfiles{$file}) {
229
 
                        debug "db $this->{name}" => "saving $file";
230
 
                        my $filename=$this->{directory}."/".$file;
231
 
                
232
 
                        sysopen(my $fh, $filename."-new",
233
 
                                        O_WRONLY|O_TRUNC|O_CREAT,$this->{mode}) or
234
 
                                $this->error("could not write $filename-new: $!");
235
 
                        $this->{format}->beginfile;
236
 
                        foreach my $item (@{$filecontents{$file}}) {
237
 
                                $this->{format}->write($fh, $this->{cache}->{$item}, $item)
238
 
                                        or $this->error("could not write $filename-new: $!");
239
 
                        }
240
 
                        $this->{format}->endfile;
241
 
 
242
 
                        # Ensure -new is flushed.
243
 
                        $fh->flush or $this->error("could not flush $filename-new: $!");
244
 
                        # Ensure it is synced, because I've had problems with
245
 
                        # disk caching resulting in truncated files.
246
 
                        $fh->sync or $this->error("could not sync $filename-new: $!");
247
 
 
248
 
                        # Now rename the old file to -old (if doing backups),
249
 
                        # and put -new in its place.
250
 
                        if (-e $filename && $this->{backup}) {
251
 
                                rename($filename, $filename."-old") or
252
 
                                        debug "db $this->{name}" => "rename failed: $!";
253
 
                        }
254
 
                        rename($filename."-new", $filename) or
255
 
                                $this->error("rename failed: $!");
256
 
                }
257
 
        }
258
 
        
259
 
        $this->SUPER::shutdown(@_);
260
 
        return 1;
261
 
}
262
 
 
263
 
=head1 AUTHOR
264
 
 
265
 
Joey Hess <joeyh@debian.org>
266
 
 
267
 
=cut
268
 
 
269
 
1