| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Sman::Config; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | #$Id$ | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 3 |  |  | 3 |  | 1792 | use 5.006; | 
|  | 3 |  |  |  |  | 6 |  | 
| 6 | 3 |  |  | 3 |  | 8 | use strict; | 
|  | 3 |  |  |  |  | 3 |  | 
|  | 3 |  |  |  |  | 41 |  | 
| 7 | 3 |  |  | 3 |  | 286 | use warnings; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 69 |  | 
| 8 | 3 |  |  | 3 |  | 1167 | use FindBin qw($Bin); | 
|  | 3 |  |  |  |  | 2135 |  | 
|  | 3 |  |  |  |  | 263 |  | 
| 9 | 3 |  |  | 3 |  | 1190 | use POSIX qw(sysconf _PC_CHOWN_RESTRICTED);     # for _isverysafe | 
|  | 3 |  |  |  |  | 12494 |  | 
|  | 3 |  |  |  |  | 11 |  | 
| 10 | 3 |  |  | 3 |  | 2353 | use Cwd;                                        # for _isverysafe | 
|  | 3 |  |  |  |  | 4 |  | 
|  | 3 |  |  |  |  | 125 |  | 
| 11 | 3 |  |  | 3 |  | 1242 | use File::stat; # used in _issafe() | 
|  | 3 |  |  |  |  | 13037 |  | 
|  | 3 |  |  |  |  | 9 |  | 
| 12 | 3 |  |  | 3 |  | 998 | use fields qw( conf ); | 
|  | 3 |  |  |  |  | 1918 |  | 
|  | 3 |  |  |  |  | 11 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | # call like my $smanconfig = new Sman::Config(); | 
| 15 |  |  |  |  |  |  | sub new { | 
| 16 | 1 |  |  | 1 | 0 | 368 | my $proto = shift; | 
| 17 | 1 |  | 33 |  |  | 6 | my $class = ref($proto) || $proto; | 
| 18 | 1 |  |  |  |  | 1 | my $self  = {}; | 
| 19 | 1 |  |  |  |  | 2 | bless ($self, $class); | 
| 20 | 1 |  |  |  |  | 3 | $self->{conf} = []; # empty list | 
| 21 | 1 |  |  |  |  | 2 | my $configfile = shift; | 
| 22 | 1 | 50 |  |  |  | 2 | if (defined($configfile)) { | 
| 23 | 0 |  |  |  |  | 0 | $self->ReadSingleConfigFile($configfile); | 
| 24 |  |  |  |  |  |  | } | 
| 25 | 1 |  |  |  |  | 2 | return $self; | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | # Gets a config var. Because we're case INsensitive. | 
| 29 |  |  |  |  |  |  | # returns "" if no data found. | 
| 30 |  |  |  |  |  |  | sub GetConfigData { | 
| 31 | 0 |  |  | 0 | 0 |  | my ($self, $directive) = @_; | 
| 32 |  |  |  |  |  |  | #print "Looking for $directive...\n"; | 
| 33 | 0 |  |  |  |  |  | for(@ {$self->{conf}} ) { | 
|  | 0 |  |  |  |  |  |  | 
| 34 | 0 | 0 | 0 |  |  |  | return $_->[1] if (uc($_->[0]) eq uc($directive) && defined($_->[1])); | 
| 35 |  |  |  |  |  |  | } | 
| 36 | 0 |  |  |  |  |  | return ""; | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | # Sets a config var. Because we're case INsensitive. | 
| 40 |  |  |  |  |  |  | # if an existing value is set for a name, it's replaced, WHERE IT WAS. | 
| 41 |  |  |  |  |  |  | # returns the data. | 
| 42 |  |  |  |  |  |  | sub SetConfigData { | 
| 43 | 0 |  |  | 0 | 0 |  | my ($self, $directive, $data) = @_; | 
| 44 |  |  |  |  |  |  | #print "Setting '$directive' to '$data'\n"; | 
| 45 | 0 |  |  |  |  |  | for (my $i=0; $i < scalar(@ {$self->{conf}}); $i++ ) { | 
|  | 0 |  |  |  |  |  |  | 
| 46 | 0 | 0 |  |  |  |  | if (uc($self->{conf}->[$i]->[0]) eq uc($directive)) { | 
| 47 |  |  |  |  |  |  | warn "Clobbering previous setting for '$directive'\n" | 
| 48 | 0 | 0 |  |  |  |  | if defined($self->{verbose});   # there is no self->{verbose}. Why no error? | 
| 49 | 0 |  |  |  |  |  | $self->{conf}->[$i]->[1] = $data; | 
| 50 | 0 |  |  |  |  |  | return $data; | 
| 51 |  |  |  |  |  |  | } | 
| 52 |  |  |  |  |  |  | } | 
| 53 | 0 |  |  |  |  |  | my @line = ($directive, $data); # stored as originally input | 
| 54 | 0 |  |  |  |  |  | push(@ {$self->{conf}}, \@line);    # push the listref on the list | 
|  | 0 |  |  |  |  |  |  | 
| 55 | 0 |  |  |  |  |  | return $data; | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | # this returns only the first one found in the path | 
| 59 |  |  |  |  |  |  | #  $Bin/sman.conf, ~/.sman.conf, /usr/local/etc/sman.conf, /etc/sman.conf | 
| 60 |  |  |  |  |  |  | sub FindDefaultConfigFile { | 
| 61 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 62 | 0 |  |  |  |  |  | my (@dirs) = $self->_getconfigdirs(); | 
| 63 | 0 |  |  |  |  |  | for(@dirs) { | 
| 64 | 0 | 0 |  |  |  |  | if (-e "$_/sman-defaults.conf") { | 
| 65 | 0 | 0 |  |  |  |  | if($self->_isverysafe("$_/sman-defaults.conf") ) { | 
| 66 | 0 |  |  |  |  |  | return "$_/sman-defaults.conf"; | 
| 67 |  |  |  |  |  |  | } else { | 
| 68 | 0 |  |  |  |  |  | warn "$0: Can't use $_/sman-defaults.conf: ownership not safe.\n"; | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  | } | 
| 71 |  |  |  |  |  |  | } | 
| 72 | 0 |  |  |  |  |  | return ""; | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | # finds and returns the config file(s). Looks for sman.conf(s) in: | 
| 76 |  |  |  |  |  |  | #  $Bin/sman.conf, ~/.sman.conf, /usr/local/etc/sman.conf, /etc/sman.conf | 
| 77 |  |  |  |  |  |  | #  (in that order) | 
| 78 |  |  |  |  |  |  | sub FindConfigFiles { | 
| 79 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 80 | 0 |  |  |  |  |  | my (@dirs, @configs) = $self->_getconfigdirs(); | 
| 81 | 0 |  |  |  |  |  | for(@dirs) { | 
| 82 | 0 |  |  |  |  |  | my $f = "$_/sman.conf"; | 
| 83 | 0 | 0 | 0 |  |  |  | if (-e $f && $self->_isverysafe($f) ) { | 
| 84 | 0 |  |  |  |  |  | push(@configs, $f); | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  | } | 
| 87 | 0 |  |  |  |  |  | my $defaultconfig = $self->FindDefaultConfigFile(); | 
| 88 | 0 | 0 |  |  |  |  | push(@configs, $defaultconfig) if ($defaultconfig); | 
| 89 | 0 |  |  |  |  |  | return @configs; | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | # we pass verbose here because it could be that the user's verbose setting is overridden from above | 
| 93 |  |  |  |  |  |  | # returns the name of the file read, or "" if none found. | 
| 94 |  |  |  |  |  |  | sub ReadDefaultConfigFile { | 
| 95 | 0 |  |  | 0 | 0 |  | my ($self, $verbose) = @_; | 
| 96 | 0 |  |  |  |  |  | my @configfiles = $self->FindConfigFiles();     # this includes the default one. | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | # read the first config file. | 
| 99 | 0 |  |  |  |  |  | for (@configfiles) { | 
| 100 | 0 | 0 |  |  |  |  | print "Reading config file $_\n" if $verbose; | 
| 101 | 0 |  |  |  |  |  | $self->ReadSingleConfigFile($_); | 
| 102 | 0 |  |  |  |  |  | last; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  | #print "Used config file '$configfiles[0]', found '" . join(", ", @configfiles) . "'.\n" | 
| 105 |  |  |  |  |  |  | #   if ($verbose || $self->GetConfigData("VERBOSE")); | 
| 106 | 0 | 0 |  |  |  |  | if (scalar(@configfiles)) { | 
| 107 | 0 |  |  |  |  |  | return $configfiles[0]; | 
| 108 |  |  |  |  |  |  | } else { | 
| 109 | 0 |  |  |  |  |  | return ""; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | # adds data from the file into our configuration data | 
| 114 |  |  |  |  |  |  | # returns the filename read, or "" on error | 
| 115 |  |  |  |  |  |  | sub ReadSingleConfigFile { | 
| 116 | 0 |  |  | 0 | 0 |  | my ($self, $file) = @_; | 
| 117 | 0 |  |  |  |  |  | my $prevline; | 
| 118 | 0 | 0 |  |  |  |  | if (!open(FILE, "< $file")) { | 
| 119 | 0 |  |  |  |  |  | die "Couldn't open $file: $!"; | 
| 120 |  |  |  |  |  |  | } else { | 
| 121 | 0 |  |  |  |  |  | while(defined(my $line = )) { | 
| 122 | 0 |  |  |  |  |  | chomp($line); | 
| 123 | 0 | 0 |  |  |  |  | if (defined($prevline)) { | 
| 124 | 0 |  |  |  |  |  | $line = "$prevline $line"; | 
| 125 | 0 |  |  |  |  |  | undef $prevline; | 
| 126 |  |  |  |  |  |  | } | 
| 127 | 0 | 0 |  |  |  |  | if ($line =~ s/\\$//) { # if the last char is \, remove it, and | 
| 128 | 0 |  |  |  |  |  | $prevline = $line;  # record it | 
| 129 |  |  |  |  |  |  | } else {                        # else parse it | 
| 130 | 0 | 0 |  |  |  |  | next if $line =~ /^\s*$/;   # empty line | 
| 131 | 0 | 0 |  |  |  |  | next if $line =~ /^\s*#/;   # a comment | 
| 132 | 0 |  |  |  |  |  | $line =~ s/^\s+//;          # strip leading ws | 
| 133 | 0 |  |  |  |  |  | my ($directive, $value) = split(/\s+/, $line, 2); | 
| 134 | 0 | 0 | 0 |  |  |  | if (defined($directive) && $directive && defined($value)) { | 
|  |  |  | 0 |  |  |  |  | 
| 135 | 0 |  |  |  |  |  | $self->SetConfigData($directive, $value); # will clobber old setting | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  | } | 
| 139 | 0 | 0 |  |  |  |  | close(FILE) || die "Couldn't close $file: $!"; | 
| 140 |  |  |  |  |  |  | } | 
| 141 | 0 |  |  |  |  |  | return $file; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub Reset { | 
| 145 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 146 | 0 |  |  |  |  |  | $self->{conf} = {}; # reset the puppy | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | # returns a list of params from the config | 
| 150 |  |  |  |  |  |  | sub GetConfigNames { | 
| 151 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 152 | 0 |  |  |  |  |  | my @names = (); | 
| 153 | 0 |  |  |  |  |  | for( @ {$self->{conf}} ) { | 
|  | 0 |  |  |  |  |  |  | 
| 154 | 0 | 0 | 0 |  |  |  | if (defined($_->[0]) && defined($_->[1])) { | 
| 155 | 0 |  |  |  |  |  | push(@names, $_->[0]); | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  | } | 
| 158 | 0 |  |  |  |  |  | return @names; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | sub Dump { | 
| 162 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 163 | 0 |  |  |  |  |  | my $str = "# Sman::Config settings:\n"; | 
| 164 | 0 |  |  |  |  |  | for (@ { $self->{conf} } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 165 | 0 |  |  |  |  |  | $str .= " $_->[0] $_->[1]\n"; | 
| 166 |  |  |  |  |  |  | } | 
| 167 | 0 |  |  |  |  |  | return $str; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | sub SetEnvironmentVariablesFromConfig | 
| 171 |  |  |  |  |  |  | { | 
| 172 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 173 | 0 |  |  |  |  |  | my $verbose = $self->GetConfigData("VERBOSE"); | 
| 174 | 0 |  |  |  |  |  | my @envs = grep { /^ENV_/ } $self->GetConfigNames(); | 
|  | 0 |  |  |  |  |  |  | 
| 175 | 0 |  |  |  |  |  | for my $e (@envs) { | 
| 176 | 0 |  |  |  |  |  | (my $copy = $e ) =~ s/^ENV_//; | 
| 177 | 0 |  |  |  |  |  | $ENV{uc($copy)} = $self->GetConfigData($e); | 
| 178 | 0 | 0 |  |  |  |  | print "Set ENV{$copy} to " . $self->GetConfigData($e) . "\n" | 
| 179 |  |  |  |  |  |  | if ($verbose); | 
| 180 |  |  |  |  |  |  | } | 
| 181 | 0 |  |  |  |  |  | return @envs; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub _getconfigdirs { | 
| 185 | 0 |  |  | 0 |  |  | my (@dirs, @configs) = ( $Bin );    # From FindBin | 
| 186 | 0 | 0 |  |  |  |  | if (defined($ENV{HOME})) { push(@dirs, $ENV{HOME}); } | 
|  | 0 |  |  |  |  |  |  | 
| 187 | 0 |  |  |  |  |  | push(@dirs, qw(/etc/ /usr/local/etc/)); | 
| 188 | 0 |  |  |  |  |  | return @dirs; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | #from perl cookbook "8.17. Testing a File for Trustworthiness" | 
| 192 |  |  |  |  |  |  | sub _issafe { | 
| 193 | 0 |  |  | 0 |  |  | my ($self, $path) = @_; | 
| 194 | 0 |  |  |  |  |  | my $info = stat($path); | 
| 195 | 0 | 0 |  |  |  |  | return 0 unless $info; | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | # owner neither superuser nor me | 
| 198 |  |  |  |  |  |  | # the real uid is in stored in the $< variable | 
| 199 | 0 | 0 | 0 |  |  |  | if (($info->uid != 0) && ($info->uid != $<)) { | 
| 200 | 0 |  |  |  |  |  | return 0; | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | # check whether group or other can write file. | 
| 204 |  |  |  |  |  |  | # use 066 to detect either reading or writing | 
| 205 | 0 | 0 |  |  |  |  | if ($info->mode & 022) {   # someone else can write this | 
| 206 | 0 | 0 |  |  |  |  | return 0 unless -d _;  # non-directories aren't safe | 
| 207 |  |  |  |  |  |  | # but directories with the sticky bit (01000) are | 
| 208 | 0 | 0 |  |  |  |  | return 0 unless $info->mode & 01000; | 
| 209 |  |  |  |  |  |  | } | 
| 210 | 0 |  |  |  |  |  | return 1; | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | #from perl cookbook "8.17. Testing a File for Trustworthiness" | 
| 214 |  |  |  |  |  |  | sub _isverysafe { | 
| 215 | 0 |  |  | 0 |  |  | my ($self, $path) = @_; | 
| 216 | 0 | 0 |  |  |  |  | return $self->_issafe($path) if sysconf(_PC_CHOWN_RESTRICTED); | 
| 217 | 0 | 0 |  |  |  |  | $path = getcwd() . '/' . $path if $path !~ m{^/}; | 
| 218 | 0 |  |  |  |  |  | do { | 
| 219 | 0 | 0 |  |  |  |  | return unless $self->_issafe($path); | 
| 220 | 0 |  |  |  |  |  | $path =~ s#([^/]+|/)$##;               # dirname | 
| 221 | 0 | 0 |  |  |  |  | $path =~ s#/$## if length($path) > 1;  # last slash | 
| 222 |  |  |  |  |  |  | } while length $path; | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 0 |  |  |  |  |  | return 1; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | 1; | 
| 234 |  |  |  |  |  |  | __END__ |