Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / dspam / dspam_ @ 8589c6df

Historique | Voir | Annoter | Télécharger (20,2 ko)

1
#!/bin/sh
2
# -*- sh -*-
3

    
4
: << =cut
5

    
6
=head1 NAME
7

    
8
dspam_ - Plugin to monitor various aspects of DSPAM performance
9

    
10
=head1 APPLICABLE SYSTEMS
11

    
12
Any system running a recent (3.8.0 or higher) DSPAM install.
13

    
14
=head1 CONFIGURATION
15

    
16
The plugin uses the output of the dspam_stats command, which is usually part
17
of any DSPAM install. You'll need to run this plugin as a user that has enough
18
rights to run dspam_stats and generate data for all users. This means that the
19
plugin needs to be run either as root, or as a user that has read access to
20
dspam.conf, and is added as a Trusted user in dspam.conf.
21

    
22
The following environment variables are used by this plugin:
23

    
24
 dspam_stats - Where to find the dspam_stats binary when it's not in
25
               $PATH (default: find anywhere in $PATH).
26
 statefile   - Where to read/write the statefile that is used to store
27
               dspam_stats output
28
               (default: $MUNIN_PLUGSTATE/dspam.state).
29
 warning     - When to trigger a warning (default: 95:).
30
 critical    - When to trigger a critical (default: 90:).
31
 pattern     - A pattern that is passed to grep in order to find the
32
               DSPAM uids to display. When this variable is set, the
33
               value of target (see USAGE) is ignored (default: empty).
34
 description - A string describing the set of uids selected by
35
               above pattern (default: empty).
36

    
37
Warning and critical values can also set on a DSPAM uid basis, use Munins
38
internal format for the DSPAM uid for this notation (see CONFIGURATION
39
EXAMPLES and USAGE for details).
40

    
41
=head2 CONFIGURATION EXAMPLES
42

    
43
 [dspam*]
44
 user root
45
 env.dspam_stats /opt/dspam/bin/dspam_stats
46
 env.statefile /tmp/dspam.state
47

    
48
 [dspam_accuracy*]
49
 env.critical 95:
50
 env.warning 96:
51

    
52
 # raise warning level for username@example.org
53
 env.username_example_org_warning 97:
54

    
55
 # show all accounts from one domain
56
 env.pattern @example\.org
57
 env.description domain example.org
58

    
59
=head1 USAGE
60

    
61
Link this plugin to /etc/munin/plugins/ and restart the munin-node. The link
62
should be in the format: dspam_<graph>_<target>, where:
63

    
64
 graph      - One of: accuracy, processed, processed_abs.
65
 target     - The uid that DSPAM generates in dspam_stats output,
66
              but converted to Munin internal name format. Normally
67
              this means that non-alphabetic and non-numeral characters
68
              are replaced by an underscore. For example,
69
              username@example.org will become username_example_org.
70
              A special case is uid ALL, which will draw a graph for
71
              a total of all uids, or for a list of all uids (depending
72
              on the graph type).
73
              NB For advanced uid selection such as 'all users of domain
74
              example.org', please see the environment variable 'pattern' 
75
              under CONFIGURATION.
76

    
77
=head1 INTERPRETATION
78

    
79
The plugin supports the following graph types:
80

    
81
 accuracy     - Shows the overall accuracy of all users as a
82
                percentage. The overall accuracy is the number of
83
                correctly classified messages (both ham and spam) in
84
                relation to the number of all processed messages.
85

    
86
 absprocessed - Shows the absolute numbers of messages processed,
87
                sorted by the classification that DSPAM uses. The
88
                numbers are stacked, making the height of the column
89
                display the increase of processed messages over time.
90

    
91
 processed    - Shows the same data as dspam_processed_abs_, but as
92
                percentage of the total amount of processed messages,
93
                making it clear to see how the amounts of classified
94
                messages are divided.
95

    
96
=head1 AUTHOR
97

    
98
Copyright 2010 Tom Hendrikx <tom@whyscream.net>
99

    
100
=head1 LICENSE
101

    
102
GPLv2
103

    
104
This program is free software; you can redistribute it and/or modify
105
it under the terms of the GNU General Public License as published by
106
the Free Software Foundation; version 2 dated June, 1991.
107

    
108
This program is distributed in the hope that it will be useful, but
109
WITHOUT ANY WARRANTY; without even the implied warranty of
110
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
111
General Public License for more details.
112

    
113
You should have received a copy of the GNU General Public License
114
along with this program; if not, write to the Free Software
115
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
116
02110-1301 USA.
117

    
118
=head1 BUGS
119

    
120
None known. Please report to author when you think you found something.
121

    
122
=head2 TODO LIST
123

    
124
Currently developed and tested with bash/dash on linux.
125
More testing might be needed with other shells and OSes.
126

    
127
=head1 VERSION
128

    
129
$Id: dspam_ 72 2010-09-15 22:09:15Z tomhendr $
130

    
131
=head1 MAGIC MARKERS
132

    
133
 #%# family=auto
134
 #%# capabilities=autoconf suggest
135

    
136
=cut
137

    
138
# defaults for configurable settings
139
: ${dspam_stats:=$(which dspam_stats 2> /dev/null)}
140
: ${statefile:=${MUNIN_PLUGSTATE}/dspam.state}
141
: ${statefile_max_age:=180} # 3 minutes
142
: ${warning:=95:}
143
: ${critical:=90:}
144

    
145
# include munin plugin helper
146
. $MUNIN_LIBDIR/plugins/plugin.sh
147

    
148
#######################################
149
# Some generic file locking functions #
150
#######################################
151

    
152
#
153
# file_is_usable $file $max_age
154
#	Check if file is OK for usage: existing, readable and not too old
155
#
156
file_is_usable() {
157
	local file=$1
158
	local max_age=$2
159
	local lock=$file.lock
160

    
161
	[ ! -f $file ] && debug statefile $file does not exist && return 66 # EX_NOINPUT
162
	[ ! -r $file ] && debug statefile $file is not readable && return 65 # EX_DATAERR
163

    
164
	local mtime=$(stat --format %Y $file)
165
	local file_age=$(( $(date +%s) -mtime ))
166
	[ $file_age -gt $max_age ] && debug file $file is too old: $file_age seconds && return 65 # EX_DATAERR
167

    
168
	debug file $file is ok, $file_age seconds old
169
	return 0 # EX_OK
170
}
171

    
172
#
173
# file_get_lock $file
174
#	Obtain a lock for the named file
175
#
176
file_get_lock() {
177
	local file=$1
178
	local lock=$file.lock
179

    
180
	while file_is_locked $file; do
181
		sleep 1
182
	done
183

    
184
	echo $$ > $lock
185
	[ $? -gt 0 ] && debug failed to create lockfile $lock && return 73 # EX_CANTCREAT
186

    
187
	debug created lock for $file
188
	return 0 # EX_OK
189
}
190

    
191
#
192
# file_remove_lock $file
193
#	Remove a set lockfile for the named file
194
#
195
file_remove_lock() {
196
	local file=$1
197
	local lock=$file.lock
198

    
199
	rm -f $lock
200
	[ $? -gt 0 ] && debug failed to remove lockfile $lock && return 69 # EX_UNAVAILABLE
201

    
202
	debug removed lock for file $file
203
	return 0 # EX_OK
204
}
205

    
206
#
207
# file_is_locked $file
208
#	Check if a file is locked
209
#
210
file_is_locked() {
211
	local file=$1
212
	local lock=$1.lock
213

    
214
	[ -f $lock ] && debug file $file is locked && return 0 # EX_OK
215
	debug file $file is not locked
216
	return 69 # EX_UNAVAILABLE
217
}
218

    
219
####################################
220
# DSPAM output processing function #
221
####################################
222

    
223
#
224
# update_statefile
225
#	Read the output of dspam_stats, convert it and write usable data to the statefile
226
#
227
update_statefile() {
228

    
229
	file_is_usable $statefile $statefile_max_age && return $? # return when all OK
230
	! file_get_lock $statefile && return $? # return when locking failed
231

    
232
	local tmpfile=$(mktemp)
233
	debug created tmpfile $tmpfile
234

    
235
	debug starting $dspam_stats -t -S
236
	local t_start=$(date +%s)
237
	$dspam_stats -t -S | while read a b c d e f g h i j k l m x; do
238

    
239
		# example of output format (3.9.1 rc1) for each user:
240
		#username@example.org
241
		#    TP:     0 TN:  2147 FP:     0 FN:    53 SC:     0 NC:     0
242
		#    SHR:    0.00%       HSR:    0.00%       OCA:   97.59%
243

    
244
		# or for short user names:
245
		#vmail    TP:  1141 TN:   459 FP:     0 FN:     5 SC:     0 NC:     0
246
		#         SHR:   99.56%       HSR:    0.00%       OCA:   99.69%
247

    
248
		case $a in
249
			TP:)
250
				# the 2nd line
251
				local tp=$b tn=$d fp=$f fn=$h sc=$j nc=$l
252
				;;
253
			SHR:)
254
				# the 3rd line
255
				local shr=$(echo $b | sed 's/%$//g')
256
				local hsr=$(echo $d | sed 's/%$//g')
257
				local oca=$(echo $f | sed 's/%$//g')
258

    
259
				# we're done, write data from current user to the statefile
260
				local clean_user=$(clean_fieldname $uid)
261
				local state="$uid $clean_user $tp $tn $fp $fn $sc $nc $shr $hsr $oca"
262
				echo $state >> $tmpfile
263
				[ $? -gt 0 ] && debug failed to write data for $uid to tmpfile && return 73 # EX_CANTCREAT
264
				debug wrote data for $uid to tmpfile: $state
265
				;;
266
			*)
267
				# the 1st line
268
				local uid=$a
269
				# data from 2nd line is also here
270
                                [ "$b" = "TP:" ] && local tp=$c tn=$e fp=$g fn=$i sc=$k nc=$m
271
				;;
272
		esac
273
	done
274
	local t_end=$(date +%s)
275
	debug dspam_stats finished, runtime $((t_end - t_start)) seconds
276

    
277
	mv $tmpfile $statefile
278
	[ $? -gt 0 ] && debug failed to move tmpfile to $statefile && return 73 # EX_CANTCREAT
279
	debug moved tmpfile to $statefile
280

    
281
	file_remove_lock $statefile
282

    
283
	return 0 # EX_OK
284
}
285

    
286

    
287
#
288
# abs2perc
289
#	Outputs a percentage calculated from its two arguments
290
#
291
abs2perc() {
292
	# division by zero protection: debug output is merely informal and harmless ;)
293
	[ $1 -eq 0 ] && echo 0 && debug abs2perc prevented possible division-by-zero on first argument && return 0 # EX_OK
294
	[ $2 -eq 0 ] && echo 0 && debug abs2perc prevented possible division-by-zero on second argument && return 0 # EX_OK
295

    
296
	echo $1 $2 | awk '{ print $1 * 100 / $2 }'
297
}
298

    
299
#
300
# debug $output
301
#	Prints debugging output when munin-run is called with --pidebug argument (i.e. when MUNIN_DEBUG is set)
302
#
303
debug() {
304
	if [ -n "$MUNIN_DEBUG" ]; then
305
		echo "# DEBUG: $@"
306
	fi
307
}
308

    
309
########################################
310
# Functions that generate munin output #
311
########################################
312

    
313
#
314
# print_autoconf
315
#	Output for 'munin-node-configure' autoconf functionality
316
#
317
print_autoconf() {
318
	if [ -z "$dspam_stats" ]; then
319
		echo "no (no dspam_stats binary found)"
320
	elif [ ! -x $dspam_stats ]; then
321
		echo "no ($dspam_stats found but not executable)"
322
        else
323
                echo yes
324
        fi
325
}
326

    
327
#
328
# print_suggest
329
#	Output for 'munin-node-configure --suggest'
330
#
331
print_suggest() {
332
	echo accuracy_ALL
333
	echo processed_ALL
334
	echo absprocessed_ALL
335
}
336

    
337
#
338
# print_config
339
#	Output for 'munin-run <plugin> config' command.
340
#
341
print_config() {
342
	debug printing config for graph: $graph
343

    
344
	while file_is_locked $statefile; do
345
		debug statefile is locked, waiting
346
		sleep 1
347
	done
348

    
349
	case $graph in
350
		accuracy)
351
			if [ -n "$pattern" ]; then
352
				debug env.pattern was set, so use it: $pattern
353
				local uid=$description
354
				local uid_count=$(grep $pattern $statefile | wc -l)
355
				debug uid_count retrieved from statefile: $uid_count
356
			elif [ "$target" = "ALL" ]; then
357
				local pattern="-v TOTAL"
358
				debug target=ALL: need pattern for all users but not TOTAL: $pattern
359
				local uid="all users"
360
				local uid_count=$(grep $pattern $statefile | wc -l)
361
				debug uid_count retrieved from statefile: $uid_count
362
			else
363
				local pattern="\b$target\b"
364
				debug target=$target: need pattern for a single user: $pattern
365
				local uid=$(grep $pattern $statefile | cut -d' ' -f1)
366
				debug retrieved uid value from statefile: $uid
367
				local uid_count=1
368
			fi
369

    
370
			echo "graph_title Accuracy for $uid"
371
			echo graph_category spamfilter
372
			echo graph_args --base 1000 --upper-limit 100 --rigid
373
			echo graph_vlabel Accuracy in %
374
			echo "graph_info This graph shows the current DSPAM Overall Accuracy for $uid ($uid_count uids). Overall Accuracy is the percentage of messages that is classified correctly as either ham or spam."
375

    
376
			debug starting grep for user data in statefile
377
			local t_start=$(date +%s)
378
			grep $pattern $statefile | while read uid clean_user x; do
379
					echo $clean_user.label $uid
380
					print_warning $clean_user
381
					print_critical $clean_user
382
			done
383
			local t_end=$(date +%s)
384
			debug grep finished, runtime $((t_end - t_start)) seconds
385

    
386
			;;
387

    
388
		processed)
389
			if [ -n "$pattern" ]; then
390
				debug env.pattern was set, so use it: $pattern
391
				local uid=$description
392
				local uid_count=$(grep $pattern $statefile | wc -l)
393
				debug uid_count retrieved from statefile: $uid_count
394
			elif [ "$target" = "ALL" ]; then
395
				local pattern="-v TOTAL"
396
				debug target=ALL: need pattern for all users but not TOTAL: $pattern
397
				local uid="all users"
398
				local uid_count=$(grep $pattern $statefile | wc -l)
399
				debug uid_count retrieved from statefile: $uid_count
400
			else
401
				local pattern="\b$target\b"
402
				debug target=$target: need pattern for a single user: $pattern
403
				local uid=$(grep $pattern $statefile | cut -d' ' -f1)
404
				debug retrieved uid value from statefile: $uid
405
				local uid_count=1
406
			fi
407

    
408
			echo "graph_title Processed messages for $uid (%)"
409
			echo graph_category spamfilter
410
			echo graph_args --base 1000 --upper-limit 100 --rigid
411
			echo graph_vlabel Messages in %
412
			echo "graph_info This graph shows the messages that DSPAM processed for $uid ($uid_count uids) in percentages of all processed messages. Messages are divided in the following categories: true positives/negatives, false positives/negatives, and corpusfed ham/spam."
413
			echo tp.label True positives
414
			echo tp.info Spam messages correctly classified as spam.
415
			echo tp.draw AREASTACK
416
			echo tn.label True negatives
417
			echo tn.info Ham messages correctly classified as ham.
418
			echo tn.draw AREASTACK
419
			echo fp.label False positives
420
			echo fp.info Ham messages incorrectly classified as spam, but corrected by the user.
421
			echo fp.draw AREASTACK
422
			echo fn.label False negatives
423
			echo fn.info Spam messages incorrectly classified as ham, but corrected by the user.
424
			echo fn.draw AREASTACK
425
			echo sc.label Corpusfed spam
426
			echo sc.info Spam messages from a collected corpus for training purposes.
427
			echo sc.draw AREASTACK
428
			echo nc.label Corpusfed ham
429
			echo nc.info Ham messages from a collected corpus for training purposes.
430
			echo nc.draw AREASTACK
431
			;;
432

    
433
		absprocessed)
434
			if [ -n "$pattern" ]; then
435
				debug env.pattern was set, so use it: $pattern
436
				local uid=$description
437
				local uid_count=$(grep $pattern $statefile | wc -l)
438
				debug uid_count retrieved from statefile: $uid_count
439
			elif [ "$target" = "ALL" ]; then
440
				local pattern="-v TOTAL"
441
				debug target=ALL: need pattern for all users but not TOTAL: $pattern
442
				local uid="all users"
443
				local uid_count=$(grep $pattern $statefile | wc -l)
444
				debug uid_count retrieved from statefile: $uid_count
445
			else
446
				local pattern="\b$target\b"
447
				debug target=$target: need pattern for a single user: $pattern
448
				local uid=$(grep $pattern $statefile | cut -d' ' -f1)
449
				debug retrieved uid value from statefile: $uid
450
				local uid_count=1
451
			fi
452

    
453
			echo "graph_title Processed messages for $uid"
454
			echo graph_category spamfilter
455
			echo graph_args --base 1000
456
			echo graph_vlabel Messages
457
			echo graph_total Total
458
			echo "graph_info This graph shows the messages that DSPAM processed for $uid ($uid_count uids). Messages are divided in the following categories: true positives/negatives, false positives/negatives, and corpusfed ham/spam."
459
			echo tp.label True positives
460
			echo tp.info Spam messages correctly classified as spam.
461
			echo tp.draw AREASTACK
462
			echo tn.label True negatives
463
			echo tn.info Ham messages correctly classified as ham.
464
			echo tn.draw AREASTACK
465
			echo fp.label False positives
466
			echo fp.info Ham messages incorrectly classified as spam, but corrected by the user.
467
			echo fp.draw AREASTACK
468
			echo fn.label False negatives
469
			echo fn.info Spam messages incorrectly classified as ham, but corrected by the user.
470
			echo fn.draw AREASTACK
471
			echo sc.label Corpusfed spam
472
			echo sc.info Spam messages from a collected corpus for training purposes.
473
			echo sc.draw AREASTACK
474
			echo nc.label Corpusfed ham
475
			echo nc.info Ham messages from a collected corpus for training purposes.
476
			echo nc.draw AREASTACK
477
			;;
478

    
479
		*)
480
			debug no config available for graph: $graph, exiting with error
481
			exit 78 # EX_CONFIG
482
			;;
483
	esac
484
	debug finished with printing config for graph: $graph
485
}
486

    
487
#
488
# print_fetch
489
#	Output for 'munin-run <plugin> fetch' command: the actual data to graph.
490
#
491
print_fetch() {
492
	debug printing fetch for graph: $graph
493

    
494
	while file_is_locked $statefile; do
495
		debug statefile is locked, waiting
496
		sleep 1
497
	done
498

    
499
	case $graph in
500
		accuracy)
501
			if [ -n "$pattern" ]; then
502
				debug env.pattern was set, so use it: $pattern
503
				continue
504
			elif [ $target = "ALL" ]; then
505
				local pattern="-v TOTAL"
506
				debug target=ALL: need pattern for all users, but not for TOTAL: $pattern
507
			else
508
				local pattern="\b$target\b"
509
				debug target=$target: need pattern for a single user: $pattern
510
			fi
511

    
512
			debug starting grep for user data in statefile
513
			local t_start=$(date +%s)
514
			grep $pattern $statefile | while read x clean_user x x x x x x x x oca x; do
515
				echo $clean_user.value $oca
516
			done
517
			local t_end=$(date +%s)
518
			debug grep finished, runtime $((t_end - t_start)) seconds
519
			;;
520

    
521
		processed)
522
			if [ -n "$pattern" ]; then
523
				debug env.pattern was set, so use it: $pattern
524
				continue
525
			elif [ $target = "ALL" ]; then
526
				local pattern="TOTAL"
527
				debug target=ALL: need pattern for TOTAL of all users: $pattern
528
			else
529
				local pattern="\b$target\b"
530
				debug target=$target: need pattern for a single user: $pattern
531
			fi
532

    
533
			local tmpfile=$(mktemp) || debug failed to create tmpfile && debug tmpfile created at: $tmpfile
534
			debug starting grep for user data in statefile, sending to tmpfile
535
			local t_start=$(date +%s)
536
			grep $pattern $statefile > $tmpfile
537
			local t_end=$(date +%s)
538
			debug grep finished, runtime $((t_end - t_start)) seconds
539

    
540
			debug starting loop over data in tmpfile
541
			while read user x tp tn fp fn sc nc x; do
542
				debug read data for user $user: $tp $tn $fp $fn $sc $nc
543
				all_tp=$((all_tp + tp))
544
				all_tn=$((all_tn + tn))
545
				all_fp=$((all_fp + fp))
546
				all_fn=$((all_fn + fn))
547
				all_sc=$((all_sc + sc))
548
				all_nc=$((all_nc + nc))
549
				debug calculated new totals: $all_tp $all_tn $all_fp $all_fn $all_sc $all_nc
550
			done < $tmpfile
551
			debug finished data loop
552
			rm -f $tmpfile || debug failed to remove tmpfile && debug removed tmpfile
553

    
554
			local total=$((all_tp + all_tn + all_fp + all_fn + all_sc + all_nc))
555
			debug calculated total of all messages: $total
556
			echo tp.value $(abs2perc $all_tp $total)
557
			echo tn.value $(abs2perc $all_tn $total)
558
			echo fp.value $(abs2perc $all_fp $total)
559
			echo fn.value $(abs2perc $all_fn $total)
560
			echo sc.value $(abs2perc $all_sc $total)
561
			echo nc.value $(abs2perc $all_nc $total)
562
			;;
563

    
564
		absprocessed)
565
			if [ -n "$pattern" ]; then
566
				debug env.pattern was set, so use it: $pattern
567
				continue
568
			elif [ $target = "ALL" ]; then
569
				local pattern="TOTAL"
570
				debug target=ALL: need pattern for TOTAL of all users: $pattern
571
			else
572
				local pattern="\b$target\b"
573
				debug target=$target: need pattern for a single user: $pattern
574
			fi
575

    
576
			local tmpfile=$(mktemp) || debug failed to create tmpfile && debug tmpfile created at: $tmpfile
577
			debug starting grep for user data in statefile, sending to tmpfile
578
			local t_start=$(date +%s)
579
			grep $pattern $statefile > $tmpfile
580
			local t_end=$(date +%s)
581
			debug grep finished, runtime $((t_end - t_start)) seconds
582

    
583
			debug starting loop over data in tmpfile
584
			while read user x tp tn fp fn sc nc x; do
585
				debug read data for user $user: $tp $tn $fp $fn $sc $nc
586
				all_tp=$((all_tp + tp))
587
				all_tn=$((all_tn + tn))
588
				all_fp=$((all_fp + fp))
589
				all_fn=$((all_fn + fn))
590
				all_sc=$((all_sc + sc))
591
				all_nc=$((all_nc + nc))
592
				debug calculated new totals: $all_tp $all_tn $all_fp $all_fn $all_sc $all_nc
593
			done < $tmpfile
594
			debug finished data loop
595
			rm -f $tmpfile || debug failed to remove tmpfile && debug removed tmpfile
596

    
597
			echo tp.value $all_tp
598
			echo tn.value $all_tn
599
			echo fp.value $all_fp
600
			echo fn.value $all_fn
601
			echo sc.value $all_sc
602
			echo nc.value $all_nc
603
			;;
604

    
605
		*)
606
			debug no fetch available for graph: $graph, exiting with error
607
			exit 78 # EX_CONFIG
608
			;;
609
	esac
610
	debug finished printing fetch for graph: $graph
611
}
612

    
613

    
614
#####################
615
# Main process loop #
616
#####################
617

    
618
# show env settings
619
debug dspam_ plugin started, pid=$$
620
debug settings:
621
debug - dspam_stats is set to: $dspam_stats
622
debug - statefile is set to: $statefile
623
debug - statefile_max_age is set to: $statefile_max_age
624
debug - warning is set to: $warning
625
debug - critical is set to: $critical
626
debug - pattern is set to: $pattern
627
debug - description is set to: $description
628

    
629
command=$1
630
[ -n "$command" ] || command="fetch"
631
debug - command is set to: $command
632

    
633
graph=$(basename $0 | cut -d'_' -f2)
634
debug - graph is set to: $graph
635

    
636
target=$(basename $0 | cut -d'_' -f3-)
637
[ -n "$target" ] || target="ALL"
638
debug - target is set to: $target
639

    
640
debug settings completed, starting process
641

    
642
case $command in
643
	autoconf)
644
		print_autoconf
645
		;;
646
	suggest)
647
		print_suggest
648
		;;
649
	config)
650
		update_statefile
651
		print_config
652
		;;
653
	fetch)
654
		update_statefile
655
		print_fetch
656
		;;
657
esac
658

    
659
debug exiting
660
exit 0 # EX_OK