1#!/usr/local/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 gnupg_homedir --secretkeyring gnupg_homedir\n";
159    print "    Select an alternate gpg homedirectory to locate the secret keyring.\n";
160    print "    Will use '$ENV{'HOME'}/.gnupg/' by default.\n\n";
161
162    print "  -k keyid      --keyid keyid\n";
163    print "   Select the keyid to use for signing.\n\n";
164
165    print "  -l            --list\n";
166    print "    List the files in database rather than printing the raw file.\n\n";
167
168    print "  -v            --verbose\n";
169    print "    Verbose output.\n\n";
170    return;
171}
172
173sub check_gpg_uid () {
174    if (0 != $>) {
175	print "--------------------------------------------------\n";
176	print "\n";
177	print " You are not root. Please remember that samhain/yule\n";
178	print " will use the public keyring of root to verify a signature.\n";
179	print "\n";
180	print "--------------------------------------------------\n";
181    } else {
182	if (!("@yulectl_prg@" =~ //)) {
183	    print "--------------------------------------------------\n";
184	    print "\n";
185	    print " Please remember that yule will drop root after startup. Signature\n";
186	    print " verification on SIGHUP will fail if you do not import the public key\n";
187	    print " into the keyring of the non-root yule user.\n";
188	    print "\n";
189	    print "--------------------------------------------------\n";
190	}
191    }
192}
193
194sub check_gpg_sign () {
195    if ( defined($secretkeyring)) {
196        if ( (!-d "$secretkeyring")){
197            print "--------------------------------------------------\n";
198            print "\n";
199            print " Secret keyring $secretkeyring not found!\n";
200            print "\n";
201            print " Please check the path/name of the alternate secret keyring.\n";
202            print "\n";
203            print "--------------------------------------------------\n";
204            print "\n";
205            exit;
206        }
207    } else {
208        if ( (!-d "$ENV{'HOME'}/.gnupg") || (!-e "$ENV{'HOME'}/.gnupg/secring.gpg")) {
209	    print "--------------------------------------------------\n";
210	    print "\n";
211	    if (!-d "$ENV{'HOME'}/.gnupg") {
212	        print " Directory \$HOME/.gnupg not found!\n";
213	    } else {
214	        print " Secret keyring \$HOME/.gnupg/secring.gpg not found!\n";
215	    }
216	    print "\n";
217	    print " This indicates that you have never created a \n";
218	    print " public/private keypair, and thus cannot sign.\n";
219	    print " \n";
220	    print " Please use $0 --generate-keys or gpg --gen-key\n";
221	    print " to generate a public/private keypair first.\n";
222	    print "\n";
223	    print "--------------------------------------------------\n";
224	    print "\n";
225	    exit;
226        }
227    }
228}
229
230sub check_gpg_verify () {
231    if ( (!-d "$ENV{'HOME'}/.gnupg") || (!-e "$ENV{'HOME'}/.gnupg/pubring.gpg")) {
232	print "--------------------------------------------------\n";
233	print "\n";
234	if (!-d "$ENV{'HOME'}/.gnupg") {
235	    print " Directory \$HOME/.gnupg not found!\n";
236	} else {
237	    print " Public keyring \$HOME/.gnupg/pubring.gpg not found!\n";
238	}
239	print "\n";
240	print " This indicates that you have never used gpg before \n";
241	print " and/or have no public keys to verify signatures.\n";
242	print " \n";
243	print " Please use 'gpg --export key_id' to export the public\n";
244	print " signing key of the user who is signing the\n";
245	print " configuration/database files.\n\n";
246	print " Then you can use 'gpg --import keyfile' to import the key\n";
247	print " into this user's public keyring.\n";
248	print "\n";
249	print "--------------------------------------------------\n";
250	print "\n";
251	exit;
252    }
253}
254
255
256sub generate () {
257    my $command = "$gpg --homedir $ENV{'HOME'}/.gnupg --gen-key";
258    check_gpg_uid();
259    system ($command) == 0
260	or die "system $command failed: $?";
261    exit;
262}
263
264sub examine () {
265    my $iscfg = 0;
266    my $have_fp  = 0;
267    my $have_sig = 0;
268    my $message = '';
269    my $retval  = 9;
270    my $fh;
271    my $filename;
272
273    if (!($file1 =~ /^\-$/)) {
274	die ("Cannot open $file1 for read: $!") unless ((-e $file1) && (-r _));
275    }
276    open FIN,  "<$file1" or die "Cannot open $file1 for read: $!";
277
278    my $dir = tempdir( CLEANUP => 1 );
279    $filename = $dir . "/exa_jhfdbilw." . $$;
280    open $fh, ">$filename" or die "Cannot open $filename";
281    autoflush $fh 1;
282
283    while (<FIN>) {
284	print $fh $_;
285	if ($_ =~ /^\s*\[Misc\]/) {
286	    $iscfg = 1;
287	}
288    }
289    if ($iscfg == 1) {
290	$message .=  "File $file1 is a configuration file\n\n";
291    } else {
292	$message .=  "File $file1 is a database file\n\n";
293    }
294
295
296    my $command = "$gpg --homedir $ENV{'HOME'}/.gnupg --status-fd 1 ";
297    $command .= "--verbose " if (defined($opts{'v'}));
298    $command .= "--verify $filename ";
299    if (defined($opts{'v'})) {
300	$command .= "2>&1";
301    } else {
302	$command .= "2>/dev/null";
303    }
304
305    print STDOUT "Using: $command\n\n" if (defined($opts{'v'}));
306    open  GPGIN, "$command |" or die "Cannot fork: $!";
307
308    while (<GPGIN>) {
309	if ($_ =~ /^\[GNUPG:\] GOODSIG ([0-9A-F]+) (.*)$/) {
310	    $message .= "GOOD signature with key: $1\n";
311	    $message .= "Key owner:               $2\n";
312	    $have_sig = 1;
313	    $retval   = 0;
314	}
315	if ($_ =~ /^\[GNUPG:\] VALIDSIG ([0-9A-F]+) ([0-9\-]+)\s/) {
316	    $message .= "Key fingerprint:         $1\n";
317	    $message .= "Signature generated on:  $2\n\n";
318	    $have_fp = 1;
319	    $message .=  "This file is signed with a valid signature.\n"
320		if ($have_sig == 1);
321	    $have_sig = 1;
322	    $have_fp = 1;
323	}
324	if ($_ =~ /^\[GNUPG:\] NODATA 1/) {
325	    $message .=  "NO signature found.\n\n";
326	    $message .=  "This file is not signed !!!\n";
327	    $have_sig = 1;
328	    $have_fp = 1;
329	    $retval  = 2;
330	}
331	if ($_ =~ /^\[GNUPG:\] BADSIG ([0-9A-F]+) (.*)$/) {
332	    $message .=  "BAD signature with key: $1\n";
333	    $message .=  "Key owner:              $2\n\n";
334	    $message .=  "This file is signed with an invalid signature !!!\n";
335	    $have_sig = 1;
336	    $have_fp = 1;
337	    $retval = 1;
338	}
339	if ($_ =~ /^\[GNUPG:\] NO_PUBKEY ([0-9A-F]+)/) {
340	    $message .=  "NOT CHECKED signature with key: $1\n\n";
341	    $message .=  "The signature of this file cannot be checked: no public key available !!!\n";
342	    $have_sig = 1;
343	    $have_fp = 1;
344	    $retval  = 1;
345	}
346	print STDOUT $_ if (defined($opts{'v'}));
347    }
348    close (GPGIN);
349    print STDOUT "\n" if (defined($opts{'v'}));
350    if ($have_sig == 0) {
351	$message .=  "NO valid signature found\n";
352    }
353    elsif ($have_fp == 0) {
354	$message .=  "NO fingerprint found\n";
355    }
356    close (FIN);
357    if ($no_print_examine == 0) {
358	print STDOUT $message;
359    }
360    unlink0( $fh, $filename ) or die "Cannot unlink $filename safely";
361    return $retval;
362}
363
364sub remove () {
365    my $bodystart = 1;
366    my $sigstart  = 0;
367    my $sigend    = 0;
368    my $filename  = "";
369    my $fh;
370    my $stats;
371
372    open FH, "<$file1" or die "Cannot open file $file1 for read: $!";
373    if (!($file1 =~ /^\-$/)) {
374	flock(FH, LOCK_EX) unless ($no_remove_lock == 1);
375	my $dir = tempdir( CLEANUP => 1 ) or die "Tempdir failed";
376	$filename = $dir . "/rem_iqegBCQb." . $$;
377	open $fh, ">$filename" or die "Cannot open $filename";
378	$stats = stat($file1);
379	# ($fh, $filename) = tempfile(UNLINK => 1);
380    } else {
381	open $fh, ">$file1" or die "Cannot open file $file1 for write: $!";
382    }
383    autoflush $fh 1;
384    while (<FH>) {
385	if ($_ =~ /^-----BEGIN PGP SIGNED MESSAGE-----/) {
386	    $sigstart = 1;
387	    $bodystart = 0;
388	    next;
389	} elsif (($sigstart == 1) && ($_ =~ /^\s+$/)) {
390	    $sigstart = 0;
391	    $bodystart = 1;
392	    next;
393	} elsif ($_ =~ /^-----BEGIN PGP SIGNATURE-----/) {
394	    $bodystart = 0;
395	    $sigend = 1;
396	    next;
397	} elsif (($sigend == 1) && ($_ =~ /^-----END PGP SIGNATURE-----/)) {
398	    $sigend = 0;
399	    $bodystart = 1;
400	    next;
401	}
402	if ($bodystart == 1) {
403	    print $fh $_;
404	}
405    }
406    if (!($file1 =~ /^\-$/)) {
407	copy("$filename", "$file1")
408	    or die "Copy $filename to $file1 failed: $!";
409	chmod $stats->mode, $file1;
410	chown $stats->uid, $stats->gid, $file1;
411	flock(FH, LOCK_UN) unless ($no_remove_lock == 1);
412	close FH;
413    }
414    unlink0( $fh, $filename ) or die "Cannot unlink $filename safely";
415    return;
416}
417
418sub print_cfgfile () {
419    my $bodystart = 0;
420    my $sigstart  = 0;
421
422    if (!defined($file2)) {
423	$file2 = '-';
424    }
425
426    open FH, "<$file1" or die "Cannot open file $file1 for read: $!";
427    open FO, ">$file2" or die "Cannot open file $file2 for write: $!";
428    while (<FH>) {
429	if ($_ =~ /^-----BEGIN PGP SIGNED MESSAGE-----/) {
430	    $sigstart = 1;
431	    next;
432	} elsif (($sigstart == 1) && ($_ =~ /^\s+$/)) {
433	    $sigstart = 0;
434	    $bodystart = 1;
435	    next;
436	} elsif ($_ =~ /^-----BEGIN PGP SIGNATURE-----/) {
437	    $bodystart = 0;
438	    exit;
439	}
440	if ($bodystart == 1) {
441	    print FO $_;
442	}
443    }
444    exit;
445}
446sub print_datafile () {
447    die ("Cannot find program $daemon")
448	unless (-e $daemon);
449    if (defined($opts{'v'})) {
450	open FH, "$daemon --full-detail -d $datafile |"
451	    or die "Cannot open datafile $datafile for read: $!";
452    } else {
453	open FH, "$daemon -d $datafile |"
454	    or die "Cannot open datafile $datafile for read: $!";
455    }
456    while (<FH>) {
457	print $_;
458    }
459    exit;
460}
461
462sub sign_file () {
463
464    my $fileout = '';
465    my $bodystart = 1;
466    my $sigstart  = 0;
467    my $sigend    = 0;
468    my $stats;
469    my $fh1;
470    my $filename1;
471    my $flag1     = 0;
472
473    check_gpg_uid();
474    check_gpg_agent();
475
476    if (!defined($file2)) {
477	$file2 = $file1;
478    }
479
480    if ($file1 =~ /^\-$/) {
481	my $dir = tempdir( CLEANUP => 1 ) or die "Tempdir failed";
482	$filename1 = $dir . "/sig_vs8827sd." . $$;
483	open $fh1, ">$filename1" or die "Cannot open $filename1";
484	$flag1 = 1;
485	# my ($fh1, $filename1) = tempfile(UNLINK => 1);
486
487	while (<STDIN>) {
488	  if ($_ =~ /^-----BEGIN PGP SIGNED MESSAGE-----/) {
489	    $sigstart = 1;
490	    $bodystart = 0;
491	    next;
492	  } elsif (($sigstart == 1) && ($_ =~ /^\s+$/)) {
493	    $sigstart = 0;
494	    $bodystart = 1;
495	    next;
496	  } elsif ($_ =~ /^-----BEGIN PGP SIGNATURE-----/) {
497	    $bodystart = 0;
498	    $sigend = 1;
499	    next;
500	  } elsif (($sigend == 1) && ($_ =~ /^-----END PGP SIGNATURE-----/)) {
501	    $sigend = 0;
502	    $bodystart = 1;
503	    next;
504	  }
505	  if ($bodystart == 1) {
506	    print $fh1 $_;
507	  }
508	  #
509	  # print $fh1 $_;
510	  #
511	}
512	$file1 = $filename1;
513	$fileout = '-';
514    } else {
515	open (LOCKFILE, "<$file1") or die "Cannot open $file1: $!";
516	flock(LOCKFILE, LOCK_EX);
517	$no_print_examine = 1;
518	$no_remove_lock   = 1;
519	if (examine() < 2) {
520	    remove();
521	}
522	$fileout = $file1 . ".asc";
523	$stats   = stat($file1)
524	    or die "No file $file1: $!";
525    }
526
527    if (defined($passphrase)) {
528	local $SIG{PIPE} = 'IGNORE';
529	my $command = "$gpg --homedir $ENV{'HOME'}/.gnupg --passphrase-fd 0 -a ${KEYTAG} ${TARGETKEYID} --clearsign -o $fileout --not-dash-escaped ";
530        $command .= "--secret-keyring $secretkeyring " if (defined($opts{'s'}));
531	$command .= "$file1";
532	open (FH, "|$command")  or die "can't fork: $!";
533	print FH "$passphrase"  or die "can't write: $!";
534	close FH                or die "can't close: status=$?";
535    } else {
536	my $command = "$gpg --homedir $ENV{'HOME'}/.gnupg                   -a ${KEYTAG} ${TARGETKEYID} --clearsign -o $fileout --not-dash-escaped ";
537        $command .= "--secret-keyring $secretkeyring " if (defined($opts{'s'}));
538	$command .= "$file1";
539	system("$command") == 0
540	    or die "system $command failed: $?";
541    }
542
543    if (!($fileout =~ /^\-$/)) {
544	my $st_old = stat($file1)
545	    or die "No file $file1: $!";
546	my $st_new = stat($fileout)
547	    or die "No file $fileout: $!";
548	die ("Signed file is smaller than unsigned file")
549	    unless ($st_new->size > $st_old->size);
550	move("$fileout", "$file2")
551	    or die "Move $fileout to $file2 failed: $!";
552	chmod $stats->mode, $file2;
553	chown $stats->uid, $stats->gid, $file2;
554	flock(LOCKFILE, LOCK_UN);
555    }
556
557    if ($flag1 == 1) {
558	unlink0( $fh1, $filename1 ) or die "Cannot unlink $filename1 safely";
559    }
560    if ($return_from_sign == 1) {
561	return;
562    }
563    exit;
564}
565
566Getopt::Long::Configure ("posix_default");
567Getopt::Long::Configure ("bundling");
568# Getopt::Long::Configure ("debug");
569
570GetOptions (\%opts, 'm=s', 'h|help', 'v|verbose', 'l|list',
571	    'c|cfgfile=s',
572	    'd|datafile=s',
573	    'p|passphrase=s',
574	    's|secretkeyring=s',
575            'k|keyid=s',
576	    'create-cfgfile',  # -m F
577	    'print-cfgfile',   # -m f
578	    'create-datafile', # -m D
579	    'print-datafile',  # -m d
580	    'remove-signature',# -m R
581	    'sign',            # -m E
582	    'examine',         # -m e
583	    'generate-keys');  # -m G
584
585if (defined ($opts{'h'})) {
586    usage();
587    exit;
588}
589
590if (defined($opts{'k'})) {
591    $TARGETKEYID = $opts{'k'};
592    $KEYTAG = "--default-key";
593}
594if (defined($opts{'c'})) {
595    $cfgfile = $opts{'c'};
596}
597if (defined($opts{'d'})) {
598    $datafile = $opts{'d'};
599}
600if (defined($opts{'p'})) {
601    $passphrase = $opts{'p'};
602}
603if (defined($opts{'s'})) {
604    $secretkeyring = $opts{'s'};
605}
606
607if (defined ($opts{'m'}) && ($opts{'m'} =~ /[FfDdREeG]{1}/) ) {
608    $action = $opts{'m'};
609}
610elsif (defined ($opts{'create-cfgfile'})) {
611    $action = 'F';
612}
613elsif (defined ($opts{'print-cfgfile'})) {
614    $action = 'f';
615}
616elsif (defined ($opts{'create-datafile'})) {
617    $action = 'D';
618}
619elsif (defined ($opts{'print-datafile'})) {
620    $action = 'd';
621}
622elsif (defined ($opts{'remove-signature'})) {
623    $action = 'R';
624}
625elsif (defined ($opts{'sign'})) {
626    $action = 'E';
627}
628elsif (defined ($opts{'examine'})) {
629    $action = 'e';
630}
631elsif (defined ($opts{'generate-keys'})) {
632    $action = 'G';
633}
634else {
635    usage();
636    die ("No valid action specified !");
637}
638
639if (defined($ARGV[0])) {
640    $file1 = $ARGV[0];
641}
642if (defined($ARGV[1])) {
643    $file2 = $ARGV[1];
644}
645
646
647if (($action =~ /[REe]{1}/) && !defined($file1)) {
648    usage();
649    die("Option -m $action requires a filename (or '-' for stdio)\n");
650}
651
652if ($action =~ /^F$/) {
653    if (!defined($file1)) {
654	$file1 = $cfgfile;
655    }
656    $file2 = $cfgfile;
657    sign_file ();
658}
659
660if ($action =~ /^D$/) {
661    if (!defined($file1)) {
662	$file1 = $datafile;
663    }
664    $file2 = $datafile;
665    sign_file ();
666}
667
668if ($action =~ /^R$/) {
669    # $file1 defined
670    my $i = 0;
671    while (defined($ARGV[$i])) {
672	$file1 = $ARGV[$i];
673	remove ();
674	++$i;
675    }
676}
677
678if ($action =~ /^E$/) {
679    # $file1 defined
680    # default: $file2 = $file1
681    check_gpg_sign();
682    my $i = 0;
683    while (defined($ARGV[$i])) {
684	$file1 = $ARGV[$i];
685	$file2 = $file1;
686	$return_from_sign = 1;
687	sign_file ();
688	++$i;
689    }
690}
691
692if ($action =~ /^e$/) {
693    # $file1 defined
694    # default: $file2 = stdout
695    check_gpg_verify();
696    my $i = 0;
697    my $ret = 0;
698    while (defined($ARGV[$i])) {
699	print "\n";
700	$file1 = $ARGV[$i];
701	$ret += examine ();
702	++$i;
703	print "\n--------------------------------\n" if (defined($ARGV[$i]));
704    }
705    exit($ret);
706}
707
708if ($action =~ /^f$/) {
709    $file1 = $cfgfile;
710    $file2 = "-";
711    print_cfgfile ();
712}
713
714if ($action =~ /^d$/) {
715    # $file1 irrelevant
716    if (defined($opts{'l'})) {
717	print_datafile ();
718    } else {
719	$file1 = $datafile;
720	$file2 = "-";
721	print_cfgfile ();
722    }
723}
724
725
726
727