| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # -*- perl -*- | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # Net::NIS::Tied - interface to YP^H^HNIS | 
| 4 |  |  |  |  |  |  | # | 
| 5 |  |  |  |  |  |  | # $Id: 104 $ | 
| 6 |  |  |  |  |  |  | # | 
| 7 |  |  |  |  |  |  | package Net::NIS; | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 6 |  |  | 6 |  | 201096 | use strict; | 
|  | 6 |  |  |  |  | 18 |  | 
|  | 6 |  |  |  |  | 505 |  | 
| 10 | 6 |  |  | 6 |  | 172 | use 5.006; | 
|  | 6 |  |  |  |  | 34 |  | 
|  | 6 |  |  |  |  | 224 |  | 
| 11 | 6 |  |  | 6 |  | 43 | use warnings;			# Sigh, only available in 5.6 and above | 
|  | 6 |  |  |  |  | 16 |  | 
|  | 6 |  |  |  |  | 194 |  | 
| 12 | 6 |  |  | 6 |  | 92 | use Carp; | 
|  | 6 |  |  |  |  | 12 |  | 
|  | 6 |  |  |  |  | 1000 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | ############################################################################### | 
| 15 |  |  |  |  |  |  | # BEGIN user-configurable section | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | # Linux and Solaris seem to have this file.  It contains a number of | 
| 18 |  |  |  |  |  |  | # lines, each with a key/value pair (separated by spaces). | 
| 19 |  |  |  |  |  |  | my $Nicknames_File = '/var/yp/nicknames'; | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | # For those systems who don't have a nicknames file, here are some | 
| 22 |  |  |  |  |  |  | # reasonable defaults. | 
| 23 |  |  |  |  |  |  | my %Nicknames_Default = | 
| 24 |  |  |  |  |  |  | ( | 
| 25 |  |  |  |  |  |  | passwd    => 'passwd.byname', | 
| 26 |  |  |  |  |  |  | group     => 'group.byname', | 
| 27 |  |  |  |  |  |  | networks  => 'networks.byaddr', | 
| 28 |  |  |  |  |  |  | hosts     => 'hosts.byname', | 
| 29 |  |  |  |  |  |  | protocols => 'protocols.bynumber', | 
| 30 |  |  |  |  |  |  | services  => 'services.byname', | 
| 31 |  |  |  |  |  |  | aliases   => 'mail.aliases', | 
| 32 |  |  |  |  |  |  | ethers    => 'ethers.byname', | 
| 33 |  |  |  |  |  |  | ); | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | # Special case: this magic map acts as a front end to yp_master() | 
| 37 |  |  |  |  |  |  | our $Magic_ypmaster_map = '__YPMASTER'; | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | # Ouch.  It really hurts to enumerate these here, manually, instead of | 
| 40 |  |  |  |  |  |  | # somehow relying on the autogenerated list made by h2xs.  But at least | 
| 41 |  |  |  |  |  |  | # we have a test (t/15yperr_num.t) that should catch inconsistencies. | 
| 42 |  |  |  |  |  |  | # | 
| 43 |  |  |  |  |  |  | # Please be sure to keep these in numerical order, starting with 0.  If | 
| 44 |  |  |  |  |  |  | # There are ever gaps in the YPERR_xxx sequence, or duplicates, we will | 
| 45 |  |  |  |  |  |  | # have to rethink this approach.  But until then, let's not worry. | 
| 46 | 6 |  |  | 6 |  | 42 | use vars qw(@YPERRS); | 
|  | 6 |  |  |  |  | 12 |  | 
|  | 6 |  |  |  |  | 1027 |  | 
| 47 |  |  |  |  |  |  | @YPERRS = map { "YPERR_$_" } | 
| 48 |  |  |  |  |  |  | qw( | 
| 49 |  |  |  |  |  |  | SUCCESS | 
| 50 |  |  |  |  |  |  | BADARGS | 
| 51 |  |  |  |  |  |  | RPC | 
| 52 |  |  |  |  |  |  | DOMAIN | 
| 53 |  |  |  |  |  |  | MAP | 
| 54 |  |  |  |  |  |  | KEY | 
| 55 |  |  |  |  |  |  | YPERR | 
| 56 |  |  |  |  |  |  | RESRC | 
| 57 |  |  |  |  |  |  | NOMORE | 
| 58 |  |  |  |  |  |  | PMAP | 
| 59 |  |  |  |  |  |  | YPBIND | 
| 60 |  |  |  |  |  |  | YPSERV | 
| 61 |  |  |  |  |  |  | NODOM | 
| 62 |  |  |  |  |  |  | BADDB | 
| 63 |  |  |  |  |  |  | VERS | 
| 64 |  |  |  |  |  |  | ACCESS | 
| 65 |  |  |  |  |  |  | BUSY | 
| 66 |  |  |  |  |  |  | ); | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | # Magic!  This variable is magically tied to a global in our .xs which | 
| 69 |  |  |  |  |  |  | # keeps track of the status returned from the last yp_xxx() function. | 
| 70 |  |  |  |  |  |  | # | 
| 71 |  |  |  |  |  |  | # This variable is exported by default.  I'm not too happy with its | 
| 72 |  |  |  |  |  |  | # name, but it seems like the best out of all the possibilities I | 
| 73 |  |  |  |  |  |  | # considered.  The primary benefit is that, given the fixed nature | 
| 74 |  |  |  |  |  |  | # of the YPERR_xxx constant names, '$yperr' will be easier for someone | 
| 75 |  |  |  |  |  |  | # to remember than $yp_status, $ypstatus, $yp_err, or anything like that. | 
| 76 |  |  |  |  |  |  | # | 
| 77 |  |  |  |  |  |  | # Any other suggestions, before it's too late to change it? | 
| 78 | 6 |  |  | 6 |  | 77 | use vars qw($yperr); | 
|  | 6 |  |  |  |  | 18 |  | 
|  | 6 |  |  |  |  | 301 |  | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | # END   user-configurable section | 
| 81 |  |  |  |  |  |  | ############################################################################### | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 6 |  |  | 6 |  | 29 | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $PKG); | 
|  | 6 |  |  |  |  | 11 |  | 
|  | 6 |  |  |  |  | 8665 |  | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | require Exporter; | 
| 86 |  |  |  |  |  |  | require DynaLoader; | 
| 87 |  |  |  |  |  |  | require AutoLoader; | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | @ISA = qw(Exporter DynaLoader); | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | %EXPORT_TAGS = ( all => [ '$yperr', @YPERRS ] ); | 
| 92 |  |  |  |  |  |  | @EXPORT_OK   = (          '$yperr', @YPERRS   ); | 
| 93 |  |  |  |  |  |  | @EXPORT      = (          '$yperr'            ); | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | $VERSION = '0.44'; | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | $PKG = __PACKAGE__;		# For interpolating into error messages | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | ############# | 
| 100 |  |  |  |  |  |  | #  DESTROY  #  Not really used, but needed so AUTOLOAD doesn't trap it | 
| 101 |  |  |  |  |  |  | ############# | 
| 102 | 0 |  |  | 0 |  | 0 | sub DESTROY {} | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | ############## | 
| 105 |  |  |  |  |  |  | #  AUTOLOAD  #  from h2xs | 
| 106 |  |  |  |  |  |  | ############## | 
| 107 |  |  |  |  |  |  | sub AUTOLOAD { | 
| 108 |  |  |  |  |  |  | # This AUTOLOAD is used to 'autoload' constants from the constant() | 
| 109 |  |  |  |  |  |  | # XS function.  If a constant is not found then control is passed | 
| 110 |  |  |  |  |  |  | # to the AUTOLOAD in AutoLoader. | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 19 |  |  | 19 |  | 7915 | my $constname; | 
| 113 | 6 |  |  | 6 |  | 48 | use vars qw($AUTOLOAD); | 
|  | 6 |  |  |  |  | 17 |  | 
|  | 6 |  |  |  |  | 2274 |  | 
| 114 | 19 |  |  |  |  | 96 | ($constname = $AUTOLOAD) =~ s/.*:://; | 
| 115 | 19 | 50 |  |  |  | 54 | croak "& not defined" if $constname eq 'constant'; | 
| 116 | 19 | 50 |  |  |  | 102 | my $val = constant($constname, @_ ? $_[0] : 0); | 
| 117 | 19 | 50 |  |  |  | 133 | if ($! != 0) { | 
| 118 | 0 | 0 |  |  |  | 0 | if ($! =~ /Invalid/) { | 
| 119 | 0 | 0 |  |  |  | 0 | if ($constname =~ /^YP/) { | 
| 120 | 0 |  |  |  |  | 0 | croak "No such constant, ${PKG}::$constname"; | 
| 121 |  |  |  |  |  |  | } else { | 
| 122 | 0 |  |  |  |  | 0 | croak "No such function, ${PKG}::$constname()"; | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  | } | 
| 125 |  |  |  |  |  |  | else { | 
| 126 | 0 |  |  |  |  | 0 | croak "Your vendor has not defined Net::NIS macro $constname"; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | { | 
| 131 | 6 |  |  | 6 |  | 40 | no strict 'refs'; | 
|  | 6 |  |  |  |  | 11 |  | 
|  | 6 |  |  |  |  | 1898 |  | 
|  | 19 |  |  |  |  | 20 |  | 
| 132 | 19 |  |  | 19 |  | 125 | *$AUTOLOAD = sub { $val }; | 
|  | 19 |  |  |  |  | 3834 |  | 
| 133 |  |  |  |  |  |  | } | 
| 134 | 19 |  |  |  |  | 82 | goto &$AUTOLOAD; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | bootstrap Net::NIS $VERSION; | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | # Magic: The $yperr variable will now have the YP status, int & string form | 
| 140 |  |  |  |  |  |  | _yp_tie_status ($yperr); | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | ###################### | 
| 144 |  |  |  |  |  |  | #  _expand_nickname  #  Look for a string in the /var/yp/nicknames file | 
| 145 |  |  |  |  |  |  | ###################### | 
| 146 |  |  |  |  |  |  | sub _expand_nickname($) { | 
| 147 | 0 |  |  | 0 |  | 0 | my $map = shift; | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 6 |  |  | 6 |  | 85 | use vars '%nickname'; | 
|  | 6 |  |  |  |  | 15 |  | 
|  | 6 |  |  |  |  | 8604 |  | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | # First time through?  Read the nicknames file, or initialize to a | 
| 152 |  |  |  |  |  |  | # reasonable default (hardcoded above). | 
| 153 | 0 | 0 |  |  |  | 0 | if (keys %nickname == 0) { | 
| 154 | 0 | 0 |  |  |  | 0 | if (open NICKNAMES, '<', $Nicknames_File) { | 
| 155 | 0 |  |  |  |  | 0 | while (defined (my $line = )) { | 
| 156 | 0 | 0 |  |  |  | 0 | $line =~ /^\s*(\S+)\s+(\S+)$/ | 
| 157 |  |  |  |  |  |  | or next; | 
| 158 | 0 |  |  |  |  | 0 | $nickname{$1} = $2; | 
| 159 |  |  |  |  |  |  | } | 
| 160 | 0 |  |  |  |  | 0 | close NICKNAMES; | 
| 161 |  |  |  |  |  |  | } else { | 
| 162 |  |  |  |  |  |  | # No nicknames file | 
| 163 | 0 |  |  |  |  | 0 | %nickname = %Nicknames_Default; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | # If there's a nickname defined for this map, return it... otherwise, | 
| 168 |  |  |  |  |  |  | # the map name itself. | 
| 169 | 0 | 0 |  |  |  | 0 | $nickname{$map} || $map; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | ############# | 
| 174 |  |  |  |  |  |  | #  TIEHASH  #  establish the relationship between a hash and a YP map. | 
| 175 |  |  |  |  |  |  | ############# | 
| 176 |  |  |  |  |  |  | sub TIEHASH { | 
| 177 | 1 |  |  | 1 |  | 504 | my $class = shift; | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | # Second argument must be a map name (passwd, mail.aliases, etc) | 
| 180 | 1 | 50 |  |  |  | 4 | my $map = shift | 
| 181 |  |  |  |  |  |  | or croak "Usage: tie \%hash, $PKG, 'MAP NAME' [, 'DOMAIN' ]\n"; | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | # Third argument (optional) is the NIS domain.  If unset, bail out | 
| 184 |  |  |  |  |  |  | # now, setting error to NODOM ("Local domain name not set").  Otherwise, | 
| 185 |  |  |  |  |  |  | # if we try the yp_match, it fails with the less-than-helpful BADARGS. | 
| 186 |  |  |  |  |  |  | my $domain = shift || yp_get_default_domain() | 
| 187 | 1 | 50 | 33 |  |  | 31 | or do { | 
| 188 | 1 |  |  |  |  | 6 | $yperr = YPERR_NODOM(); | 
| 189 | 1 |  |  |  |  | 3 | return undef; | 
| 190 |  |  |  |  |  |  | }; | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | # Check validity of map name. | 
| 193 |  |  |  |  |  |  | # As a special case, use '__YPMASTER' to act as a front end to yp_master() | 
| 194 | 0 |  |  |  |  |  | $map = _expand_nickname($map); | 
| 195 | 0 | 0 |  |  |  |  | unless ($map eq $Magic_ypmaster_map) { | 
| 196 | 0 | 0 |  |  |  |  | if (! Net::NIS::yp_master( $domain, $map )) { | 
| 197 | 0 |  |  |  |  |  | $yperr = YPERR_MAP(); | 
| 198 | 0 |  |  |  |  |  | return undef; | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | # All OK.  Force $yperr to OK, and return a blessed object | 
| 203 | 0 |  |  |  |  |  | $yperr = YPERR_SUCCESS(); | 
| 204 | 0 |  |  |  |  |  | bless { map => $map, domain => $domain }, $class; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | ########### | 
| 209 |  |  |  |  |  |  | #  FETCH  #  read-only access to a key. | 
| 210 |  |  |  |  |  |  | ########### | 
| 211 |  |  |  |  |  |  | sub FETCH { | 
| 212 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 213 | 0 |  |  |  |  |  | my $key  = shift; | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | # Special case for magic yp_master map | 
| 216 | 0 | 0 |  |  |  |  | if ($self->{map} eq $Magic_ypmaster_map) { | 
| 217 | 0 |  |  |  |  |  | return Net::NIS::yp_master($self->{domain}, $key); | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | # Have we slurped in all keys using yp_all() ?  Look up our key therein. | 
| 221 | 0 | 0 | 0 |  |  |  | if (exists $self->{_alldata} && exists $self->{_alldata}->{$key}) { | 
| 222 | 0 |  |  |  |  |  | return $self->{_alldata}->{$key}; | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | # Haven't called yp_all(), or key not found there.  Do a real YP lookup. | 
| 226 | 0 | 0 |  |  |  |  | if (defined (my $val = yp_match($self->{domain}, $self->{map}, $key))) { | 
| 227 | 0 |  |  |  |  |  | return $val; | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | # Error... is it 'no such key in map'?  That's OK | 
| 231 | 0 | 0 |  |  |  |  | if ($yperr == YPERR_KEY()) { | 
| 232 | 0 |  |  |  |  |  | return undef; | 
| 233 |  |  |  |  |  |  | } | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | # Any other error: fatal | 
| 236 | 0 |  |  |  |  |  | croak sprintf("Unable to find '%s' in %s.  Reason: %s", | 
| 237 |  |  |  |  |  |  | $key, $self->{map}, $yperr); | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | ############ | 
| 242 |  |  |  |  |  |  | #  EXISTS  #  Does a key exist?  This isn't cheap, it still incurs a yp_match | 
| 243 |  |  |  |  |  |  | ############ | 
| 244 |  |  |  |  |  |  | sub EXISTS { | 
| 245 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 246 |  |  |  |  |  |  |  | 
| 247 | 0 |  |  |  |  |  | defined $self->FETCH (@_); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | ############## | 
| 252 |  |  |  |  |  |  | #  FIRSTKEY  #  For iterating with each() or keys() | 
| 253 |  |  |  |  |  |  | ############## | 
| 254 |  |  |  |  |  |  | # | 
| 255 |  |  |  |  |  |  | # Important note: this uses the yp_all() mechanism to slurp in a complete | 
| 256 |  |  |  |  |  |  | # hash containing all the key/value pairs.  It is delayed until here, | 
| 257 |  |  |  |  |  |  | # because our caller could simply want to perform lookups (via FETCH) | 
| 258 |  |  |  |  |  |  | # without iterating over all keys. | 
| 259 |  |  |  |  |  |  | # | 
| 260 |  |  |  |  |  |  | sub FIRSTKEY { | 
| 261 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | # Special case when called with magic yp_master key | 
| 264 | 0 | 0 |  |  |  |  | if ($self->{map} eq $Magic_ypmaster_map) { | 
| 265 | 0 |  |  |  |  |  | my %master; | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 0 |  |  |  |  |  | for my $map (Net::NIS::yp_maplist( $self->{domain} )) { | 
| 268 | 0 |  |  |  |  |  | $master{$map} = Net::NIS::yp_master( $self->{domain},$map ); | 
| 269 |  |  |  |  |  |  | } | 
| 270 | 0 |  |  |  |  |  | $self->{_alldata} = \%master; | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  | else { | 
| 273 |  |  |  |  |  |  | # Each time we get called, slurp across again... just in case any | 
| 274 |  |  |  |  |  |  | # values have changed.  This is suboptimal: in effect, we're keeping | 
| 275 |  |  |  |  |  |  | # a cache around for who-knows-how-long.  Suggestions welcome for | 
| 276 |  |  |  |  |  |  | # improving it (perhaps keeping a {_last_updated} time??) | 
| 277 | 0 |  |  |  |  |  | $self->{_alldata} = yp_all ($self->{domain}, $self->{map}); | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | # Returned value must be a hash.  If it isn't, something bad happened. | 
| 280 | 0 | 0 |  |  |  |  | if (ref $self->{_alldata} ne 'HASH') { | 
| 281 | 0 |  |  |  |  |  | croak sprintf("No such map '%s'.  Reason: %s", | 
| 282 |  |  |  |  |  |  | $self->{map}, $yperr); | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | # Reset the each() operator, and let it do the rest. | 
| 287 | 0 |  |  |  |  |  | my $trashme = keys %{ $self->{_alldata} }; | 
|  | 0 |  |  |  |  |  |  | 
| 288 | 0 |  |  |  |  |  | return scalar each %{ $self->{_alldata} }; | 
|  | 0 |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | ############# | 
| 292 |  |  |  |  |  |  | #  NEXTKEY  #  no-brainer, just lets each() do the work on our internal hash | 
| 293 |  |  |  |  |  |  | ############# | 
| 294 |  |  |  |  |  |  | sub NEXTKEY { | 
| 295 | 0 |  |  | 0 |  |  | my $self    = shift; | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 0 |  |  |  |  |  | return each %{ $self->{_alldata} }; | 
|  | 0 |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | # ------NO WRITE ACCESS ALLOWED------ | 
| 302 |  |  |  |  |  |  | sub _read_only(@) { | 
| 303 | 0 |  |  | 0 |  |  | croak "$PKG provides read-only access"; | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 0 |  |  | 0 |  |  | sub STORE  { _read_only(@_); } | 
| 307 | 0 |  |  | 0 |  |  | sub DELETE { _read_only(@_); } | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | ############################################################################### | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | 1; | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | __END__ |