Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 23d89499

Von Moritz Bunkus vor mehr als 13 Jahren hinzugefügt

  • ID 23d894990d46c57b3d354b080ca8a373dbeeb756
  • Vorgänger ee71ba33
  • Nachfolger 149d2f33

Perl-Module für Daemons

Unterschiede anzeigen:

modules/fallback/Daemon/Generic.pm
1

  
2
# Copyright (C) 2006, David Muir Sharnoff <perl@dave.sharnoff.org>
3

  
4
package Daemon::Generic;
5

  
6
use strict;
7
use warnings;
8
require Exporter;
9
require POSIX;
10
use Getopt::Long;
11
use File::Slurp;
12
use File::Flock;
13
our @ISA = qw(Exporter);
14
our @EXPORT = qw(newdaemon);
15

  
16
our $VERSION = 0.71;
17

  
18
our $force_quit_delay = 15;
19
our $package = __PACKAGE__;
20
our $caller;
21

  
22
sub newdaemon
23
{
24
	my (%args) = @_;
25
	my $pkg = $caller || caller() || 'main';
26

  
27
	my $foo = bless {}, $pkg;
28

  
29
	unless ($foo->isa($package)) {
30
		no strict qw(refs);
31
		my $isa = \@{"${pkg}::ISA"};
32
		unshift(@$isa, $package);
33
	}
34

  
35
	bless $foo, 'This::Package::Does::Not::Exist';
36
	undef $foo;
37

  
38
	new($pkg, %args);
39
}
40

  
41
sub new
42
{
43
	my ($pkg, %args) = @_;
44

  
45
	if ($pkg eq __PACKAGE__) {
46
		$pkg = caller() || 'main';
47
	}
48

  
49
	srand(time ^ ($$ << 5))
50
		unless $args{no_srand};
51

  
52
	my $av0 = $0;
53
	$av0 =~ s!/!/.!g;
54

  
55
	my $self = {
56
		gd_args		=> \%args,
57
		gd_pidfile	=> $args{pidfile},
58
		gd_logpriority	=> $args{logpriority},
59
		gd_progname	=> $args{progname}
60
					? $args{progname}
61
					: $0,
62
		gd_pidbase	=> $args{pidbase}
63
					? $args{pidbase}
64
					: ($args{progname} 
65
						? "/var/run/$args{progname}"
66
						: "/var/run/$av0"),
67
		gd_foreground	=> $args{foreground} || 0,
68
		configfile	=> $args{configfile}
69
					? $args{configfile}
70
					: ($args{progname}
71
						? "/etc/$args{progname}.conf"
72
						: "/etc/$av0"),
73
		debug		=> $args{debug} || 0,
74
	};
75
	bless $self, $pkg;
76

  
77
	$self->gd_getopt;
78
	$self->gd_parse_argv;
79

  
80
	my $do = $self->{do} = $ARGV[0];
81

  
82
	$self->gd_help		if $do eq 'help';
83
	$self->gd_version	if $do eq 'version';
84
	$self->gd_install	if $do eq 'install';
85
	$self->gd_uninstall	if $do eq 'uninstall';
86

  
87
	$self->gd_pidfile unless $self->{gd_pidfile};
88

  
89
	my %newconfig = $self->gd_preconfig;
90

  
91
	$self->{gd_pidfile} = $newconfig{pidfile} if $newconfig{pidfile};
92

  
93
	print "Configuration looks okay\n" if $do eq 'check';
94

  
95
	my $pidfile = $self->{gd_pidfile};
96
	my $killed = 0;
97
	my $locked = 0;
98
	if (-e $pidfile) {
99
		if ($locked = lock($pidfile, undef, 'nonblocking')) {
100
			# old process is dead
101
			if ($do eq 'status') {
102
			    print "$0 dead\n";
103
			    exit 1;
104
			}
105
		} else {
106
			sleep(2) if -M $pidfile < 2/86400;
107
			my $oldpid = read_file($pidfile);
108
			chomp($oldpid);
109
			if ($oldpid) {
110
				if ($do eq 'stop' or $do eq 'restart') {
111
					$killed = $self->gd_kill($oldpid);
112
					$locked = lock($pidfile);
113
					if ($do eq 'stop') {
114
						unlink($pidfile);
115
						exit;
116
					}
117
				} elsif ($do eq 'reload') {
118
					if (kill(1,$oldpid)) {
119
						print "Requested reconfiguration\n";
120
						exit;
121
					} else {
122
						print "Kill failed: $!\n";
123
					}
124
				} elsif ($do eq 'status') {
125
					if (kill(0,$oldpid)) {
126
						print "$0 running - pid $oldpid\n";
127
						$self->gd_check($pidfile, $oldpid);
128
						exit 0;
129
					} else {
130
						print "$0 dead\n";
131
						exit 1;
132
					}
133
				} elsif ($do eq 'check') {
134
					if (kill(0,$oldpid)) {
135
						print "$0 running - pid $oldpid\n";
136
						$self->gd_check($pidfile, $oldpid);
137
						exit;
138
					} 
139
				} elsif ($do eq 'start') {
140
					print "\u$self->{gd_progname} is already running (pid $oldpid)\n";
141
					exit; # according to LSB, this is no error
142
				}
143
			} else {
144
				$self->gd_error("Pid file $pidfile is invalid but locked, exiting\n");
145
			}
146
		}
147
	} else {
148
		$locked = lock($pidfile, undef, 'nonblocking') 
149
			or die "Could not lock pid file $pidfile: $!";
150
	}
151

  
152
	if ($do eq 'reload' || $do eq 'stop' || $do eq 'check' || ($do eq 'restart' && ! $killed)) {
153
		print "No $0 running\n";
154
	}
155

  
156
	if ($do eq 'stop') {
157
		unlink($pidfile);
158
		exit;
159
	}
160

  
161
	if ($do eq 'status') {
162
		print "Unused\n";
163
		exit 3;
164
	}
165

  
166
	if ($do eq 'check') {
167
		$self->gd_check($pidfile);
168
		exit 
169
	}
170

  
171
	unless ($do eq 'reload' || $do eq 'restart' || $do eq 'start') {
172
		$self->gd_other_cmd($do, $locked);
173
	}
174

  
175
	unless ($self->{gd_foreground}) {
176
		$self->gd_daemonize;
177
	}
178

  
179
	$locked or lock($pidfile, undef, 'nonblocking') 
180
		or die "Could not lock PID file $pidfile: $!";
181

  
182
	write_file($pidfile, "$$\n");
183

  
184
	print STDERR "Starting up...\n";
185

  
186
	$self->gd_postconfig(%newconfig);
187

  
188
	$self->gd_setup_signals;
189

  
190
	$self->gd_run;
191

  
192
	unlink($pidfile);
193
	exit(0);
194
}
195

  
196
sub gd_check {}
197

  
198
sub gd_more_opt { return() }
199

  
200
sub gd_getopt
201
{
202
	my $self = shift;
203
	Getopt::Long::Configure("auto_version");
204
	GetOptions(
205
		'configfile=s'	=> \$self->{configfile},
206
		'foreground!'	=> \$self->{gd_foreground},
207
		'debug!'	=> \$self->{debug},
208
		$self->{gd_args}{options}
209
			? %{$self->{gd_args}{options}}
210
			: (),
211
		$self->gd_more_opt(),
212
	) or exit($self->gd_usage());
213

  
214
	if (@ARGV < ($self->{gd_args}{minimum_args} || 1)) {
215
		exit($self->gd_usage());
216
	}
217
	if (@ARGV > ($self->{gd_args}{maximum_args} || 1)) {
218
		exit($self->gd_usage());
219
	}
220
}
221

  
222
sub gd_parse_argv { }
223

  
224
sub gd_help
225
{
226
	my $self = shift;
227
	exit($self->gd_usage($self->{gd_args}));
228
}
229

  
230
sub gd_version
231
{
232
	my $self = shift;
233
	no strict qw(refs);
234
	my $v = $self->{gd_args}{version} 
235
		|| ${ref($self)."::VERSION"} 
236
		|| $::VERSION 
237
		|| $main::VERSION 
238
		|| "?";
239
	print "$self->{gd_progname} - version $v\n";;
240
	exit;
241
} 
242

  
243
sub gd_pidfile
244
{
245
	my $self = shift;
246
	my $x = $self->{configfile};
247
	$x =~ s!/!.!g;
248
	$self->{gd_pidfile} = "$self->{gd_pidbase}$x.pid";
249
}
250

  
251
sub gd_other_cmd
252
{
253
	my $self = shift;
254
	$self->gd_usage;
255
	exit(1);
256
}
257

  
258
sub gd_redirect_output
259
{
260
	my $self = shift;
261
	return if $self->{gd_foreground};
262
	my $logname = $self->gd_logname;
263
	my $p = $self->{gd_logpriority} ? "-p $self->{gd_logpriority}" : "";
264
	open(STDERR, "|logger $p -t '$logname'") or (print "could not open stderr: $!" && exit(1));
265
	close(STDOUT);
266
	open(STDOUT, ">&STDERR") or die "redirect STDOUT -> STDERR: $!";
267
	close(STDIN);
268
}
269

  
270
sub gd_daemonize
271
{
272
	my $self = shift;
273
	print "Starting $self->{gd_progname} server\n";
274
	$self->gd_redirect_output();
275
	my $pid;
276
	POSIX::_exit(0) if $pid = fork;
277
	die "Could not fork: $!" unless defined $pid;
278
	POSIX::_exit(0) if $pid = fork;
279
	die "Could not fork: $!" unless defined $pid;
280

  
281
	POSIX::setsid();
282
	select(STDERR);
283
	$| = 1;
284
	print "Sucessfully daemonized\n";
285
}
286

  
287
sub gd_logname
288
{
289
	my $self = shift;
290
	return $self->{gd_progname}."[$$]";
291
}
292

  
293
sub gd_reconfig_event
294
{
295
	my $self = shift;
296
	print STDERR "Reconfiguration requested\n";
297
	$self->gd_postconfig($self->gd_preconfig());
298
}
299

  
300
sub gd_quit_event
301
{
302
	my $self = shift;
303
	print STDERR "Quitting...\n";
304
	exit(0);
305
}
306

  
307
sub gd_setup_signals
308
{
309
	my $self = shift;
310
	$SIG{INT} = sub { $self->gd_quit_event() };
311
	$SIG{HUP} = sub { $self->gd_reconfig_event() };
312
}
313

  
314
sub gd_run { die "must defined gd_run()" }
315

  
316
sub gd_error
317
{
318
	my $self = shift;
319
	my $e = shift;
320
	my $do = $self->{do};
321
	if ($do && $do eq 'stop') {
322
		warn $e;
323
	} else {
324
		die $e;
325
	}
326
}
327

  
328
sub gd_flags_more { return () }
329

  
330
sub gd_flags
331
{
332
	my $self = shift;
333
	return (
334
		'-c file'	=> "Specify configuration file (instead of $self->{configfile})",
335
		'-f'		=> "Run in the foreground (don't detach)",
336
		$self->gd_flags_more
337
	);
338
}
339

  
340
sub gd_commands_more { return () }
341

  
342
sub gd_commands
343
{
344
	my $self = shift;
345
	return (
346
		start		=> "Starts a new $self->{gd_progname} if there isn't one running already",
347
		stop		=> "Stops a running $self->{gd_progname}",
348
		reload		=> "Causes a running $self->{gd_progname} to reload it's config file.  Starts a new one if none is running.",
349
		restart		=> "Stops a running $self->{gd_progname} if one is running.  Starts a new one.",
350
		$self->gd_commands_more(),
351
		($self->gd_can_install()
352
			? ('install' => "Setup $self->{gd_progname} to run automatically after reboot")
353
			: ()),
354
		($self->gd_can_uninstall()
355
			? ('uninstall' => "Do not run $self->{gd_progname} after reboots")
356
			: ()),
357
		check		=> "Check the configuration file and report the daemon state",
358
		help		=> "Display this usage info",
359
		version		=> "Display the version of $self->{gd_progname}",
360
	)
361
}
362

  
363
sub gd_positional_more { return() }
364

  
365
sub gd_alts
366
{
367
	my $offset = shift;
368
	my @results;
369
	for (my $i = $offset; $i <= $#_; $i += 2) {
370
		push(@results, $_[$i]);
371
	}
372
	return @results;
373
}
374

  
375
sub gd_usage
376
{
377
	my $self = shift;
378

  
379
	require Text::Wrap;
380
	import Text::Wrap;
381

  
382
	my $col = 15;
383

  
384
	my @flags = $self->gd_flags;
385
	my @commands = $self->gd_commands;
386
	my @positional = $self->gd_positional_more;
387

  
388
	my $summary = "Usage: $self->{gd_progname} ";
389
	my $details = '';
390
	for my $i (gd_alts(0, @flags)) {
391
		$summary .= "[ $i ] ";
392
	}
393
	$summary .= "{ ";
394
	$summary .= join(" | ", gd_alts(0, @commands));
395
	$summary .= " } ";
396
	$summary .= join(" ", gd_alts(0, @positional));
397

  
398
	my (@all) = (@flags, @commands, @positional);
399
	while (@all) {
400
		my ($key, $desc) = splice(@all, 0, 2);
401
		local($Text::Wrap::columns) = 79;
402
		$details .= wrap(
403
			sprintf(" %-${col}s ", $key),
404
			" " x ($col + 2),
405
			$desc);
406
		$details .= "\n";
407
	}
408

  
409
	print "$summary\n$details";
410
	return 0;
411
}
412

  
413
sub gd_install_pre {}
414
sub gd_install_post {}
415

  
416
sub gd_can_install
417
{
418
	my $self = shift;
419
	require File::Basename;
420
	my $basename = File::Basename::basename($0);
421
	if (
422
		-x "/usr/sbin/update-rc.d"
423
		&& 
424
		-x $0
425
		&& 
426
		$0 !~ m{^(?:/usr|/var)?/tmp/}
427
		&&
428
		eval { symlink("",""); 1 }
429
		&& 
430
		-d "/etc/init.d"
431
		&&
432
		! -e "/etc/init.d/$basename"
433
	) {
434
		return sub {
435
			$self->gd_install_pre("update-rc.d");
436
			require Cwd;
437
			my $abs_path = Cwd::abs_path($0);
438
			symlink($abs_path, "/etc/init.d/$basename")
439
				or die "Install failed: symlink /etc/init.d/$basename -> $abs_path: $!\n";
440
			print "+ /usr/sbin/update-rc.d $basename defaults\n";
441
			system("/usr/sbin/update-rc.d", $basename, "defaults");
442
			my $exit = $? >> 8;
443
			$self->gd_install_post("update-rc.d");
444
			exit($exit) if $exit;
445
		};
446
	}
447

  
448
	return 0;
449
}
450

  
451
sub gd_install
452
{
453
	my $self = shift;
454
	my $ifunc = $self->gd_can_install();
455
	die "Install command not supported\n" unless $ifunc;
456
	&$ifunc($self);
457
	exit(0);
458
}
459

  
460
sub gd_uninstall_pre {}
461
sub gd_uninstall_post {}
462

  
463
sub gd_can_uninstall
464
{
465
	my $self = shift;
466
	require File::Basename;
467
	my $basename = File::Basename::basename($0);
468
	require Cwd;
469
	my $abs_path = Cwd::abs_path($0) || 'no abs path';
470
	my $link = readlink("/etc/init.d/$basename") || 'no link';
471
	if (
472
		$link eq $abs_path
473
		&& 
474
		-x "/usr/sbin/update-rc.d"
475
	) {
476
		return sub {
477
			$self->gd_uninstall_pre("update-rc.d");
478
			unlink("/etc/init.d/$basename");
479
			print "+ /usr/sbin/update-rc.d $basename remove\n";
480
			system("/usr/sbin/update-rc.d", $basename, "remove");
481
			my $exit = $? >> 8;
482
			$self->gd_uninstall_post("update-rc.d");
483
			exit($exit) if $exit;
484
		}
485
	}
486
	return 0;
487
}
488

  
489
sub gd_uninstall
490
{
491
	my $self = shift;
492
	my $ufunc = $self->gd_can_uninstall();
493
	die "Cannot uninstall\n" unless $ufunc;
494
	&$ufunc($self);
495
	exit(0);
496
}
497

  
498
sub gd_kill
499
{
500
	my ($self, $pid) = @_;
501

  
502
	my $talkmore = 0;
503
	my $killed = 0;
504
	if (kill(0, $pid)) {
505
		$killed = 1;
506
		kill(2,$pid);
507
		print "Killing $pid\n";
508
		my $t = time;
509
		sleep(1) if kill(0, $pid);
510
		if ($force_quit_delay && kill(0, $pid)) {
511
			print "Waiting for $pid to die...\n";
512
			$talkmore = 1;
513
			while(kill(0, $pid) && time - $t < $force_quit_delay) {
514
				sleep(1);
515
			}
516
		}
517
		if (kill(15, $pid)) {
518
			print "Killing $pid with -TERM...\n";
519
			if ($force_quit_delay) {
520
				while(kill(0, $pid) && time - $t < $force_quit_delay * 2) {
521
					sleep(1);
522
				}
523
			} else {
524
				sleep(1) if kill(0, $pid);
525
			}
526
		}
527
		if (kill(9, $pid)) {
528
			print "Killing $pid with -KILL...\n";
529
			my $k9 = time;
530
			my $max = $force_quit_delay * 4;
531
			$max = 60 if $max < 60;
532
			while(kill(0, $pid)) {
533
				if (time - $k9 > $max) {
534
					print "Giving up on $pid ever dying.\n";
535
					exit(1);
536
				}
537
				print "Waiting for $pid to die...\n";
538
				sleep(1);
539
			}
540
		}
541
		print "Process $pid is gone\n" if $talkmore;
542
	} else {
543
		print "Process $pid no longer running\n";
544
	}
545
	return $killed;
546
}
547

  
548
sub gd_preconfig { die "gd_preconfig() must be redefined"; }
549

  
550
sub gd_postconfig { }
551

  
552

  
553
1;
modules/fallback/Daemon/Generic/Event.pm
1

  
2
# Copyright (C) 2006, David Muir Sharnoff <muir@idiom.com>
3

  
4
package Daemon::Generic::Event;
5

  
6
use strict;
7
use warnings;
8
require Daemon::Generic;
9
require Event;
10
require Exporter;
11

  
12
our @ISA = qw(Daemon::Generic Exporter);
13
our @EXPORT = @Daemon::Generic::EXPORT;
14
our $VERSION = 0.3;
15

  
16
sub newdaemon
17
{
18
	local($Daemon::Generic::caller) = caller() || 'main';
19
	local($Daemon::Generic::package) = __PACKAGE__;
20
	Daemon::Generic::newdaemon(@_);
21
}
22

  
23
sub gd_setup_signals
24
{
25
	my $self = shift;
26
	my $reload_event = Event->signal(
27
		signal	=> 'HUP',
28
		desc	=> 'reload on SIGHUP',
29
		prio	=> 6,
30
		cb	=> sub { 
31
			$self->gd_reconfig_event; 
32
			$self->{gd_timer}->cancel()
33
				if $self->{gd_timer};
34
			$self->gd_setup_timer();
35
		},
36
	);
37
	my $quit_event = Event->signal(
38
		signal	=> 'INT',
39
		cb	=> sub { $self->gd_quit_event; },
40
	);
41
}
42

  
43
sub gd_setup_timer
44
{
45
	my $self = shift;
46
	if ($self->can('gd_run_body')) {
47
		my $interval = ($self->can('gd_interval') && $self->gd_interval()) || 1;
48
		$self->{gd_timer} = Event->timer(
49
			cb		=> [ $self, 'gd_run_body' ],
50
			interval	=> $interval,
51
			hard		=> 0,
52
		);
53
	}
54
}
55

  
56
sub gd_run
57
{
58
	my $self = shift;
59
	$self->gd_setup_timer();
60
	Event::loop();
61
}
62

  
63
sub gd_quit_event
64
{
65
	my $self = shift;
66
	print STDERR "Quitting...\n";
67
	Event::unloop_all();
68
}
69

  
70
1;
71

  
72
=head1 NAME
73

  
74
 Daemon::Generic::Event - Generic daemon framework with Event.pm
75

  
76
=head1 SYNOPSIS
77

  
78
 use Daemon::Generic::Event;
79

  
80
 @ISA = qw(Daemon::Generic::Event);
81

  
82
 sub gd_preconfig {
83
	# stuff
84
 }
85

  
86
=head1 DESCRIPTION
87

  
88
Daemon::Generic::Event is a subclass of L<Daemon::Generic> that
89
predefines some methods:
90

  
91
=over 15
92

  
93
=item gd_run()
94

  
95
Setup a periodic callback to C<gd_run_body()> if there is a C<gd_run_body()>.
96
Call C<Event::loop()>.  
97

  
98
=item gd_setup_signals()
99

  
100
Bind SIGHUP to call C<gd_reconfig_event()>. 
101
Bind SIGINT to call C<gd_quit_event()>.
102

  
103
=back
104

  
105
To use Daemon::Generic::Event, you have to provide a C<gd_preconfig()>
106
method.   It can be empty if you have a C<gd_run_body()>.
107

  
108
Set up your own events in C<gd_preconfig()> and C<gd_postconfig()>.
109

  
110
If you have a C<gd_run_body()> method, it will be called once per
111
second or every C<gd_interval()> seconds if you have a C<gd_interval()>
112
method.  Unlike in L<Daemon::Generic::While1>, C<gd_run_body()> should
113
not include a call to C<sleep()>.
114

  
115
=head1 THANK THE AUTHOR
116

  
117
If you need high-speed internet services (T1, T3, OC3 etc), please 
118
send me your request-for-quote.  I have access to very good pricing:
119
you'll save money and get a great service.
120

  
121
=head1 LICENSE
122

  
123
Copyright(C) 2006 David Muir Sharnoff <muir@idiom.com>. 
124
This module may be used and distributed on the same terms
125
as Perl itself.
126

  
modules/fallback/Daemon/Generic/While1.pm
1
# Copyright (C) 2006, David Muir Sharnoff <muir@idiom.com>
2

  
3
package Daemon::Generic::While1;
4

  
5
use strict;
6
use warnings;
7
use Carp;
8
require Daemon::Generic;
9
require POSIX;
10
require Exporter;
11

  
12
our @ISA = qw(Daemon::Generic Exporter);
13
our @EXPORT = @Daemon::Generic::EXPORT;
14
our $VERSION = 0.3;
15

  
16
sub newdaemon
17
{
18
	local($Daemon::Generic::caller) = caller() || 'main';
19
	local($Daemon::Generic::package) = __PACKAGE__;
20
	Daemon::Generic::newdaemon(@_);
21
}
22

  
23
sub gd_setup_signals
24
{
25
	my ($self) = @_;
26
	$SIG{HUP} = sub {
27
		$self->{gd_sighup} = time;
28
	};
29
	my $child;
30
	$SIG{INT} = sub {
31
		$self->{gd_sigint} = time;
32
		#
33
		# We'll be getting a SIGTERM in a bit if we're not dead, so let's use it.
34
		#
35
		$SIG{TERM} = sub {
36
			$self->gd_quit_event(); 
37
			kill(15, $child) if $child;  # if we're still alive, let's stay that way
38
		};
39
	};
40
}
41

  
42
sub gd_sleep
43
{
44
	my ($self, $period) = @_;
45
	croak "Sleep period must be defined" unless defined $period;
46
	my $hires;
47
	if ($period*1000 != int($period*1000)) {
48
		$hires = 1;
49
		require Time::HiRes;
50
		import Time::HiRes qw(time sleep);
51
	}
52
	my $t = time;
53
	while (time - $t < $period) {
54
		return if $self->{gd_sigint};
55
		return if $self->{gd_sighup};
56
		if ($hires) {
57
			my $p = (time - $t < 1)
58
				? time - $t
59
				: 1;
60
			sleep($p);
61
		} else {
62
			sleep(1);
63
		}
64
	}
65
}
66

  
67
sub gd_run
68
{
69
	my ($self) = @_;
70
	while(1) {
71
		if ($self->{gd_sigint}) {
72
			$self->{gd_sigint} = 0;
73
			$self->gd_quit_event();
74
		}
75

  
76
		if ($self->{gd_sighup}) {
77
			$self->{gd_sighup} = 0;
78
			$self->gd_reconfig_event();
79
		}
80

  
81
		$self->gd_run_body();
82
	}
83
}
84

  
85
sub gd_reconfig_event
86
{
87
	my $self = shift;
88
	print STDERR "Reconfiguration requested\n";
89
	$self->gd_postconfig($self->gd_preconfig());
90
}
91

  
92
sub gd_quit_event
93
{
94
	print STDERR "Quitting...\n";
95
	exit(0);
96
}
97

  
98

  
99
sub gd_run_body { die "must override gd_run_body()" }
100

  
101
1;
102

  
103
=head1 NAME
104

  
105
 Daemon::Generic::While1 - Daemon framework with default while(1) loop
106

  
107
=head1 SYNOPSIS
108

  
109
 @ISA = qw(Daemon::Generic::While1);
110

  
111
 sub gd_run_body {
112
	# stuff
113
 }
114

  
115
=head1 DESCRIPTION
116

  
117
This is a slight variation on L<Daemon::Generic>: a default
118
C<gd_run()> provided.  It has a while(1) loop that calls 
119
C<gd_run_body()> over and over.  It checks for reconifg and
120
and terminate events and only actions them between calls
121
to C<gd_run_body()>. 
122

  
123
Terminate events will be forced through after 
124
C<$Daemon::Generic::force_quit_delay> seconds if
125
C<gd_run_body()> doesn't return quickly enough.
126

  
127
=head1 SUBCLASS METHODS REQUIRD
128

  
129
The following method is required to be overridden to subclass
130
Daemon::Generic::While1:
131

  
132
=over 15
133

  
134
=item gd_run_body()
135

  
136
This method will be called over and over.  This method should
137
include a call to C<sleep(1)> (or a bit more).  Reconfig events
138
will not interrupt it.  Quit events will only interrupt it 
139
after 15 seconds.  
140

  
141
=back
142

  
143
=head1 ADDITIONAL METHODS
144

  
145
The following additional methods are available for your use
146
(as compared to L<Daemon::Generic>):
147

  
148
=over 15
149

  
150
=item gd_sleep($period)
151

  
152
This will sleep for C<$period> seconds but in one-second
153
intervals so that if a SIGINT or SIGHUP arrives the sleep
154
period can end more quickly.
155

  
156
Using this makes it safe for C<gd_run_body()> to sleep for
157
longer than C<$Daemon::Generic::force_quit_delay> seconds 
158
at a time.
159

  
160
=back
161

  
162
=head1 ADDITIONAL MEMBER DATA
163

  
164
The following additional bits of member data are defined:
165

  
166
=over 15
167

  
168
=item gd_sigint
169

  
170
The time at which an (unprocessed) SIGINT was recevied.
171

  
172
=item gd_sighup
173

  
174
The time at which an (unprocessed) SIGHUP was recevied.
175

  
176
=back
177

  
178
=head1 THANK THE AUTHOR
179

  
180
If you need high-speed internet services (T1, T3, OC3 etc), please 
181
send me your request-for-quote.  I have access to very good pricing:
182
you'll save money and get a great service.
183

  
184
=head1 LICENSE
185

  
186
Copyright(C) 2006 David Muir Sharnoff <muir@idiom.com>. 
187
This module may be used and distributed on the same terms
188
as Perl itself.
189

  
modules/fallback/File/Flock.pm
1
# Copyright (C) 1996, 1998 David Muir Sharnoff
2

  
3
package File::Flock;
4

  
5
require Exporter;
6
@ISA = qw(Exporter);
7
@EXPORT = qw(lock unlock lock_rename);
8

  
9
use Carp;
10
use POSIX qw(EAGAIN EACCES EWOULDBLOCK ENOENT EEXIST O_EXCL O_CREAT O_RDWR); 
11
use Fcntl qw(LOCK_SH LOCK_EX LOCK_NB LOCK_UN);
12
use IO::File;
13

  
14
use vars qw($VERSION $debug $av0debug);
15

  
16
BEGIN	{
17
	$VERSION = 2008.01;
18
	$debug = 0;
19
	$av0debug = 0;
20
}
21

  
22
use strict;
23
no strict qw(refs);
24

  
25
my %locks;		# did we create the file?
26
my %lockHandle;
27
my %shared;
28
my %pid;
29
my %rm;
30

  
31
sub new
32
{
33
	my ($pkg, $file, $shared, $nonblocking) = @_;
34
	&lock($file, $shared, $nonblocking) or return undef;
35
	return bless \$file, $pkg;
36
}
37

  
38
sub DESTROY
39
{
40
	my ($this) = @_;
41
	unlock($$this);
42
}
43

  
44
sub lock
45
{
46
	my ($file, $shared, $nonblocking) = @_;
47

  
48
	my $f = new IO::File;
49

  
50
	my $created = 0;
51
	my $previous = exists $locks{$file};
52

  
53
	# the file may be springing in and out of existance...
54
	OPEN:
55
	for(;;) {
56
		if (-e $file) {
57
			unless (sysopen($f, $file, O_RDWR)) {
58
				redo OPEN if $! == ENOENT;
59
				croak "open $file: $!";
60
			}
61
		} else {
62
			unless (sysopen($f, $file, O_CREAT|O_EXCL|O_RDWR)) {
63
				redo OPEN if $! == EEXIST;
64
				croak "open >$file: $!";
65
			}
66
			print STDERR " {$$ " if $debug; # }
67
			$created = 1;
68
		}
69
		last;
70
	}
71
	$locks{$file} = $created || $locks{$file} || 0;
72
	$shared{$file} = $shared;
73
	$pid{$file} = $$;
74
	
75
	$lockHandle{$file} = $f;
76

  
77
	my $flags;
78

  
79
	$flags = $shared ? LOCK_SH : LOCK_EX;
80
	$flags |= LOCK_NB
81
		if $nonblocking;
82
	
83
	local($0) = "$0 - locking $file" if $av0debug && ! $nonblocking;
84
	my $r = flock($f, $flags);
85

  
86
	print STDERR " ($$ " if $debug and $r;
87

  
88
	if ($r) {
89
		# let's check to make sure the file wasn't
90
		# removed on us!
91

  
92
		my $ifile = (stat($file))[1];
93
		my $ihandle;
94
		eval { $ihandle = (stat($f))[1] };
95
		croak $@ if $@;
96

  
97
		return 1 if defined $ifile 
98
			and defined $ihandle 
99
			and $ifile == $ihandle;
100

  
101
		# oh well, try again
102
		flock($f, LOCK_UN);
103
		close($f);
104
		return File::Flock::lock($file);
105
	}
106

  
107
	return 1 if $r;
108
	if ($nonblocking and 
109
		(($! == EAGAIN) 
110
		or ($! == EACCES)
111
		or ($! == EWOULDBLOCK))) 
112
	{
113
		if (! $previous) {
114
			delete $locks{$file};
115
			delete $lockHandle{$file};
116
			delete $shared{$file};
117
			delete $pid{$file};
118
		}
119
		if ($created) {
120
			# oops, a bad thing just happened.  
121
			# We don't want to block, but we made the file.
122
			&background_remove($f, $file);
123
		}
124
		close($f);
125
		return 0;
126
	}
127
	croak "flock $f $flags: $!";
128
}
129

  
130
#
131
# get a lock on a file and remove it if it's empty.  This is to
132
# remove files that were created just so that they could be locked.
133
#
134
# To do this without blocking, defer any files that are locked to the
135
# the END block.
136
#
137
sub background_remove
138
{
139
	my ($f, $file) = @_;
140

  
141
	if (flock($f, LOCK_EX|LOCK_NB)) {
142
		unlink($file)
143
			if -s $file == 0;
144
		flock($f, LOCK_UN);
145
		return 1;
146
	} else {
147
		$rm{$file} = 1
148
			unless exists $rm{$file};
149
		return 0;
150
	}
151
}
152

  
153
sub unlock
154
{
155
	my ($file) = @_;
156

  
157
	if (ref $file eq 'File::Flock') {
158
		bless $file, 'UNIVERSAL'; # avoid destructor later
159
		$file = $$file;
160
	}
161

  
162
	croak "no lock on $file" unless exists $locks{$file};
163
	my $created = $locks{$file};
164
	my $unlocked = 0;
165

  
166

  
167
	my $size = -s $file;
168
	if ($created && defined($size) && $size == 0) {
169
		if ($shared{$file}) {
170
			$unlocked = 
171
				&background_remove($lockHandle{$file}, $file);
172
		} else { 
173
			# {
174
			print STDERR " $$} " if $debug;
175
			unlink($file) 
176
				or croak "unlink $file: $!";
177
		}
178
	}
179
	delete $locks{$file};
180
	delete $pid{$file};
181

  
182
	my $f = $lockHandle{$file};
183

  
184
	delete $lockHandle{$file};
185

  
186
	return 0 unless defined $f;
187

  
188
	print STDERR " $$) " if $debug;
189
	$unlocked or flock($f, LOCK_UN)
190
		or croak "flock $file UN: $!";
191

  
192
	close($f);
193
	return 1;
194
}
195

  
196
sub lock_rename
197
{
198
	my ($oldfile, $newfile) = @_;
199

  
200
	if (exists $locks{$newfile}) {
201
		unlock $newfile;
202
	}
203
	delete $locks{$newfile};
204
	delete $shared{$newfile};
205
	delete $pid{$newfile};
206
	delete $lockHandle{$newfile};
207
	delete $rm{$newfile};
208

  
209
	$locks{$newfile}	= $locks{$oldfile}	if exists $locks{$oldfile};
210
	$shared{$newfile}	= $shared{$oldfile}	if exists $shared{$oldfile};
211
	$pid{$newfile}		= $pid{$oldfile}	if exists $pid{$oldfile};
212
	$lockHandle{$newfile}	= $lockHandle{$oldfile} if exists $lockHandle{$oldfile};
213
	$rm{$newfile}		= $rm{$oldfile}		if exists $rm{$oldfile};
214

  
215
	delete $locks{$oldfile};
216
	delete $shared{$oldfile};
217
	delete $pid{$oldfile};
218
	delete $lockHandle{$oldfile};
219
	delete $rm{$oldfile};
220
}
221

  
222
#
223
# Unlock any files that are still locked and remove any files
224
# that were created just so that they could be locked.
225
#
226
END {
227
	my $f;
228
	for $f (keys %locks) {
229
		&unlock($f)
230
			if $pid{$f} == $$;
231
	}
232

  
233
	my %bgrm;
234
	for my $file (keys %rm) {
235
		my $f = new IO::File;
236
		if (sysopen($f, $file, O_RDWR)) {
237
			if (flock($f, LOCK_EX|LOCK_NB)) {
238
				unlink($file)
239
					if -s $file == 0;
240
				flock($f, LOCK_UN);
241
			} else {
242
				$bgrm{$file} = 1;
243
			}
244
			close($f);
245
		}
246
	}
247
	if (%bgrm) {
248
		my $ppid = fork;
249
		croak "cannot fork" unless defined $ppid;
250
		my $pppid = $$;
251
		my $b0 = $0;
252
		$0 = "$b0: waiting for child ($ppid) to fork()";
253
		unless ($ppid) {
254
			my $pid = fork;
255
			croak "cannot fork" unless defined $pid;
256
			unless ($pid) {
257
				for my $file (keys %bgrm) {
258
					my $f = new IO::File;
259
					if (sysopen($f, $file, O_RDWR)) {
260
						if (flock($f, LOCK_EX)) {
261
							unlink($file)
262
								if -s $file == 0;
263
							flock($f, LOCK_UN);
264
						}
265
						close($f);
266
					}
267
				}
268
				print STDERR " $pppid] $pppid)" if $debug;
269
			}
270
			kill(9, $$); # exit w/o END or anything else
271
		}
272
		waitpid($ppid, 0);
273
		kill(9, $$); # exit w/o END or anything else
274
	}
275
}
276

  
277
1;
278

  
279
__DATA__
280

  
281
=head1 NAME
282

  
283
 File::Flock - file locking with flock
284

  
285
=head1 SYNOPSIS
286

  
287
 use File::Flock;
288

  
289
 lock($filename);
290

  
291
 lock($filename, 'shared');
292

  
293
 lock($filename, undef, 'nonblocking');
294

  
295
 lock($filename, 'shared', 'nonblocking');
296

  
297
 unlock($filename);
298

  
299
 my $lock = new File::Flock '/somefile';
300

  
301
 lock_rename($oldfilename, $newfilename)
302

  
303
=head1 DESCRIPTION
304

  
305
Lock files using the flock() call.  If the file to be locked does not
306
exist, then the file is created.  If the file was created then it will
307
be removed when it is unlocked assuming it's still an empty file.
308

  
309
Locks can be created by new'ing a B<File::Flock> object.  Such locks
310
are automatically removed when the object goes out of scope.  The
311
B<unlock()> method may also be used.
312

  
313
B<lock_rename()> is used to tell File::Flock when a file has been
314
renamed (and thus the internal locking data that is stored based
315
on the filename should be moved to a new name).  B<unlock()> the
316
new name rather than the original name.
317

  
318
=head1 LICENSE
319

  
320
File::Flock may be used/modified/distibuted on the same terms
321
as perl itself.  
322

  
323
=head1 AUTHOR
324

  
325
David Muir Sharnoff <muir@idiom.org>
326

  
327

  

Auch abrufbar als: Unified diff