Subversion Repositories DevTools

Rev

Rev 6776 | Rev 7397 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1038 dpurdie 1
package Utils;
2
use File::Basename;
3
use Data::Dumper;
6776 dpurdie 4
$Data::Dumper::Sortkeys  = 1;
1038 dpurdie 5
 
6
sub trunc ($) {
5398 dpurdie 7
    my $file = shift;
8
    open my $fh, ">$file" or die "Can't write $file: $!\n";
9
    print $fh '';
10
    close $fh;
1038 dpurdie 11
}
12
 
13
sub checkpid ($) {
5398 dpurdie 14
    my $conf = shift;
15
    my $pidfile = $conf->{'pidfile'};
16
    my $logger = $conf->{logger};
1038 dpurdie 17
 
5398 dpurdie 18
    trunc $pidfile unless -f $pidfile;
1038 dpurdie 19
 
5398 dpurdie 20
    open my $fh, $pidfile or $logger->err("Can't read pidfile $pidfile: $!");
21
    my ($pid) = <$fh>;
22
    close $fh;
1038 dpurdie 23
 
5398 dpurdie 24
    if (defined $pid) {
25
        chomp $pid;
26
        $logger->err("Process with pid $pid already running") if 0 < int $pid && kill 0, $pid;
27
    }
1038 dpurdie 28
}
29
 
30
sub writepid ($) {
5398 dpurdie 31
    my $conf = shift;
32
    my $logger = $conf->{logger};
33
    my $pidfile = $conf->{'pidfile'};
1038 dpurdie 34
 
5398 dpurdie 35
    open my $fh, ">$pidfile" or $logger->err("Can't write pidfile: $!");
36
    print $fh "$$\n";
37
    close $fh;
1038 dpurdie 38
}
39
 
40
#-------------------------------------------------------------------------------
41
# Function        : trimstr
42
#
43
# Description     : Trim a string
44
#
45
# Inputs          : Array of strings
46
#
47
# Returns         : Array of trimmed strings
48
#
49
sub trimstr (@) {
5398 dpurdie 50
    my @str = @_;
1038 dpurdie 51
 
5398 dpurdie 52
    for (@str) {
53
        chomp;
54
        s/^[\t\s]+//;
55
        s/[\t\s]+$//;
56
    }
1038 dpurdie 57
 
5398 dpurdie 58
    return @str;
1038 dpurdie 59
}
60
 
61
#-------------------------------------------------------------------------------
62
# Function        : readconf
63
#
64
# Description     : Process config data
65
#
66
# Inputs          : $conffile   - Config file to read
67
#                   $pdata      - Ref to hash of controlling data
68
#                                 hashed by config item and '.ignore'
69
#                                 Values are:
70
#                                   default     - Default value
71
#                                   mandatory   - Must be present
7387 dpurdie 72
#                                   requires    - Items must also be defined (accept comma sep list)
1038 dpurdie 73
#                                   fmt         - Format
74
#                                                   'size'      - convert to block
75
#                                                   'period'    - convert to time
76
#                                                   'dir'       - Directory
77
#                                                   'file'      - File
78
#                                                   'vfile'     - File. Path must exist
79
#                                                   'int'       - integer
80
#                                                   'int_list'  - A list of integers
81
#                                                   'text'      - Random Text
82
#                                                   'bool'      - y/n 1/0
83
#                               .ignore       - Regexp of items to ignore
7387 dpurdie 84
#                               .oneOf        - Array of Array of config items. One item in each array must be defined
1038 dpurdie 85
#
86
#
87
#
88
# Returns         : $conf       - Ref to config data
89
#                   $errors     - Ref to an array of error messages
90
#
91
 
92
sub readconf
93
{
94
    my ($conffile, $pdata) = @_;
95
    my @errors;
5398 dpurdie 96
    my $conf;
1038 dpurdie 97
    my $ignored;
98
 
99
    if ( open my $fh, $conffile )
100
    {
5398 dpurdie 101
        while (<$fh>) {
102
            next if /^[\t\w]+#/;
103
            s/#.*//;
1038 dpurdie 104
 
5398 dpurdie 105
            my ($key, $val) = trimstr split '=', $_, 2;
106
            next unless defined $val;
2571 dpurdie 107
 
108
            #
109
            #   Create hash of key : value
110
            #   Special handling of int_list. Multiple definitions are allowed
111
            #
112
            if ( exists $conf->{$key} ) {
113
                if ( exists $pdata->{$key} && $pdata->{$key}{'fmt'} eq 'int_list' ) {
114
                    $conf->{$key} = join (' ', $conf->{$key} ,$val);
115
                } else {
116
                    push @errors, "Multiple configuration of: $key";
117
                }
118
            } else {
119
                $conf->{$key} = $val;
120
            }
5398 dpurdie 121
        }
122
        close $fh;
1038 dpurdie 123
 
124
        #
125
        #   Validate mandatory entries
126
        #   Insert defaults that are not present
7387 dpurdie 127
        #   Check conflicts
128
        #   Check required entries
1038 dpurdie 129
        #
130
        while ( (my ($key, $entry)) = each %{$pdata} )
131
        {
7387 dpurdie 132
            next if $key =~ m~^\.~;
1040 dpurdie 133
            if ( exists ($entry->{mandatory}) && $entry->{mandatory}  )
1038 dpurdie 134
            {
135
                if ( !exists $conf->{$key} )
136
                {
137
                    push @errors, "Mandatory config not found: $key";
138
                }
139
            }
140
 
7387 dpurdie 141
            if ( exists $entry->{requires} && exists $conf->{$key} ) {
142
                foreach my $rkey (split (',',  $entry->{requires}))
143
                {
144
                    if ( !exists $conf->{$rkey} )
145
                    {
146
                        push @errors, "$key requires that $rkey also be specified";
147
                    }
148
                }
149
            }
150
 
1040 dpurdie 151
            if ( exists $entry->{default} )
1038 dpurdie 152
            {
153
                if ( !exists $conf->{$key} )
154
                {
1040 dpurdie 155
                    $conf->{$key} = $entry->{default};
1038 dpurdie 156
                }
157
            }
158
        }
159
 
160
        #
7387 dpurdie 161
        #   oneOf processing
162
        #   
163
        if ($pdata->{'.oneOf'}) {
164
            foreach my $set ( @{$pdata->{'.oneOf'}} ) {
165
                my $found = 0;
166
                foreach my $key ( @{$set}) {
167
                    if (exists $conf->{$key} && defined $conf->{$key}) {
168
                        $found++;
169
                    }
170
                }
171
                if ($found ne 1) {
172
                    push @errors, "Require exactly one of " . join(',',  @{$set});
173
                }
174
            }
175
 
176
        }
177
 
178
        #
1038 dpurdie 179
        #   Scan all user items
180
        #
181
        my $ignore_re = $pdata->{'.ignore'} ;
182
        while ( (my ($key, $data)) = each %{$conf} )
183
        {
184
            if ( $ignore_re )
185
            {
186
                #
187
                #   Ignore entry is a hash of entries to ignore
188
                #       Key is RE with a group
189
                #       Value is the name of config item under which the
190
                #       data will be stored. The re group will be used to
191
                #       as the key for the final hash.
192
                #
193
                my $done = 0;
194
                while ( (my ($re, $ename)) = each %{$ignore_re} )
195
                {
196
                    if ( $key =~ m~$re~ )
197
                    {
198
                        $ignored->{$ename}{$1} = $data;
199
                        $done = 1;
200
                    }
201
                }
202
                if ( $done )
203
                {
204
                    delete $conf->{$key};
205
                    next;
206
                }
207
            }
208
 
209
            if ( !exists $pdata->{$key} )
210
            {
211
                push @errors, "Unknown config item: $key";
212
                next;
213
            }
214
 
215
            my $fmt = $pdata->{$key}{'fmt'};
216
            unless ( $fmt )
217
            {
218
                push @errors, "Unconfigured config item: $key";
219
                next;
220
            }
221
 
222
            if ( $fmt eq 'size' ) {
223
                $conf->{$key} = toBytes( $data );
2571 dpurdie 224
 
1038 dpurdie 225
            } elsif ( $fmt eq 'period' ) {
226
                $conf->{$key} = timeToSecs( $data );
227
 
228
            } elsif ( $fmt eq 'dir' ) {
229
                if ( ! -d $data ) {
230
                    push @errors, "Directory not found:$key: $data";
231
                }
232
            } elsif ( $fmt eq 'file' ) {
233
                if ( ! -f $data ) {
234
                    push @errors, "File not found:$key: $data";
235
                }
236
            } elsif ( $fmt eq 'vfile' ) {
237
                 my($filename, $pdir, $suffix) = fileparse($data);
238
                if ( ! -d $pdir ) {
239
                    push @errors, "Directory not found:$key: $pdir";
240
                }
241
            } elsif ( $fmt eq 'int' ) {
242
                unless ( $data =~ m~^\d+$~ ) {
243
                    push @errors, "Invalid Number:$key: $data";
244
                }
245
            } elsif ( $fmt eq 'int_list' ) {
246
                unless ( $data =~ m~^((\d+)[, ]*)*$~ ) {
247
                    push @errors, "Invalid Number List:$key: $data";
248
                }
249
            } elsif ( $fmt eq 'bool' ) {
250
                $data = lc $data;
251
                if ( $data eq '1' || $data eq 'y'  || $data eq 'yes') {
252
                    $conf->{$key} = 1;
253
                } elsif ( $data eq '0' || $data eq 'n' || $data eq 'no') {
254
                    $conf->{$key} = 0;
255
                } else {
256
                    push @errors, "Invalid Boolean:$key: $data";
257
                }
258
 
259
            } elsif ( $fmt eq 'text' ) {
260
 
261
            } else {
262
                push @errors, "Unknown config fmt:$key, $fmt";
263
            }
264
        }
265
    }
266
    else
267
    {
268
        push @errors, "Can't read $conffile: $!";
269
    }
270
 
271
    #
272
    #   Merge in ignored entries
273
    #
274
    while ( my ($key, $data ) = each %{$ignored})
275
    {
276
        $conf->{$key} = $data;
277
    }
278
 
279
#DebugDumpData('config', $conf );
1040 dpurdie 280
#DebugDumpData('errors' , \@errors);
1038 dpurdie 281
    return $conf, \@errors;
282
}
283
 
284
#-------------------------------------------------------------------------------
285
# Function        : mtime
286
#
287
# Description     : Return the modification time of a file
288
#
289
# Inputs          : $path
290
#
291
# Returns         : modification time
292
#                   mode
293
#
294
sub mtime
295
{
296
    my ($path) = @_;
297
    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
298
       $atime,$mtime,$ctime,$blksize,$blocks) = stat($path);
299
    return (($mtime || 0), $mode);
300
}
301
 
302
#-------------------------------------------------------------------------------
303
# Function        : timeToSecs
304
#
305
# Description     : Process a string and convert it to seconds
306
#                   Handle:
307
#                       100     -> 100 seconds
308
#                       100s    -> 100 seconds
309
#                       5m      -> 5 minutes
310
#                       10h     -> 10 hours
311
#                       1d      -> 1 day
312
#                       1d10h   -> 1d and 10 hours
313
#
314
#
315
# Inputs          : String
316
#
317
# Returns         : Seconds
318
#
319
sub timeToSecs
320
{
321
    my ($src) = @_;
322
    my $result = 0;
323
    while ( $src =~ s~(\d+)([smhd])?~~ )
324
    {
325
        my $factor = 1;
326
        my $base = $1;
327
        my $mode = $2 || 's';
328
        if ( $mode eq 'd' ) {
329
            $factor = 24 * 60 * 60;
330
        } elsif ( $mode eq 'h' ) {
331
            $factor = 60 * 60;
332
        } elsif ( $mode eq 'm' ) {
333
            $factor = 60;
334
        }
335
        $result += $base * $factor;
336
    }
337
    return $result;
338
}
339
 
340
#-------------------------------------------------------------------------------
7387 dpurdie 341
# Function        : TouchFile 
342
#
343
# Description     : touch a file
344
#                   Real use is to touch a marker file
345
#
346
# Inputs          : path        - path to the file
347
#
348
# Returns         : TRUE if an error occured in creating the file
349
#
350
sub TouchFile($$)
351
{
352
 
353
    my ($conf, $path) = @_;
354
    my $logger = $conf->{logger};
355
    my $result = 0;
356
    my $tfh;
357
 
358
    $logger->verbose( "Touching: $path" );
359
    if ( ! -f $path )
360
    {
361
        open ($tfh, ">>", $path) || ($result = 1);
362
        close $tfh;
363
    }
364
    else
365
    {
366
 
367
        #
368
        #   Modify the file
369
        #
370
        #   Need to physically modify the file
371
        #   Need to change the 'change time' on the file. Simply setting the
372
        #   last-mod and last-access is not enough to get past WIN32
373
        #   OR 'utime()' does not work as expected
374
        #
375
        #   Read in the first character of the file, rewind and write it
376
        #   out again.
377
        #
378
        my $data;
379
        open ($tfh , "+<", $path ) || return 1;
380
        if ( read ( $tfh, $data, 1 ) )
381
        {
382
            seek  ( $tfh, 0, 0 );
383
            print $tfh $data;
384
        }
385
        else
386
        {
387
            #
388
            #   File must have been of zero length
389
            #   Delete the file and create it
390
            #
391
            close ($tfh);
392
            unlink ( $path );
393
            open ($tfh, ">>", $path) || ($result = 1);
394
        }
395
        close ($tfh);
396
    }
397
 
398
    #
399
    #   Ensure all can remove the file
400
    #   May be created by root and need to be deleted by the blatDaemon
401
    #
402
    chmod 0666, $path;
403
    return $result;
404
}
405
 
406
#-------------------------------------------------------------------------------
1038 dpurdie 407
# Function        : toBytes
408
#
409
# Description     : Process a string and convert it to a byte count
410
#                   Handle:
411
#                       100     -> 100 bytes
412
#                       10k     -> 10 kilobytes
413
#                       5m      -> 5 Megabytes
414
#                       1g      -> 1 Gigabytes
415
#                       10b     -> 10 Blocks
416
#
417
# Inputs          : String
418
#
419
# Returns         : Byte Count
420
#
421
sub toBytes
422
{
423
    my ($src) = @_;
424
    my $result = 0;
425
    if ( $src =~ m~(\d+)([kmgb]?)~i )
426
    {
427
        my $factor = 1;
428
        my $base = $1;
429
        my $mode = lc($2) || '-';
430
        if ( $mode eq 'g' ) {
431
            $factor = 1024 * 1024 * 1024;
432
        } elsif ( $mode eq 'm' ) {
433
            $factor = 1024 * 1024;
434
        } elsif ( $mode eq 'k' ) {
435
            $factor = * 1024;
436
        } elsif ( $mode eq 'b' ) {
437
            $factor = 1024;
438
        }
439
        $result = $base * $factor;
440
    }
441
    return $result;
442
}
443
 
444
#-------------------------------------------------------------------------------
445
# Function        : DebugDumpData
446
#
447
# Description     : Dump a data structure
448
#
449
# Inputs          : $name           - A name to give the structure
450
#                   @refp           - An array of references
451
#
452
# Returns         :
453
#
454
sub DebugDumpData
455
{
456
    my ($name, @refp) = @_;
457
 
458
    my $ii = 0;
459
    foreach  ( @refp )
460
    {
461
        print Data::Dumper->Dump ( [$_], ["*[Arg:$ii] $name" ]);
462
        $ii++
463
    }
464
}
465
 
466
 
467
1;