source: trunk/scripts/samhainadmin.pl.in @ 415

Last change on this file since 415 was 415, checked in by katerina, 9 years ago

Fixes for tickets #314, #315, #316, #317, #318, #319, #320, and #321.

File size: 19.1 KB
Line 
1#! /usr/bin/perl
2
3# Copyright Rainer Wichmann (2004)
4#
5# License Information:
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19#
20
21use warnings;
22use strict;
23use Getopt::Long;
24use File::Basename;
25use File::Copy;
26use File::stat;
27use File::Temp qw/ tempfile tempdir unlink0 /;
28use IO::Handle;
29use Fcntl qw(:DEFAULT :flock);
30use Tie::File;
31
32# Do I/O to the data file in binary mode (so it
33# wouldn't complain about invalid UTF-8 characters).
34use bytes;
35
36File::Temp->safe_level( File::Temp::HIGH );
37
38my %opts = ();
39my $action;
40my $file1;
41my $file2;
42my $passphrase;
43my $secretkeyring;
44my $return_from_sign = 0;
45my $no_print_examine = 0;
46my $no_remove_lock   = 0;
47my $base = basename($0);
48
49my $cfgfile  = "@myconffile@";
50my $datafile = "@mydatafile@";
51my $daemon   = "@sbindir@/@install_name@";
52my $gpg      = "@mygpg@";
53
54my $TARGETKEYID = "@mykeyid@";
55my $KEYTAG      = "@mykeytag@";
56
57$cfgfile  =~ s/^REQ_FROM_SERVER//;
58$datafile =~ s/^REQ_FROM_SERVER//;
59
60$gpg = "gpg" if ($gpg eq "");
61
62sub check_gpg_agent() {
63    my  $gpgconf = "$ENV{'HOME'}/.gnupg/gpg.conf";
64
65    if (!-f "$gpgconf") {
66        $gpgconf = "$ENV{'HOME'}/.gnupg/options";
67    }
68
69    if (-f $gpgconf) {
70
71        my @array = ();
72        tie @array, 'Tie::File', $gpgconf or die "Cannot tie ${gpgconf}: $!";
73        my @grep = grep(/^\s*use-agent/, @array);
74       
75        # print "matches = $#grep\n";
76       
77        if ($#grep >= 0)
78        {
79            if (exists $ENV{'GPG_AGENT_INFO'})
80            {
81                my $socke = $ENV{'GPG_AGENT_INFO'};
82                $socke =~ s/:.*//;
83               
84                # print "socke = $socke\n";
85               
86                if (! -S $socke)
87                {
88                    print "--------------------------------------------------\n";
89                    print "\n";
90                    print " GPG is set to use gpg-agent, but GPG agent is";
91                    print " not running, though GPG_AGENT_INFO is defined.\n\n";
92                    print " Please restart gpg-agent, or remove the use-agent\n";
93                    print " option from ${gpgconf} and unset GPG_AGENT_INFO\n\n";
94                    print "--------------------------------------------------\n";
95                    print "\n";
96                    exit 1;
97                }
98            }
99            else
100            {
101                print "--------------------------------------------------\n";
102                print "\n";
103                print " GPG is set to use gpg-agent, but ";
104                print " GPG_AGENT_INFO is not defined.\n\n";
105                print " Please start gpg-agent, or remove the use-agent\n";
106                print " option from ${gpgconf}\n\n";
107                print "--------------------------------------------------\n";
108                print "\n";
109                exit 1;
110            }
111        }
112        untie @array;
113    }
114}
115
116
117sub usage() {
118    print "Usage:\n";
119    print "  $base { -m F | --create-cfgfile }    [options] [in.cfgfile]\n";
120    print "    Sign the configuration file. If in.cfgfile is given, sign it\n";
121    print "    and install it as configuration file.\n\n";
122
123    print "  $base { -m f | --print-cfgfile }     [options] \n";
124    print "    Print the configuration file to stdout. Signatures are removed.\n\n";
125
126    print "  $base { -m D | --create-datafile }   [options] [in.datafile]\n";
127    print "    Sign the database file. If in.datafile is given, sign it\n";
128    print "    and install it as database file.\n\n";
129
130    print "  $base { -m d | --print-datafile }    [options] \n";
131    print "    Print the database file to stdout. Signatures are removed. Use\n";
132    print "    option --list to list files in database rather than printing the raw file.\n\n";
133
134    print "  $base { -m R | --remove-signature }  [options] file1 [file2 ...]\n";
135    print "    Remove cleartext signature from input file(s). The file\n";
136    print "    is replaced by the non-signed file.\n\n";
137
138    print "  $base { -m E | --sign }              [options] file1 [file2 ...]\n";
139    print "    Sign file(s) with a cleartext signature. The file\n";
140    print "    is replaced by the signed file.\n\n";
141
142    print "  $base { -m e | --examine }           [options] file1 [file2 ...]\n";
143    print "    Report signature status of file(s).\n\n";
144
145    print "  $base { -m G | --generate-keys }     [options] \n";
146    print "    Generate a PGP keypair to use for signing.\n\n";
147
148    print "Options:\n";
149    print "  -c cfgfile    --cfgfile cfgfile\n";
150    print "    Select an alternate configuration file.\n\n";
151
152    print "  -d datafile   --datafile datafile\n";
153    print "    Select an alternate database file.\n\n";
154
155    print "  -p passphrase --passphrase passphrase\n";
156    print "    Set the passphrase for gpg. By default, gpg will ask.\n\n";
157
158    print "  -s secretkeyring --secretkeyring secretkeyring\n";
159    print "    Select an alternate secret keyring for gpg.\n";
160    print "    Will use '$ENV{'HOME'}/.gnupg/secring.gpg' by default.\n\n";
161
162    print "  -l            --list\n";
163    print "    List the files in database rather than printing the raw file.\n\n";
164
165    print "  -v            --verbose\n";
166    print "    Verbose output.\n\n";
167    return;
168}
169
170sub check_gpg_uid () {
171    if (0 != $>) {
172        print "--------------------------------------------------\n";
173        print "\n";
174        print " You are not root. Please remember that samhain/yule\n";
175        print " will use the public keyring of root to verify a signature.\n";
176        print "\n";
177        print "--------------------------------------------------\n";
178    } else {
179        if (!("@yulectl_prg@" =~ //)) {
180            print "--------------------------------------------------\n";
181            print "\n";
182            print " Please remember that yule will drop root after startup. Signature\n";
183            print " verification on SIGHUP will fail if you do not import the public key\n";
184            print " into the keyring of the non-root yule user.\n";
185            print "\n";
186            print "--------------------------------------------------\n";
187        }
188    }
189}
190   
191sub check_gpg_sign () {
192    if ( defined($secretkeyring)) {
193        if ( (!-d "$secretkeyring")){
194            print "--------------------------------------------------\n";
195            print "\n";
196            print " Secret keyring $secretkeyring not found!\n";
197            print "\n";
198            print " Please check the path/name of the alternate secret keyring.\n";
199            print "\n";
200            print "--------------------------------------------------\n";
201            print "\n";
202            exit;
203        }
204    } else {
205        if ( (!-d "$ENV{'HOME'}/.gnupg") || (!-e "$ENV{'HOME'}/.gnupg/secring.gpg")) {
206            print "--------------------------------------------------\n";
207            print "\n";
208            if (!-d "$ENV{'HOME'}/.gnupg") {
209                print " Directory \$HOME/.gnupg not found!\n";
210            } else {
211                print " Secret keyring \$HOME/.gnupg/secring.gpg not found!\n";
212            }
213            print "\n";
214            print " This indicates that you have never created a \n";
215            print " public/private keypair, and thus cannot sign.\n";
216            print " \n";
217            print " Please use $0 --generate-keys or gpg --gen-key\n";
218            print " to generate a public/private keypair first.\n";
219            print "\n";
220            print "--------------------------------------------------\n";
221            print "\n";
222            exit;
223        }
224    }
225}
226
227sub check_gpg_verify () {
228    if ( (!-d "$ENV{'HOME'}/.gnupg") || (!-e "$ENV{'HOME'}/.gnupg/pubring.gpg")) {
229        print "--------------------------------------------------\n";
230        print "\n";
231        if (!-d "$ENV{'HOME'}/.gnupg") {
232            print " Directory \$HOME/.gnupg not found!\n";
233        } else {
234            print " Public keyring \$HOME/.gnupg/pubring.gpg not found!\n";
235        }
236        print "\n";
237        print " This indicates that you have never used gpg before \n";
238        print " and/or have no public keys to verify signatures.\n";
239        print " \n";
240        print " Please use 'gpg --export key_id' to export the public\n";
241        print " signing key of the user who is signing the\n";
242        print " configuration/database files.\n\n";
243        print " Then you can use 'gpg --import keyfile' to import the key\n";
244        print " into this user's public keyring.\n";
245        print "\n";
246        print "--------------------------------------------------\n";
247        print "\n";
248        exit;
249    }
250}
251
252
253sub generate () {
254    my $command = "$gpg --homedir $ENV{'HOME'}/.gnupg --gen-key";
255    check_gpg_uid();
256    system ($command) == 0
257        or die "system $command failed: $?";
258    exit;
259}
260
261sub examine () {
262    my $iscfg = 0;
263    my $have_fp  = 0;
264    my $have_sig = 0;
265    my $message = '';
266    my $retval  = 9;
267    my $fh;
268    my $filename;
269
270    if (!($file1 =~ /^\-$/)) {
271        die ("Cannot open $file1 for read: $!") unless ((-e $file1) && (-r _));
272    }
273    open FIN,  "<$file1" or die "Cannot open $file1 for read: $!";
274
275    my $dir = tempdir( CLEANUP => 1 );
276    $filename = $dir . "/exa_jhfdbilw." . $$;
277    open $fh, ">$filename" or die "Cannot open $filename";
278    autoflush $fh 1;
279
280    while (<FIN>) {
281        print $fh $_;
282        if ($_ =~ /^\s*\[Misc\]/) {
283            $iscfg = 1;
284        }
285    }
286    if ($iscfg == 1) {
287        $message .=  "File $file1 is a configuration file\n\n";
288    } else {
289        $message .=  "File $file1 is a database file\n\n";
290    }
291
292
293    my $command = "$gpg --homedir $ENV{'HOME'}/.gnupg --status-fd 1 ";
294    $command .= "--verbose " if (defined($opts{'v'}));
295    $command .= "--verify $filename ";
296    if (defined($opts{'v'})) {
297        $command .= "2>&1";
298    } else {
299        $command .= "2>/dev/null";
300    }
301
302    print STDOUT "Using: $command\n\n" if (defined($opts{'v'}));
303    open  GPGIN, "$command |" or die "Cannot fork: $!";
304
305    while (<GPGIN>) {
306        if ($_ =~ /^\[GNUPG:\] GOODSIG ([0-9A-F]+) (.*)$/) {
307            $message .= "GOOD signature with key: $1\n";
308            $message .= "Key owner:               $2\n";
309            $have_sig = 1;
310            $retval   = 0;
311        }
312        if ($_ =~ /^\[GNUPG:\] VALIDSIG ([0-9A-F]+) ([0-9\-]+)\s/) {
313            $message .= "Key fingerprint:         $1\n";
314            $message .= "Signature generated on:  $2\n\n";
315            $have_fp = 1;
316            $message .=  "This file is signed with a valid signature.\n"
317                if ($have_sig == 1);
318            $have_sig = 1;
319            $have_fp = 1;
320        }
321        if ($_ =~ /^\[GNUPG:\] NODATA 1/) {
322            $message .=  "NO signature found.\n\n";
323            $message .=  "This file is not signed !!!\n";
324            $have_sig = 1;
325            $have_fp = 1;
326            $retval  = 2;
327        }
328        if ($_ =~ /^\[GNUPG:\] BADSIG ([0-9A-F]+) (.*)$/) {
329            $message .=  "BAD signature with key: $1\n";
330            $message .=  "Key owner:              $2\n\n";
331            $message .=  "This file is signed with an invalid signature !!!\n";
332            $have_sig = 1;
333            $have_fp = 1;
334            $retval = 1;
335        }
336        if ($_ =~ /^\[GNUPG:\] NO_PUBKEY ([0-9A-F]+)/) {
337            $message .=  "NOT CHECKED signature with key: $1\n\n";
338            $message .=  "The signature of this file cannot be checked: no public key available !!!\n";
339            $have_sig = 1;
340            $have_fp = 1;
341            $retval  = 1;
342        }
343        print STDOUT $_ if (defined($opts{'v'}));
344    }
345    close (GPGIN);
346    print STDOUT "\n" if (defined($opts{'v'}));
347    if ($have_sig == 0) {
348        $message .=  "NO valid signature found\n";
349    }
350    elsif ($have_fp == 0) {
351        $message .=  "NO fingerprint found\n";
352    }
353    close (FIN);
354    if ($no_print_examine == 0) {
355        print STDOUT $message;
356    }
357    unlink0( $fh, $filename ) or die "Cannot unlink $filename safely";
358    return $retval;
359}
360
361sub remove () {
362    my $bodystart = 1;
363    my $sigstart  = 0;
364    my $sigend    = 0;
365    my $filename  = "";
366    my $fh;
367    my $stats;
368
369    open FH, "<$file1" or die "Cannot open file $file1 for read: $!";
370    if (!($file1 =~ /^\-$/)) {
371        flock(FH, LOCK_EX) unless ($no_remove_lock == 1);
372        my $dir = tempdir( CLEANUP => 1 ) or die "Tempdir failed";
373        $filename = $dir . "/rem_iqegBCQb." . $$;
374        open $fh, ">$filename" or die "Cannot open $filename";
375        $stats = stat($file1);
376        # ($fh, $filename) = tempfile(UNLINK => 1);
377    } else {
378        open $fh, ">$file1" or die "Cannot open file $file1 for write: $!";
379    }
380    autoflush $fh 1;
381    while (<FH>) {
382        if ($_ =~ /^-----BEGIN PGP SIGNED MESSAGE-----/) {
383            $sigstart = 1;
384            $bodystart = 0;
385            next;
386        } elsif (($sigstart == 1) && ($_ =~ /^\s+$/)) {
387            $sigstart = 0;
388            $bodystart = 1;
389            next;
390        } elsif ($_ =~ /^-----BEGIN PGP SIGNATURE-----/) {
391            $bodystart = 0;
392            $sigend = 1;
393            next;
394        } elsif (($sigend == 1) && ($_ =~ /^-----END PGP SIGNATURE-----/)) {
395            $sigend = 0;
396            $bodystart = 1;
397            next;
398        }
399        if ($bodystart == 1) {
400            print $fh $_;
401        }
402    }
403    if (!($file1 =~ /^\-$/)) {
404        copy("$filename", "$file1")
405            or die "Copy $filename to $file1 failed: $!";
406        chmod $stats->mode, $file1;
407        chown $stats->uid, $stats->gid, $file1;
408        flock(FH, LOCK_UN) unless ($no_remove_lock == 1);
409        close FH;
410    }
411    unlink0( $fh, $filename ) or die "Cannot unlink $filename safely";
412    return;
413}
414
415sub print_cfgfile () {
416    my $bodystart = 0;
417    my $sigstart  = 0;
418
419    if (!defined($file2)) {
420        $file2 = '-';
421    }
422
423    open FH, "<$file1" or die "Cannot open file $file1 for read: $!";
424    open FO, ">$file2" or die "Cannot open file $file2 for write: $!";
425    while (<FH>) {
426        if ($_ =~ /^-----BEGIN PGP SIGNED MESSAGE-----/) {
427            $sigstart = 1;
428            next;
429        } elsif (($sigstart == 1) && ($_ =~ /^\s+$/)) {
430            $sigstart = 0;
431            $bodystart = 1;
432            next;
433        } elsif ($_ =~ /^-----BEGIN PGP SIGNATURE-----/) {
434            $bodystart = 0;
435            exit;
436        }
437        if ($bodystart == 1) {
438            print FO $_;
439        }
440    }
441    exit;
442}
443sub print_datafile () {
444    die ("Cannot find program $daemon")
445        unless (-e $daemon);
446    if (defined($opts{'v'})) {
447        open FH, "$daemon --full-detail -d $datafile |"
448            or die "Cannot open datafile $datafile for read: $!";
449    } else {
450        open FH, "$daemon -d $datafile |"
451            or die "Cannot open datafile $datafile for read: $!";
452    }
453    while (<FH>) {
454        print $_;
455    }
456    exit;
457}
458
459sub sign_file () {
460
461    my $fileout = '';
462    my $bodystart = 1;
463    my $sigstart  = 0;
464    my $sigend    = 0;
465    my $stats;
466    my $fh1;
467    my $filename1;
468    my $flag1     = 0;
469
470    check_gpg_uid();
471    check_gpg_agent();
472
473    if (!defined($file2)) {
474        $file2 = $file1;
475    }
476
477    if ($file1 =~ /^\-$/) {
478        my $dir = tempdir( CLEANUP => 1 ) or die "Tempdir failed";
479        $filename1 = $dir . "/sig_vs8827sd." . $$;
480        open $fh1, ">$filename1" or die "Cannot open $filename1";
481        $flag1 = 1;
482        # my ($fh1, $filename1) = tempfile(UNLINK => 1);
483
484        while (<STDIN>) {
485          if ($_ =~ /^-----BEGIN PGP SIGNED MESSAGE-----/) {
486            $sigstart = 1;
487            $bodystart = 0;
488            next;
489          } elsif (($sigstart == 1) && ($_ =~ /^\s+$/)) {
490            $sigstart = 0;
491            $bodystart = 1;
492            next;
493          } elsif ($_ =~ /^-----BEGIN PGP SIGNATURE-----/) {
494            $bodystart = 0;
495            $sigend = 1;
496            next;
497          } elsif (($sigend == 1) && ($_ =~ /^-----END PGP SIGNATURE-----/)) {
498            $sigend = 0;
499            $bodystart = 1;
500            next;
501          }
502          if ($bodystart == 1) {
503            print $fh1 $_;
504          }
505          #
506          # print $fh1 $_;
507          #
508        }
509        $file1 = $filename1;
510        $fileout = '-';
511    } else {
512        open (LOCKFILE, "<$file1") or die "Cannot open $file1: $!";
513        flock(LOCKFILE, LOCK_EX);
514        $no_print_examine = 1;
515        $no_remove_lock   = 1;
516        if (examine() < 2) {
517            remove();
518        }
519        $fileout = $file1 . ".asc";
520        $stats   = stat($file1)
521            or die "No file $file1: $!";
522    }
523
524    if (defined($passphrase)) {
525        local $SIG{PIPE} = 'IGNORE';
526        my $command = "$gpg --homedir $ENV{'HOME'}/.gnupg --passphrase-fd 0 -a ${KEYTAG} ${TARGETKEYID} --clearsign -o $fileout --not-dash-escaped ";
527        $command .= "--secret-keyring $secretkeyring " if (defined($opts{'s'}));
528        $command .= "$file1";
529        open (FH, "|$command")  or die "can't fork: $!";
530        print FH "$passphrase"  or die "can't write: $!";
531        close FH                or die "can't close: status=$?";
532    } else {
533        my $command = "$gpg --homedir $ENV{'HOME'}/.gnupg                   -a ${KEYTAG} ${TARGETKEYID} --clearsign -o $fileout --not-dash-escaped ";
534        $command .= "--secret-keyring $secretkeyring " if (defined($opts{'s'}));
535        $command .= "$file1";
536        system("$command") == 0
537            or die "system $command failed: $?";
538    }
539
540    if (!($fileout =~ /^\-$/)) {
541        my $st_old = stat($file1)
542            or die "No file $file1: $!";
543        my $st_new = stat($fileout)
544            or die "No file $fileout: $!";
545        die ("Signed file is smaller than unsigned file")
546            unless ($st_new->size > $st_old->size);
547        move("$fileout", "$file2")
548            or die "Move $fileout to $file2 failed: $!";
549        chmod $stats->mode, $file2;
550        chown $stats->uid, $stats->gid, $file2;
551        flock(LOCKFILE, LOCK_UN);
552    }
553
554    if ($flag1 == 1) {
555        unlink0( $fh1, $filename1 ) or die "Cannot unlink $filename1 safely";
556    }
557    if ($return_from_sign == 1) {
558        return;
559    }
560    exit;
561}
562
563Getopt::Long::Configure ("posix_default");
564Getopt::Long::Configure ("bundling");
565# Getopt::Long::Configure ("debug");
566
567GetOptions (\%opts, 'm=s', 'h|help', 'v|verbose', 'l|list',
568            'c|cfgfile=s',
569            'd|datafile=s',
570            'p|passphrase=s',
571            's|secretkeyring=s',
572            'create-cfgfile',  # -m F
573            'print-cfgfile',   # -m f
574            'create-datafile', # -m D
575            'print-datafile',  # -m d
576            'remove-signature',# -m R
577            'sign',            # -m E
578            'examine',         # -m e
579            'generate-keys');  # -m G
580
581if (defined ($opts{'h'})) {
582    usage();
583    exit;
584}
585
586if (defined($opts{'c'})) {
587    $cfgfile = $opts{'c'};
588}
589if (defined($opts{'d'})) {
590    $datafile = $opts{'d'};
591}
592if (defined($opts{'p'})) {
593    $passphrase = $opts{'p'};
594}
595if (defined($opts{'s'})) {
596    $secretkeyring = $opts{'s'};
597}
598
599if (defined ($opts{'m'}) && ($opts{'m'} =~ /[FfDdREeG]{1}/) ) {
600    $action = $opts{'m'};
601}
602elsif (defined ($opts{'create-cfgfile'})) {
603    $action = 'F';
604}
605elsif (defined ($opts{'print-cfgfile'})) {
606    $action = 'f';
607}
608elsif (defined ($opts{'create-datafile'})) {
609    $action = 'D';
610}
611elsif (defined ($opts{'print-datafile'})) {
612    $action = 'd';
613}
614elsif (defined ($opts{'remove-signature'})) {
615    $action = 'R';
616}
617elsif (defined ($opts{'sign'})) {
618    $action = 'E';
619}
620elsif (defined ($opts{'examine'})) {
621    $action = 'e';
622}
623elsif (defined ($opts{'generate-keys'})) {
624    $action = 'G';
625}
626else {
627    usage();
628    die ("No valid action specified !");
629}
630
631if (defined($ARGV[0])) {
632    $file1 = $ARGV[0];
633}
634if (defined($ARGV[1])) {
635    $file2 = $ARGV[1];
636}
637
638
639if (($action =~ /[REe]{1}/) && !defined($file1)) {
640    usage();
641    die("Option -m $action requires a filename (or '-' for stdio)\n");
642}
643
644if ($action =~ /^F$/) {
645    if (!defined($file1)) {
646        $file1 = $cfgfile;
647    }
648    $file2 = $cfgfile;
649    sign_file ();
650}
651
652if ($action =~ /^D$/) {
653    if (!defined($file1)) {
654        $file1 = $datafile;
655    }
656    $file2 = $datafile;
657    sign_file ();
658}
659
660if ($action =~ /^R$/) {
661    # $file1 defined
662    my $i = 0;
663    while (defined($ARGV[$i])) {
664        $file1 = $ARGV[$i];
665        remove ();
666        ++$i;
667    }
668}
669
670if ($action =~ /^E$/) {
671    # $file1 defined
672    # default: $file2 = $file1
673    check_gpg_sign();
674    my $i = 0;
675    while (defined($ARGV[$i])) {
676        $file1 = $ARGV[$i];
677        $file2 = $file1;
678        $return_from_sign = 1;
679        sign_file ();
680        ++$i;
681    }
682}
683
684if ($action =~ /^e$/) {
685    # $file1 defined
686    # default: $file2 = stdout
687    check_gpg_verify();
688    my $i = 0;
689    my $ret = 0;
690    while (defined($ARGV[$i])) {
691        print "\n";
692        $file1 = $ARGV[$i];
693        $ret += examine ();
694        ++$i;
695        print "\n--------------------------------\n" if (defined($ARGV[$i]));
696    }
697    exit($ret);
698}
699
700if ($action =~ /^f$/) {
701    $file1 = $cfgfile;
702    $file2 = "-";
703    print_cfgfile ();
704}
705
706if ($action =~ /^d$/) {
707    # $file1 irrelevant
708    if (defined($opts{'l'})) {
709        print_datafile ();
710    } else {
711        $file1 = $datafile;
712        $file2 = "-";
713        print_cfgfile ();
714    }
715}
716
717
718
Note: See TracBrowser for help on using the repository browser.