File Coverage

blib/lib/NIS/DBM.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # $Id: DBM.pm,v 1.14 1999/09/24 20:31:02 jgsmith Exp $
2             #
3             # Copyright (c) 1999, Texas A&M University
4             # All rights reserved.
5             #
6             # Redistribution and use in source and binary forms, with or without
7             # modification, are permitted provided that the following conditions
8             # are met:
9             # 1. Redistributions of source code must retain the above copyright
10             # notice, this list of conditions and the following disclaimer.
11             # 2. Redistributions in binary form must reproduce the above copyright
12             # notice, this list of conditions and the following disclaimer in the
13             # documentation and/or other materials provided with the distribution.
14             # 3. Neither the name of the University nor the names of its contributors
15             # may be used to endorse or promote products derived from this software
16             # without specific prior written permission.
17             #
18             # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTERS ``AS IS''
19             # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21             # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22             # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24             # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26             # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27             # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28             # POSSIBILITY OF SUCH DAMAGE.
29              
30             package NIS::DBM;
31              
32 1     1   780 use strict;
  1         2  
  1         35  
33 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         1  
  1         78  
34 1     1   2069 use IniConf;
  0            
  0            
35             use Carp;
36             use NDBM_File;
37             use Net::NIS;
38             use Fcntl;
39             use IPC::Open3;
40             use LockFile::Simple qw(lock unlock);
41              
42             @ISA = qw( );
43              
44             $VERSION = '0.02';
45              
46             sub whowasi {
47             return ((caller(1))[3]);
48             }
49              
50             sub configure {
51             my $self = shift;
52              
53             my $configfile = $self->{'config_file'};
54             my @sections = @{ $self->{'sections'} };
55             push @sections, 'nismgmt';
56             my $cfg = new IniConf(-file => $configfile);
57              
58             my %available_sections = map(($_=>1), $cfg->Sections);
59              
60             my %required = (map(($_ => 1), @{ $self->{'required_keys'} },
61             qw/yp_top name_db_files uid_db_files yp_push_cmd/
62             ) );
63              
64             @sections = grep $available_sections{$_}, @sections;
65              
66             foreach (map(($cfg->Parameters($_)), @sections)) {
67             delete $required{$_};
68             }
69              
70             foreach (keys %{ $self->{'default_keys'} }) {
71             delete $required{$_};
72             }
73            
74             if(scalar(keys(%required)) > 1) {
75             croak "Required parameters missing: " . join(", ", keys %required);
76             } elsif(scalar(keys(%required)) == 1) {
77             croak "Required parameter missing: " . (keys %required)[0];
78             } else {
79             while(@sections) {
80             my ($c) = shift @sections;
81             foreach my $k ( $cfg->Parameters($c) ) {
82             next if defined $self->{'options'}->{$k};
83             $self->{'options'}->{$k} = $cfg->val($c,$k)
84             }
85             }
86             foreach my $k (keys %{$self->{'default_keys'}}) {
87             next if defined $self->{'options'}->{$k};
88             $self->{'options'}->{$k} = $self->{'default_keys'}->{$k};
89             }
90             }
91             }
92              
93             sub get_options {
94             my $self = shift;
95              
96             return keys(%{$self->{'options'}});
97             }
98              
99             sub get_option {
100             my $self = shift;
101             my $opt = shift;
102              
103             return $self->{'options'}->{$opt};
104             }
105              
106             sub set_option {
107             my $self = shift;
108              
109             while(@_) {
110             my $k = shift;
111             my $v = shift;
112              
113             $self->{'options'}->{$k} = $v;
114             }
115             }
116              
117             sub prepare {
118             my $self = shift;
119              
120             my %file_types = (name_db_files => 'byname',
121             uid_db_files => 'byuid',
122             );
123              
124             my($ypstat, $ypdom) = Net::NIS::yp_get_default_domain();
125             if($ypstat) {
126             carp "Unable to determine yp_domain: $!";
127             return 0;
128             }
129              
130             $self->{'yp_domain'} = $ypdom;
131              
132             unless(defined $self->{'options'}->{'yp_top'}) {
133             carp "No path defined for the yp_top";
134             return 0;
135             }
136              
137             unless(chdir($self->{'options'}->{'yp_top'})) {
138             carp "Unable to chdir to " . $self->{'options'}->{'yp_top'}. ": $!";
139             return 0;
140             }
141              
142             $self->{'yp_domain_maps'} = join('/', $self->{'options'}->{'yp_top'},
143             $ypdom);
144            
145             unless(chdir($self->{'yp_domain_maps'})) {
146             carp "Unable to chdir to $$self{'yp_domain_maps'}: $!";
147             return 0;
148             }
149              
150             foreach my $t (keys %file_types) {
151             if($self->{'options'}->{$t}) {
152             $self->{$t} = [ split(/\s+/, $self->{'options'}->{$t}) ];
153             } else {
154             carp "No $t defined";
155             return 0;
156             }
157             }
158              
159             my $errors = 0;
160             foreach my $t (keys %file_types) {
161             foreach my $db (@{ $self->{$t} }) {
162             if(! -f "$$self{'yp_domain_maps'}/$db.dir") {
163             $errors++;
164             carp "Unable to locate one of the $t: $$self{'yp_domain_maps'}/$db.dir";
165             }
166             if(! -f "$$self{'yp_domain_maps'}/$db.pag") {
167             $errors++;
168             carp "Unable to locate one of the $t: $$self{'yp_domain_maps'}/$db.pag";
169             }
170             if($db =~ /adjunct/) {
171             $self->{'options'}->{'use_adjunct'} = 1;
172             }
173             }
174             }
175             if($errors) {
176             croak "Problems with one or more of the db files";
177             }
178              
179             if(! -x $self->{'options'}->{'yp_push_cmd'}) {
180             croak "Unable to find executable for yp_push_cmd: " .
181             $self->{'options'}->{'yp_push_cmd'};
182             }
183              
184             $self->{'need_push'} = 0;
185              
186             return 0 unless($self->locked);
187            
188             chdir($self->{'yp_domain_maps'});
189              
190             my @db_files;
191             foreach my $t (keys %file_types) {
192             foreach my $file (@{ $self->{$t} }) {
193             push @db_files, {};
194             $db_files[-1]->{'filename'} = $file;
195             $db_files[-1]->{'type'} = $file_types{$t};
196             if(! tie %{$db_files[-1]->{'handle'}}, 'NDBM_File',
197             $file, O_RDWR|O_CREAT, 0600)
198             {
199             croak "Unable to map ndbm file: $file: $!";
200             }
201             $db_files[-1]->{'open'} = 1;
202             $db_files[-1]->{'need_push'} = 0;
203             }
204             }
205              
206             $self->{'DB_FILES'} = \@db_files;
207              
208             return 1;
209             }
210              
211              
212             sub TIEHASH {
213             my $class = shift;
214             $class = ref($class) || $class;
215             my $opts;
216             my $configfile;
217             my @confighandlers;
218              
219             my $self = { };
220             bless $self, $class;
221              
222             if(@_) {
223             if(@_ == 1) {
224             if(ref($_[0])) {
225             $opts = $_[0];
226             } else {
227             $opts = { 'config_file' => shift,
228             'sections' => [ ],
229             };
230             }
231             } else {
232             my %args = (@_);
233             if(exists $args{'filename'} and exists $args{'program_tag'}) {
234             # looks like ConfigHandler...
235             $opts = { config_file => $args{'filename'},
236             sections => [$args{'program_tag'} ],
237             default_keys => $args{'default'},
238             required_keys => $args{'required'},
239             };
240             } else {
241             $opts = \%args;
242             }
243             }
244             }
245              
246             $self->{'config_file'} = $opts->{'config_file'} || '/etc/accounts.conf';
247             $self->{'sections'} = $opts->{'sections'} || [ ];
248             $self->{'default_keys'} = $opts->{'default_keys'} || { };
249             $self->{'required_keys'} = $opts->{'required_keys'} || [ ];
250              
251             if($opts->{'program_name'}) {
252             $self->{'program_name'} = $opts->{'program_name'};
253             } else {
254             ($self->{'program_name'} = $0) =~ s,.*/,,;
255             }
256              
257             foreach my $k (qw/config_file sections default_keys required_keys/,
258             qw/program_name/)
259             {
260             delete $opts->{$k};
261             }
262              
263             $self->{'options'} = { %{$opts} };
264              
265             $self->configure;
266              
267             $self->prepare;
268              
269             return $self;
270             }
271              
272             sub FETCH {
273             my $self = shift;
274             my $key = shift;
275             my $uname;
276             my $uid;
277             my %u;
278             my(%u_byuid, %u_byname);
279             my $key_set = $self->{options}->{key_set};
280              
281             if($key_set ne 'byuid' and $key_set ne 'byname') {
282             $key_set = '';
283             }
284              
285             if($key_set) {
286             return undef if defined $self->{'DEL'}->{$key_set}->{$key};
287             } else {
288             return undef if(defined $self->{'DEL'}->{'byname'}->{$key} ||
289             defined $self->{'DEL'}->{'byuid'}->{$key});
290             }
291              
292             if($key_set) {
293             return { %{ $self->{'MODS'}->{$key_set}->{$key} } }
294             if defined $self->{'MODS'}->{$key_set}->{$key};
295             } else {
296             return { %{ $self->{'MODS'}->{'byname'}->{$key} } }
297             if defined $self->{'MODS'}->{'byname'}->{$key};
298             return { %{ $self->{'MODS'}->{'byuid'}->{$key} } }
299             if defined $self->{'MODS'}->{'byuid'}->{$key};
300             }
301              
302             if($key_set) {
303             if(defined $self->{'CACHE'}->{$key_set}->{$key}) {
304             return { %{ $self->{'CACHE'}->{$key_set}->{$key} } };
305             }
306             }
307              
308             if(!$key_set && defined $self->{'CACHE'}->{'byname'}->{$key}) {
309             return { %{ $self->{'CACHE'}->{'byname'}->{$key} } };
310             }
311             if(!$key_set && defined $self->{'CACHE'}->{'byuid'}->{$key}) {
312             return { %{ $self->{'CACHE'}->{'byuid'}->{$key} } };
313             }
314              
315             if(!$key_set || $key_set ne 'byuid') {
316             # assume $key is a username first
317             $self->_fetch_records('byname', $key, \%u_byname);
318             }
319              
320             if(scalar keys %u_byname) {
321             $uname = $key;
322             $uid = $u_byname{'uid'};
323             } else {
324             return undef if($key_set eq 'byname');
325             $uid = $key;
326             }
327              
328             if(defined $uid) {
329             $self->_fetch_records('byuid', $uid, \%u_byuid);
330             }
331              
332             unless($uname) {
333             $uname = $u_byuid{'username'};
334             $self->_fetch_records('byname', $uname, \%u_byname);
335             }
336              
337             my($cache_byname, $cache_byuid) = ('CACHE','CACHE');
338             if($self->{'options'}->{'use_adjunct'}) {
339             $u_byuid{password} = $u_byname{password};
340             }
341             foreach my $k ((keys %u_byname), (keys %u_byuid)) {
342             next if exists $u{$k};
343             if($u_byname{$k} =~ /^\s*$/ && $u_byuid{$k}) {
344             carp "$k defined in byuid files but not in byname files";
345             if($cache_byname eq 'CACHE') {
346             carp "Byname files marked for update for $uname:$uid";
347             $cache_byname = 'MODS';
348             }
349             $u{$k} = $u_byuid{$k};
350             } elsif($u_byuid{$k} =~ /^\s*$/ && $u_byname{$k}) {
351             carp "$k defined in byname files but not in byuid files";
352             if($cache_byuid eq 'CACHE') {
353             carp "Byuid files marked for update for $uname:$uid";
354             $cache_byuid = 'MODS';
355             }
356             $u{$k} = $u_byname{$k};
357             } elsif($u_byname{$k} ne $u_byuid{$k}) {
358             croak "$k inconsistant - (byname,byuid) = ('$u_byname{$k}','$u_byuid{$k}')";
359             } else {
360             $u{$k} = $u_byname{$k};
361             }
362             }
363              
364             if(scalar keys %u) {
365             $u{'uid'} = $u{'uid'} + 0;
366             $u{'gid'} = $u{'gid'} + 0;
367             $self->{$cache_byname}->{'byname'}->{$u{username}} = { %u };
368             $self->{$cache_byuid }->{'byuid'}->{$u{uid}} = { %u };
369             return { %u };
370             } else {
371             return undef;
372             }
373             }
374              
375             sub _fetch_records {
376             my $self = shift;
377             my $type = shift;
378             my $key = shift;
379             my $u = shift;
380             my @dbfiles = grep { $_->{'type'} eq $type } @{$self->{'DB_FILES'}};
381             my %tu;
382             my %u;
383             my $cache = 'CACHE';
384              
385             foreach my $i (0..$#dbfiles)
386             {
387             my $db = $dbfiles[$i];
388             my $dbinfo = $db->{'handle'}->{$key};
389             my $dbname = $db->{'filename'};
390             if(defined $dbinfo) {
391             my @dbinfo = split(/:/, $dbinfo);
392             # do consistancy/sanity checks...
393             if($self->{'options'}->{'use_adjunct'}) {
394             if($db->{'filename'} !~ /adjunct/ and $dbinfo[1] ne "##$dbinfo[0]")
395             {
396             carp "Invalid data in passwd file ($dbname), passwd field != ##$dbinfo[0]";
397             }
398             }
399             $tu{'username'} = $dbinfo[0];
400             $tu{'uid' } = $dbinfo[2];
401             $tu{'gid' } = $dbinfo[3];
402             $tu{'gecos' } = $dbinfo[4];
403             $tu{'home' } = $dbinfo[5];
404             $tu{'shell' } = $dbinfo[6];
405              
406             delete $tu{'uid'} if($tu{'uid'} =~ /^\s*$/);
407             delete $tu{'gid'} if($tu{'gid'} =~ /^\s*$/);
408              
409             $tu{'uid'} .= 'E0' if(exists $tu{'uid'} && !$tu{'uid'});
410             $tu{'gid'} .= 'E0' if(exists $tu{'gid'} && !$tu{'gid'});
411              
412             if($self->{'options'}->{'use_adjunct'}) {
413             if($db->{'filename'} =~ /adjunct/) {
414             $tu{'password'} = $dbinfo[1];
415             $u{password} = $tu{password} if $u{password} =~ /^(##\Q$u{username}\E|\s*)$/;
416             $tu{password} = $u{password} if $u{password} !~ /^(##\Q$u{username}\E|\s*)$/;
417             }
418             } else {
419             $tu{'password'} = $dbinfo[1];
420             }
421             if($i) {
422             foreach my $k ((keys %tu), (keys %u)) {
423             next if exists $u{$k};
424             if($tu{$k} =~ /^\s*$/ && $u{$k}) {
425             carp "$k defined in $type files but not in $dbname";
426             if($cache eq 'CACHE') {
427             carp "$type files marked for update for $key";
428             $cache = 'MODS';
429             }
430             #$u{$k} = $tu{$k};
431             } elsif($u{$k} =~ /^\s*$/ && $tu{$k}) {
432             carp "$k defined in $dbname but not in $type files";
433             if($cache eq 'CACHE') {
434             carp "\U$type\E files marked for update for $key";
435             $cache = 'MODS';
436             }
437             $u{$k} = $tu{$k};
438             } elsif($tu{$k} ne $u{$k}) {
439             croak "$k inconsistant - ($type,$dbname) = ('$u{$k}','$tu{$k}')";
440             } else {
441             $u{$k} = $tu{$k};
442             }
443             }
444             } else {
445             %u = %tu;
446             }
447             }
448              
449             foreach my $k (keys %u) {
450             delete $u{$k} if $u{$k} =~ /^\s*$/;
451             }
452             }
453             %{$u} = %u;
454             if($cache ne 'CACHE') {
455             $self->{'MODS'}->{$cache}->{'MODS'}->{$key} = { %u };
456             }
457             }
458              
459             sub STORE {
460             my $self = shift;
461             my $key = shift;
462             my $value;
463             my $key_set = $self->{options}->{key_set} || '';
464              
465             if($key_set ne 'byuid' and $key_set ne 'byname') {
466             $key_set = '';
467             }
468              
469             croak "@{[&whowasi]}: $key not clobberable" unless $self->{'options'}->{'CLOBBER'};
470              
471             if(ref($_[0]) eq 'HASH') {
472             $value = $_[0];
473             } else {
474             $value = { @_ };
475             }
476              
477             if($self->EXISTS($key)) {
478             # we are modifying
479             my $oldvalue = $self->FETCH($key);
480              
481             my $changed = '';
482             foreach my $k (keys %{$oldvalue}) {
483             $changed .= $k if $$oldvalue{$k} ne $$value{$k};
484             }
485             return if $changed =~ /^\s*$/;
486             $$value{password_only} = 1 if $changed eq 'password';
487              
488             delete $self->{DEL}->{byuid} ->{$$value{uid}};
489             delete $self->{DEL}->{byname}->{$$value{username}};
490             }
491              
492             $self->{MODS}->{byuid} ->{$$value{uid}} = $value;
493             $self->{MODS}->{byname}->{$$value{username}} = $value;
494            
495             delete $self->{CACHE}->{byname}->{$$value{username}};
496             delete $self->{CACHE}->{byuid} ->{$$value{uid}};
497             }
498            
499             sub EXISTS {
500             my $self = shift;
501             my $key = shift;
502             my $key_set = $self->{options}->{key_set} || '';
503              
504             if($key_set ne 'byuid' and $key_set ne 'byname') {
505             $key_set = '';
506             }
507              
508             if($key_set) {
509             return 0 if $self->{'DEL'}->{$key_set}->{$key};
510             return 1 if exists $self->{'CACHE'}->{$key_set}->{$key};
511             return 1 if exists $self->{'MODS'}->{$key_set}->{$key};
512              
513             foreach my $db (grep {$_->{type} eq $key_set} @{$self->{'DB_FILES'}})
514             {
515             my $dbinfo = $db->{'handle'}->{$key};
516             if(defined $dbinfo) {
517             $self->{'CACHE'}->{$key} = undef;
518             return 1;
519             }
520             }
521             } else {
522             return 0 if($self->{'DEL'}->{'byuid'}->{$key} || $self->{'DEL'}->{'byname'}->{$key});
523             return 1 if exists $self->{CACHE}->{byuid} ->{$key};
524             return 1 if exists $self->{CACHE}->{byname}->{$key};
525             return 1 if exists $self->{MODS} ->{byuid} ->{$key};
526             return 1 if exists $self->{MODS} ->{byname}->{$key};
527              
528             foreach my $db (@{$self->{'DB_FILES'}})
529             {
530             my $dbinfo = $db->{'handle'}->{$key};
531             if(defined $dbinfo) {
532             $self->{'CACHE'}->{$key} = undef;
533             return 1;
534             }
535             }
536             }
537              
538             return 0;
539             }
540              
541             sub CLEAR {
542             # we really don't want to remove the DB files...
543             carp "@{[&whowasi]}: Database not clobberable";
544             }
545              
546             sub FIRSTKEY {
547             my $self = shift;
548             my @keys;
549              
550             $self->{'KEYS'} = { };
551              
552             if($self->{options}->{key_set} eq 'byuid' ||
553             $self->{options}->{key_set} eq 'byname') {
554             @keys = grep {$_->{type} eq $self->{options}->{key_set}} @{$self->{'DB_FILES'}};
555             } else {
556             @keys = @{$self->{'DB_FILES'}};
557             }
558              
559             foreach my $db (@keys) {
560             foreach my $k (keys %{$db->{'handle'}}) {
561             $self->{'KEYS'}->{$k}++;
562             }
563             }
564              
565             if($self->{options}->{key_set} eq 'byuid') {
566             @keys = grep {$_ = $self->{'DEL'}->{$_}->{uid}} keys %{$self->{'DEL'}};
567             } elsif($self->{options}->{key_set} eq 'byname') {
568             @keys = grep {$_ eq $self->{'DEL'}->{$_}->{username}} keys %{$self->{'DEL'}};
569             } else {
570             @keys = keys %{$self->{'DEL'}};
571             }
572              
573             foreach my $k (@keys) {
574             delete $self->{'KEYS'}->{$k};
575             }
576              
577             if($self->{options}->{key_set} eq 'byuid') {
578             @keys = grep {$_ = $self->{'MODS'}->{$_}->{uid}} keys %{$self->{'MODS'}};
579             } elsif($self->{options}->{key_set} eq 'byname') {
580             @keys = grep {$_ eq $self->{'MODS'}->{$_}->{username}} keys %{$self->{'MODS'}};
581             } else {
582             @keys = keys %{$self->{'MODS'}};
583             }
584              
585             foreach my $k (@keys) {
586             $self->{'KEYS'}->{$k}++;
587             }
588              
589             delete $self->{KEYS}->{YP_LAST_MODIFIED};
590              
591             $self->{'KEYS'} = [ keys %{$self->{'KEYS'}} ];
592              
593             return shift @{$self->{'KEYS'}};
594             }
595              
596             sub NEXTKEY {
597             my $self = shift;
598              
599             if($self && $self->{'KEYS'} && ref($self->{'KEYS'}) eq 'ARRAY') {
600             return shift @{$self->{'KEYS'}};
601             } else {
602             return undef;
603             }
604             }
605              
606             sub DELETE {
607             my $self = shift;
608             my $key = shift;
609             my $key_set = $self->{options}->{key_set} || '';
610              
611             if($key_set ne 'byuid' and $key_set ne 'byname') {
612             $key_set = '';
613             }
614              
615             croak "@{[&whowasi]}: $key not clobberable" unless $self->{'options'}->{'CLOBBER'} > 1;
616              
617             if($key_set) {
618             delete $self->{CACHE}->{$key_set}->{$key};
619             delete $self->{MODS} ->{$key_set}->{$key};
620             $self->{DEL}->{$key_set}->{$key}++;
621             } else {
622             delete $self->{CACHE}->{byname}->{$key};
623             delete $self->{MODS} ->{byname}->{$key};
624             $self->{DEL}->{byname}->{$key}++;
625             if($key =~ /^\d+$/) {
626             delete $self->{CACHE}->{byuid}->{$key};
627             delete $self->{MODS} ->{byuid}->{$key};
628             $self->{DEL}->{byuid}->{$key}++;
629             }
630             }
631             }
632            
633             sub flush {
634             my $self = shift;
635             my $needs_push = 0;
636             my $key;
637              
638             return unless $self->{'options'}->{'FLUSH'};
639              
640             foreach my $key_set (qw/byuid byname/) {
641             foreach my $d (keys %{$self->{'DEL'}->{$key_set}}) {
642             foreach my $db (@{$self->{'DB_FILES'}}) {
643             if($db->{'open'}) {
644             if($db->{'type'} eq 'byuid' && $d =~ /^\d+$/) {
645             delete($db->{'handle'}->{$d});
646             $db->{'need_push'} = 1;
647             } elsif($db->{'type'} eq 'byname' && $d !~ /^\d+$/) {
648             delete($db->{'handle'}->{$d});
649             $db->{'need_push'} = 1;
650             }
651             }
652             }
653             }
654             }
655              
656             my $keys = {};
657             my($uadj, $u);
658             foreach my $key_set (qw/byuid byname/) {
659             foreach my $k (keys %{$self->{'MODS'}->{$key_set}}) {
660             my $a = $self->{'MODS'}->{$key_set}->{$k};
661             next unless $k eq $$a{username} || $k == $$a{uid};
662             next if $keys->{byname}->{$$a{username}} || $keys->{byuid}->{$$a{uid}};
663             $keys->{byname}->{$$a{username}}++;
664             $keys->{byuid}->{$$a{uid}}++;
665              
666             $uadj = "$$a{username}:$$a{password}:$$a{uid}:$$a{gid}:$$a{gecos}:$$a{home}:$$a{shell}";
667             if($self->{'options'}->{'use_adjunct'}) {
668             $u = "$$a{username}:##$$a{username}:$$a{uid}:$$a{gid}:$$a{gecos}:$$a{home}:$$a{shell}";
669             } else {
670             $u = $uadj;
671             }
672            
673             foreach my $db (@{$self->{'DB_FILES'}}) {
674             my $newkey;
675             my $newvalue;
676             if($db->{open}) {
677             if($db->{type} eq 'byuid') {
678             $newkey = $a->{uid};
679             } elsif($db->{type} eq 'byname') {
680             $newkey = $a->{username};
681             } else {
682             carp "Unknown filetype, please check config file ($$db{filename})";
683             next;
684             }
685             if($self->{options}->{use_adjunct} &&
686             $db->{filename} !~ /adjunct/)
687             {
688             if($$a{password_only}) {
689             $newvalue = undef;
690             } else {
691             $newvalue = $u;
692             }
693             } else {
694             $newvalue = $uadj;
695             }
696            
697             if(defined $newvalue) {
698             $db->{'handle'}->{$newkey} = $newvalue;
699             $db->{'need_push'} = 1;
700             }
701             }
702             }
703             }
704             }
705             # clear caches...
706             $self->{'DEL'} = {};
707             $self->{'MODS'} = {};
708             }
709              
710             sub push_db {
711             my $self = shift;
712             my %opts = (@_);
713            
714             foreach my $db (@{$self->{'DB_FILES'}}) {
715             next unless $db->{'need_push'};
716             if($db->{'open'} && $db->{'handle'}) {
717             $db->{handle}->{YP_LAST_MODIFIED} = sprintf("%010d",time());
718             untie(%{$db->{'handle'}});
719             }
720             next unless $self->{'options'}->{'PUSH'};
721              
722             my($pid, @out, @err) = 0;
723             $pid = open3( \*Wpush, \*Rpush, \*Epush,
724             $self->{'options'}->{'yp_push_cmd'}, '-d',
725             $self->{'yp_domain'}, $db->{'filename'});
726             close(Wpush);
727             @out=; @err=;
728             close(Rpush); close(Epush);
729            
730             if(!$opts{'no_reopen'} && $db->{'open'}) {
731             if(! tie %{$db->{'handle'}}, 'NDBM_File', $db->{'filename'},
732             O_RDWR|O_CREAT, 0600)
733             {
734             $db->{'open'} = 0;
735             carp "Unable to remap ndbm file: $$db{'filename'}: $!";
736             }
737             }
738            
739             foreach (grep !/^\s*$/, @out) {
740             $self->error(msg => $_);
741             }
742             $db->{'need_push'} = 0;
743             }
744             }
745              
746             sub finish {
747             my $self = shift;
748              
749             foreach my $db (@{$self->{'DB_FILES'}}) {
750             if($db->{'open'} && $db->{'handle'}) {
751             untie %{$db->{'handle'}};
752             $db->{'open'} = 0;
753             }
754             }
755              
756             $self->unlocked;
757             }
758              
759             sub DESTROY {
760             my $self = shift;
761              
762             if($self->{'options'}->{'FLUSH'}) {
763             $self->flush;
764             }
765              
766             # push_db handles the case of PUSH => 0
767             $self->push_db(no_reopen => 1);
768              
769             $self->finish;
770             }
771              
772             sub locked {
773             my $self = shift;
774              
775             return 1 if $self->{'locked'};
776              
777             if(lock($self->{'options'}->{'yp_lock_file'})) {
778             $self->{'locked'} = 1;
779             } else {
780             $self->{'locked'} = 0;
781             carp "Unable to lock yp files: $!";
782             }
783             return $self->{'locked'};
784             }
785              
786             sub unlocked {
787             my $self = shift;
788              
789             return 1 unless $self->{'locked'};
790              
791             if(unlock($self->{'options'}->{'yp_lock_file'})) {
792             $self->{'locked'} = 0;
793             } else {
794             $self->{'locked'} = 1;
795             carp "Unable to unlock yp files: $!";
796             }
797             return $self->{'locked'};
798             }
799            
800             1;
801             __END__