source: trunk/scripts/yuleadmin.pl.in@ 539

Last change on this file since 539 was 121, checked in by katerina, 17 years ago

Import yuleadmin.pl.in (contributed by Riccardo Murri).

  • Property svn:executable set to *
File size: 7.9 KB
Line 
1#! /usr/bin/perl
2
3# Copyright (c) 2007 Riccardo Murri <riccardo.murri@gmail.com>
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::Temp qw/ tempfile /;
27use IO::File;
28
29# Do I/O to the data file in binary mode (so it
30# wouldn't complain about invalid UTF-8 characters).
31use bytes;
32
33File::Temp->safe_level( File::Temp::HIGH );
34
35my %opts = ();
36my $outfile;
37my $verbose;
38my $base = basename($0);
39
40#my $cfgfile = "yulerc";
41#my $yule = "./yule";
42#my $gpg = "/usr/bin/gpg";
43
44my $cfgfile = "@myconffile@";
45my $yule = "@sbindir@/@install_name@";
46my $gpg = "@mygpg@";
47
48$cfgfile =~ s/^REQ_FROM_SERVER//;
49
50$gpg = "gpg" if ($gpg eq "");
51
52sub usage() {
53 print <<__END_OF_TEXT__
54Usage:
55 $base { -a | --add } [options] HOSTNAME [PASSWORD]
56 Add client HOSTNAME to configuration file. If PASSWORD is
57 omitted, it is read from stdin. If HOSTNAME already exists
58 in the configuration file, an error is given.
59
60 $base { -d | --delete } [options] HOSTNAME
61 Remove client HOSTNAME from configuration file.
62
63 $base { -l | --list } [options]
64 List clients in the yule configuration file.
65
66 $base { -r | --replace } [options] HOSTNAME [PASSWORD]
67 Replace password of existing client HOSTNAME in configuration file.
68 If PASSWORD is omitted, it is read from stdin. If HOSTNAME does not
69 already exist in the configuration file, an error is given.
70
71 $base { -u | --update } [options] HOSTNAME [PASSWORD]
72 Add client HOSTNAME to config file or replace its password with a new one.
73 If PASSWORD is omitted, it is read from stdin.
74
75Options:
76 -c CFGFILE --cfgfile CFGFILE
77 Select an alternate configuration file. (default: $cfgfile)
78
79 -o OUTFILE --output OUTFILE
80 Write modified configuration to OUTFILE. If this option is
81 omitted, $base will rename the original configuration file
82 to '$cfgfile.BAK' and overwrite it with the modified content.
83
84 -Y YULECMD --yule YULECMD
85 Use command YULECMD to generate the client key from the password.
86 (default: $yule)
87
88 -v --verbose
89 Verbose output.
90
91__END_OF_TEXT__
92;
93 return;
94}
95
96
97## subroutines
98
99sub read_clients ($) {
100 my $cfgfile = shift || '-';
101 my %clients;
102
103 open INPUT, "<$cfgfile"
104 or die ("Cannot read configuration file '$cfgfile'. Aborting");
105
106 my $section;
107 while (<INPUT>) {
108 # skip comment and blank lines
109 next if m{^\s*#};
110 next if m{^\s*$};
111
112 # match section headers
113 $section = $1 if m{^\s*\[([a-z0-9 ]+)\]}i;
114
115 # ok, list matching lines
116 if ($section =~ m/Clients/) {
117 if (m{^\s*Client=}i) {
118 chomp;
119 s{^\s*Client=}{}i;
120 my ($client, $key) = split /@/,$_,2;
121
122 $clients{lc($client)} = $key;
123 }
124 }
125 }
126
127 close INPUT;
128 return \%clients;
129}
130
131
132sub write_clients ($$$) {
133 my $cfgfile_in = shift || '-';
134 my $cfgfile_out = shift || $cfgfile_in;
135 my $clients = shift;
136
137 my @lines;
138 my $in_clients_section;
139
140 # copy-pass input file
141 my $section = '';
142 open INPUT, "<$cfgfile_in"
143 or die ("Cannot read configuration file '$cfgfile_in'. Aborting");
144 while (<INPUT>) {
145 # match section headers
146 if (m{^\s*\[([a-z0-9 ]+)\]}i) {
147 if ($in_clients_section and ($section ne $1)) {
148 # exiting [Clients] section, output remaining ones
149 foreach my $hostname (keys %{$clients}) {
150 push @lines,
151 'Client=' . $hostname . '@'
152 . $clients->{lc($hostname)} . "\n";
153 delete $clients->{lc($hostname)};
154 }
155 }
156 # update section title
157 $section = $1;
158 if ($section =~ m/Clients/i) {
159 $in_clients_section = 1;
160 } else {
161 $in_clients_section = 0;
162 }
163 }
164
165 # process entries in [Clients] section
166 if ($in_clients_section) {
167 if (m{^\s*Client=}i) {
168 my ($hostname, undef) = split /@/,$_,2;
169 $hostname =~ s{^\s*Client=}{}i;
170 if (defined($clients->{lc($hostname)})) {
171 # output (possibly) modified key
172 $_ = 'Client=' . $hostname . '@' . $clients->{lc($hostname)} . "\n";
173 delete $clients->{lc($hostname)};
174 }
175 else {
176 # client deleted, skip this line from output
177 $_ = '';
178 }
179 }
180 }
181
182 # copy input to output
183 push @lines, $_;
184 }
185 close INPUT;
186
187 # if end-of-file reached within [Clients] section, output remaining ones
188 if ($in_clients_section) {
189 foreach my $hostname (keys %{$clients}) {
190 push @lines, 'Client=' . $hostname . '@'
191 . $clients->{lc($hostname)} . "\n";
192 }
193 }
194
195 # if necessary, replace input file with output file
196 if ($cfgfile_in eq $cfgfile_out) {
197 copy($cfgfile_in, $cfgfile_in . '.BAK')
198 or die("Cannot backup config file '$cfgfile_in'. Aborting");
199 }
200 open OUTPUT, ">$cfgfile_out"
201 or die ("Cannot write to file '$cfgfile_out'. Aborting");
202 # overwrite config file line by line
203 foreach my $line (@lines) { print OUTPUT $line; }
204 close OUTPUT;
205}
206
207
208sub new_client_key ($) {
209 my $password = shift;
210 my $yulecmd = shift || $yule;
211
212 my (undef, $key) = split /@/, `$yulecmd -P $password`, 2;
213 chomp $key;
214 return $key;
215}
216
217
218## main
219
220Getopt::Long::Configure ("posix_default");
221Getopt::Long::Configure ("bundling");
222# Getopt::Long::Configure ("debug");
223
224GetOptions (\%opts,
225 'Y|yule=s',
226 'a|add',
227 'c|cfgfile=s',
228 'd|delete',
229 'h|help',
230 'l|list',
231 'o|output=s',
232 'r|replace',
233 'u|update',
234 'v|verbose',
235 );
236
237if (defined ($opts{'h'})) {
238 usage();
239 exit;
240}
241
242if (defined($opts{'c'})) {
243 $cfgfile = $opts{'c'};
244 $outfile = $cfgfile unless defined($outfile);
245}
246if (defined($opts{'Y'})) {
247 $yule = $opts{'Y'};
248}
249if (defined($opts{'v'})) {
250 $verbose = 1;
251}
252if (defined($opts{'o'})) {
253 $outfile = $opts{'o'};
254}
255
256if (defined($opts{'l'})) {
257 # list contents
258 my $clients = read_clients($cfgfile);
259
260 foreach my $client (keys %{$clients}) {
261 print "$client";
262 print " ${$clients}{$client}" if $verbose;
263 print "\n";
264 }
265}
266elsif (defined($opts{'a'})
267 or defined($opts{'u'})
268 or defined($opts{'r'})) {
269 # add HOSTNAME
270 my $hostname = $ARGV[0]
271 or die("Actions --add/--replace/--update require at least argument HOSTNAME. Aborting");
272
273 my $password;
274 if (defined($ARGV[1])) {
275 $password = uc($ARGV[1]);
276 } else {
277 $password = uc(<STDIN>);
278 # remove leading and trailing space
279 $password =~ s{\s*}{}g;
280 }
281 # sanity check
282 die ("Argument PASSWORD must be a 16-digit hexadecimal string. Aborting")
283 unless ($password =~ m/[[:xdigit:]]{16}/);
284
285 my $add = defined($opts{'a'});
286 my $replace = defined($opts{'r'});
287
288 my $clients = read_clients($cfgfile);
289 die ("Client '$hostname' already present in config file - cannot add. Aborting")
290 if ($add and defined(${$clients}{$hostname}));
291 die ("Client '$hostname' not already present in config file - cannot replace. Aborting")
292 if ($replace and not defined(${$clients}{$hostname}));
293
294 $clients->{$hostname} = new_client_key($password)
295 or die ("Cannot get key for the given password. Aborting");
296 write_clients($cfgfile, $outfile, $clients);
297}
298elsif (defined($opts{'d'})) {
299 # remove HOSTNAME
300 my $hostname = $ARGV[0]
301 or die("Action --delete requires one argument HOSTNAME. Aborting");
302
303 my $clients = read_clients($cfgfile);
304 delete ${$clients}{$hostname};
305 write_clients($cfgfile, $outfile, $clients);
306}
307else {
308 usage();
309 die ("You must specify one of --list, --add or --remove options. Aborting");
310}
Note: See TracBrowser for help on using the repository browser.