~cjwatson/debconf/dbus

« back to all changes in this revision

Viewing changes to Debconf/DbDriver/LDAP.pm

  • Committer: joeyh
  • Date: 2007-09-04 21:45:56 UTC
  • Revision ID: vcs-imports@canonical.com-20070904214556-2o6clh7qf8goknir
* Applied Davor Ocelic's patch adding a keybykey option to the LDAP
  DbDriver. Closes: #440857
  (Note that it currently has some minor uninitialised value warnings.)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/perl -w
2
2
# Copyright (C) 2002 Matthew Palmer.
 
3
# Copyright (C) 2007 Davor Ocelic.
3
4
 
4
5
=head1 NAME
5
6
 
61
62
bind DN and password should be reserved for the occasional case where you
62
63
wish to update the debconf configuration data.
63
64
 
 
65
=item keybykey
 
66
 
 
67
Enable access to individual LDAP entries, instead of fetching them
 
68
all at once in the beginning. This is very useful if you want to monitor your
 
69
LDAP logs for specific debconf keys requested. In this way, you could also
 
70
write custom handling code on the LDAP server part.
 
71
 
 
72
Note that when this option is enabled, the connection to the LDAP server
 
73
is kept active during the whole Debconf run. This is a little different
 
74
from the all-in-one behavior where two brief connections are made to LDAP;
 
75
in the beginning to retrieve all the entries, and in the end to save 
 
76
eventual changes.
 
77
 
64
78
=back
65
79
 
66
80
=cut
67
81
 
68
 
use fields qw(server port basedn binddn bindpasswd exists);
 
82
use fields qw(server port basedn binddn bindpasswd exists keybykey ds);
69
83
 
70
84
=head1 METHODS
71
85
 
90
104
        debug "db $this->{name}" => "talking to $this->{server}, data under $this->{basedn}";
91
105
 
92
106
        # Whee, LDAP away!  Net::LDAP tells us about all these methods.
93
 
        my $ds = Net::LDAP->new($this->{server}, port => $this->{port}, version => 3);
94
 
        if (! $ds) {
 
107
        $this->{ds} = Net::LDAP->new($this->{server}, port => $this->{port}, version => 3);
 
108
        if (! $this->{ds}) {
95
109
                $this->error("Unable to connect to LDAP server");
96
110
                return; # if not fatal, give up anyway
97
111
        }
100
114
        my $rv = "";
101
115
        if (!($this->{binddn} && $this->{bindpasswd})) {
102
116
                debug "db $this->{name}" => "binding anonymously; hope that's OK";
103
 
                $rv = $ds->bind;
 
117
                $rv = $this->{ds}->bind;
104
118
        } else {
105
119
                debug "db $this->{name}" => "binding as $this->{binddn}";
106
 
                $rv = $ds->bind($this->{binddn}, password => $this->{bindpasswd});
 
120
                $rv = $this->{ds}->bind($this->{binddn}, password => $this->{bindpasswd});
107
121
        }
108
122
        if ($rv->code) {
109
123
                $this->error("Bind Failed: ".$rv->error);
110
124
        }
111
125
        
112
 
        return $ds;
 
126
        return $this->{ds};
113
127
}
114
128
 
115
129
=head2 init
117
131
On initialization, connect to the directory, read all of the debconf data
118
132
into the cache, and close off the connection again.
119
133
 
 
134
If KeyByKey is enabled, then skip the complete data load and only retrieve
 
135
few keys required by debconf.
 
136
 
120
137
=cut
121
138
 
122
139
sub init {
123
140
        my $this = shift;
124
141
 
125
142
        $this->SUPER::init(@_);
126
 
        
127
 
        debug "db $this->{name}" => "getting database data";
128
 
        my $ds=$this->binddb;
129
 
        return unless $ds;
130
 
        my $data = $ds->search(base => $this->{basedn}, sizelimit => 0, timelimit => 0, filter => "(objectclass=debconfDbEntry)");
131
 
        if ($data->code) {
132
 
                $this->error("Search failed: ".$data->error);
133
 
        }
134
 
                
135
 
        # Every language does LDAP search() returns fairly similarly.  Perl's
136
 
        # modus is documented in Net::LDAP::Search, for those interested.
137
 
        my $records = $data->as_struct();
138
 
        debug "db $this->{name}" => "Read ".$data->count()." entries";  
 
143
 
 
144
        $this->binddb;
 
145
        return unless $this->{ds};
139
146
 
140
147
        # A record of all the existing entries in the DB so we know which
141
148
        # ones need to added, and which modified
142
149
        $this->{exists} = {};
143
 
 
144
 
        # This is a rather great honking loop, but it's quite simply a nested
145
 
        # bunch of loops iterating through every DN, attribute, and value in
146
 
        # the returned set (the complete debconf database) and storing it in
147
 
        # a format which the cache driver hopefully understands.
148
 
        foreach my $dn (keys %{$records}) {
149
 
                my $entry = $records->{$dn};
150
 
                debug "db $this->{name}" => "Reading data from $dn";
151
 
                my %ret = (owners => {},
152
 
                        fields => {},
153
 
                        variables => {},
154
 
                        flags => {},
155
 
                );
156
 
                my $name = "";
157
 
                                        
158
 
                foreach my $attr (keys %{$entry}) {
159
 
                        if ($attr eq 'objectclass') {
160
 
                                next;
161
 
                        }
162
 
                        debug "db $this->{name}" => "Setting data for $attr";
163
 
                        my $values = $entry->{$attr};
164
 
                        foreach my $val (@{$values}) {
165
 
                                debug "db $this->{name}" => "$attr = $val";
166
 
                                if ($attr eq 'owners') {
167
 
                                        $ret{owners}->{$val}=1;
168
 
                                } elsif ($attr eq 'flags') {
169
 
                                        $ret{flags}->{$val}='true';
170
 
                                } elsif ($attr eq 'cn') {
171
 
                                        $name = $val;
172
 
                                } elsif ($attr eq 'variables') {
173
 
                                        my ($var, $value)=split(/\s*=\s*/, $val, 2);
174
 
                                        $ret{variables}->{$var}=$value;
175
 
                                } else {
176
 
                                        $val=~s/\\n/\n/g;
177
 
                                        $ret{fields}->{$attr}=$val;
178
 
                                }
179
 
                        }
 
150
        
 
151
        if ($this->{keybykey}) {
 
152
                debug "db $this->{name}" => "will get database data key by key";
 
153
        }
 
154
        else {
 
155
                debug "db $this->{name}" => "getting database data";
 
156
                my $data = $this->{ds}->search(base => $this->{basedn}, sizelimit => 0, timelimit => 0, filter => "(objectclass=debconfDbEntry)");
 
157
                if ($data->code) {
 
158
                        $this->error("Search failed: ".$data->error);
180
159
                }
181
 
 
182
 
                $this->{cache}->{$name} = \%ret;
183
 
                $this->{exists}->{$name} = 1;
 
160
                        
 
161
                my $records = $data->as_struct();
 
162
                debug "db $this->{name}" => "Read ".$data->count()." entries";  
 
163
        
 
164
                parse_records($records);
 
165
        
 
166
                $this->{ds}->unbind;
184
167
        }
185
 
        
186
 
        # Having done all of that, all that remains is to clean up.
187
 
        $ds->unbind;
188
168
}
189
169
 
190
170
=head2 shutdown
206
186
                return 1;
207
187
        }
208
188
        
209
 
        my $ds=$this->binddb;
210
 
        return unless $ds;
 
189
        unless ($this->{keybykey}) {
 
190
                $this->binddb;
 
191
                return unless $this->{ds};
 
192
        }
211
193
 
212
194
        foreach my $item (keys %{$this->{cache}}) {
213
195
                next unless defined $this->{cache}->{$item};  # skip deleted
256
238
                
257
239
                my $rv="";
258
240
                if ($this->{exists}->{$item}) {
259
 
                        $rv = $ds->modify($entry_dn, replace => \%modify_data);
 
241
                        $rv = $this->{ds}->modify($entry_dn, replace => \%modify_data);
260
242
                } else {
261
 
                        $rv = $ds->add($entry_dn, attrs => $add_data);
 
243
                        $rv = $this->{ds}->add($entry_dn, attrs => $add_data);
262
244
                }
263
245
                if ($rv->code) {
264
246
                        $this->error("Modify failed: ".$rv->error);
265
247
                }
266
248
        }
267
249
 
268
 
        $ds->unbind();
 
250
        $this->{ds}->unbind();
269
251
 
270
252
        $this->SUPER::shutdown(@_);
271
253
}
272
 
                                
273
 
# Empty routine
274
 
 
275
 
sub load {}
276
 
 
277
 
=sub remove
 
254
 
 
255
=head2 load 
 
256
 
 
257
Empty routine for all-in-one db fetch, but does some actual
 
258
work for individual keys retrieval.
 
259
 
 
260
=cut
 
261
 
 
262
sub load {
 
263
        my $this = shift;
 
264
        return unless $this->{keybykey};
 
265
        my $entry_cn = shift;
 
266
 
 
267
        my $records = $this->get_key($entry_cn);
 
268
        return unless $records;
 
269
                
 
270
        debug "db $this->{name}" => "Read entry for $entry_cn";
 
271
 
 
272
        $this->parse_records($records);
 
273
}
 
274
 
 
275
=head2 remove
278
276
 
279
277
Called by Cache::shutdown, nothing to do because already done in LDAP::shutdown
280
278
 
284
282
        return 1;
285
283
}
286
284
 
287
 
=sub save
 
285
=head2 save
288
286
 
289
287
Called by Cache::shutdown, nothing to do because already done in LDAP::shutdown
290
288
 
294
292
        return 1;
295
293
}
296
294
 
 
295
=head2 get_key
 
296
 
 
297
Retrieve individual key from LDAP db.
 
298
The function is a no-op if KeyByKey is disabled.
 
299
Returns entry->as_struct if found, undef otherwise.
 
300
 
 
301
=cut
 
302
 
 
303
sub get_key {
 
304
        my $this = shift;
 
305
        return unless $this->{keybykey};
 
306
        my $entry_cn = shift;
 
307
 
 
308
        my $data = $this->{ds}->search(
 
309
                base => $this->{basedn},
 
310
                sizelimit => 0,
 
311
                timelimit => 0,
 
312
                filter => "(&(cn=$entry_cn)(objectclass=debconfDbEntry))");
 
313
 
 
314
        if ($data->code) {
 
315
                $this->error("Search failed: ".$data->error);
 
316
        }
 
317
 
 
318
        return unless $data->entries;
 
319
        $data->as_struct();
 
320
}
 
321
 
 
322
# Parse struct data (such as one returned by get_key())
 
323
# into internal hash/cache representation
 
324
sub parse_records {
 
325
        my $this = shift;
 
326
        my $records = shift;
 
327
 
 
328
        # This is a rather great honking loop, but it's quite simply a nested
 
329
        # bunch of loops iterating through every DN, attribute, and value in
 
330
        # the returned set (the complete debconf database) and storing it in
 
331
        # a format which the cache driver hopefully understands.
 
332
        foreach my $dn (keys %{$records}) {
 
333
                my $entry = $records->{$dn};
 
334
                debug "db $this->{name}" => "Reading data from $dn";
 
335
                my %ret = (owners => {},
 
336
                        fields => {},
 
337
                        variables => {},
 
338
                        flags => {},
 
339
                );
 
340
                my $name = "";
 
341
 
 
342
                foreach my $attr (keys %{$entry}) {
 
343
                        if ($attr eq 'objectclass') {
 
344
                                next;
 
345
                        }
 
346
                        debug "db $this->{name}" => "Setting data for $attr";
 
347
                        my $values = $entry->{$attr};
 
348
                        foreach my $val (@{$values}) {
 
349
                                debug "db $this->{name}" => "$attr = $val";
 
350
                                if ($attr eq 'owners') {
 
351
                                        $ret{owners}->{$val}=1;
 
352
                                } elsif ($attr eq 'flags') {
 
353
                                        $ret{flags}->{$val}='true';
 
354
                                } elsif ($attr eq 'cn') {
 
355
                                        $name = $val;
 
356
                                } elsif ($attr eq 'variables') {
 
357
                                        my ($var, $value)=split(/\s*=\s*/, $val, 2);
 
358
                                        $ret{variables}->{$var}=$value;
 
359
                                } else {
 
360
                                        $val=~s/\\n/\n/g;
 
361
                                        $ret{fields}->{$attr}=$val;
 
362
                                }
 
363
                        }
 
364
                }
 
365
 
 
366
                $this->{cache}->{$name} = \%ret;
 
367
                $this->{exists}->{$name} = 1;
 
368
        }
 
369
}
 
370
 
297
371
=head1 AUTHOR
298
372
 
299
373
Matthew Palmer <mpalmer@ieee.org>
300
374
 
 
375
Davor Ocelic <debconf@spinlocksolutions.com> -
 
376
Added KeyByKey support for http://infrastructures.spinlocksolutions.com/
 
377
 
301
378
=cut
302
379
 
303
380
1