Commit | Line | Data |
a4952679 |
1 | package MooseX::Daemonize; |
a392fa53 |
2 | use strict; # because Kwalitee is pedantic |
a4952679 |
3 | use Moose::Role; |
8ac4733f |
4 | use MooseX::Types::Path::Class; |
e803cff5 |
5 | use File::Path qw(make_path); |
a392fa53 |
6 | |
05b96f4d |
7 | our $VERSION = '0.13'; |
a4952679 |
8 | |
4327fe98 |
9 | with 'MooseX::Daemonize::WithPidFile', |
10 | 'MooseX::Getopt'; |
1d85c76d |
11 | |
df3c463b |
12 | sub OK () { 0 } |
13 | sub ERROR () { 1 } |
a4952679 |
14 | |
15 | has progname => ( |
5b9ebe08 |
16 | metaclass => 'Getopt', |
4327fe98 |
17 | isa => 'Str', |
18 | is => 'ro', |
19 | lazy => 1, |
20 | required => 1, |
21 | default => sub { |
a392fa53 |
22 | ( my $name = lc $_[0]->meta->name ) =~ s/::/_/g; |
23 | return $name; |
24 | }, |
a4952679 |
25 | ); |
26 | |
d9e417f4 |
27 | has pidbase => ( |
5b9ebe08 |
28 | metaclass => 'Getopt', |
4327fe98 |
29 | isa => 'Path::Class::Dir', |
30 | is => 'ro', |
31 | coerce => 1, |
5b9ebe08 |
32 | required => 1, |
4327fe98 |
33 | lazy => 1, |
b38ab84f |
34 | default => sub { Path::Class::Dir->new('', 'var', 'run') }, |
24a6a660 |
35 | ); |
36 | |
d9e417f4 |
37 | has basedir => ( |
5b9ebe08 |
38 | metaclass => 'Getopt', |
4327fe98 |
39 | isa => 'Path::Class::Dir', |
40 | is => 'ro', |
41 | coerce => 1, |
42 | required => 1, |
43 | lazy => 1, |
44 | default => sub { Path::Class::Dir->new('/') }, |
a4952679 |
45 | ); |
46 | |
47 | has foreground => ( |
2eced271 |
48 | metaclass => 'Getopt', |
cbff8e52 |
49 | cmd_aliases => 'f', |
a4952679 |
50 | isa => 'Bool', |
51 | is => 'ro', |
52 | default => sub { 0 }, |
53 | ); |
54 | |
b916501e |
55 | has stop_timeout => ( |
5b9ebe08 |
56 | metaclass => 'Getopt', |
4327fe98 |
57 | isa => 'Int', |
58 | is => 'rw', |
59 | default => sub { 2 } |
b916501e |
60 | ); |
61 | |
5b9ebe08 |
62 | # internal book-keeping |
63 | |
64 | has status_message => ( |
65 | metaclass => 'NoGetopt', |
66 | isa => 'Str', |
67 | is => 'rw', |
68 | clearer => 'clear_status_message', |
69 | ); |
70 | |
71 | has exit_code => ( |
72 | metaclass => 'NoGetopt', |
73 | isa => 'Int', |
74 | is => 'rw', |
75 | clearer => 'clear_exit_code', |
76 | ); |
77 | |
4327fe98 |
78 | # methods ... |
79 | |
80 | ## PID file related stuff ... |
81 | |
d9e417f4 |
82 | sub init_pidfile { |
83 | my $self = shift; |
84 | my $file = $self->pidbase . '/' . $self->progname . '.pid'; |
e803cff5 |
85 | |
86 | if ( !-d $self->pidbase ) { |
87 | make_path( $self->pidbase, { error => \my $err } ); |
88 | if (@$err) { |
89 | confess sprintf( "Cannot create pidbase directory '%s': %s", |
90 | $self->pidbase, @$err ); |
91 | } |
92 | } |
93 | |
d9e417f4 |
94 | confess "Cannot write to $file" unless (-e $file ? -w $file : -w $self->pidbase); |
d8985b7d |
95 | MooseX::Daemonize::Pid::File->new( file => $file ); |
d9e417f4 |
96 | } |
97 | |
5b9ebe08 |
98 | # backwards compat, |
4327fe98 |
99 | sub check { (shift)->pidfile->is_running } |
100 | sub save_pid { (shift)->pidfile->write } |
101 | sub remove_pid { (shift)->pidfile->remove } |
102 | sub get_pid { (shift)->pidfile->pid } |
103 | |
104 | ## signal handling ... |
105 | |
106 | sub setup_signals { |
107 | my $self = shift; |
4a24225a |
108 | $SIG{'INT'} = sub { $self->shutdown }; |
109 | # I can't think of a sane default here really ... |
110 | # $SIG{'HUP'} = sub { $self->handle_sighup }; |
4327fe98 |
111 | } |
112 | |
4a24225a |
113 | sub shutdown { |
114 | my $self = shift; |
115 | $self->pidfile->remove if $self->pidfile->pid == $$; |
116 | exit(0); |
117 | } |
4327fe98 |
118 | |
119 | ## daemon control methods ... |
120 | |
a4952679 |
121 | sub start { |
5b9ebe08 |
122 | my $self = shift; |
123 | |
124 | $self->clear_status_message; |
125 | $self->clear_exit_code; |
126 | |
127 | if ($self->pidfile->is_running) { |
9c8a33b3 |
128 | $self->exit_code($self->OK); |
1d85c76d |
129 | $self->status_message('Daemon is already running with pid (' . $self->pidfile->pid . ')'); |
5b9ebe08 |
130 | return !($self->exit_code); |
131 | } |
1d85c76d |
132 | |
133 | if ($self->foreground) { |
5b9ebe08 |
134 | $self->is_daemon(1); |
135 | } |
1d85c76d |
136 | else { |
137 | eval { $self->daemonize }; |
5b9ebe08 |
138 | if ($@) { |
9c8a33b3 |
139 | $self->exit_code($self->ERROR); |
5b9ebe08 |
140 | $self->status_message('Start failed : ' . $@); |
141 | return !($self->exit_code); |
142 | } |
143 | } |
cbff8e52 |
144 | |
5b9ebe08 |
145 | unless ($self->is_daemon) { |
1d85c76d |
146 | $self->exit_code($self->OK); |
5b9ebe08 |
147 | $self->status_message('Start succeeded'); |
148 | return !($self->exit_code); |
149 | } |
150 | |
151 | $self->pidfile->pid($$); |
cbff8e52 |
152 | |
24a6a660 |
153 | # Change to basedir |
154 | chdir $self->basedir; |
fa2b72a4 |
155 | |
ff5cee29 |
156 | $self->pidfile->write; |
3543c999 |
157 | $self->setup_signals; |
158 | return $$; |
159 | } |
160 | |
5b9ebe08 |
161 | sub status { |
162 | my $self = shift; |
163 | |
164 | $self->clear_status_message; |
165 | $self->clear_exit_code; |
166 | |
167 | if ($self->pidfile->is_running) { |
1d85c76d |
168 | $self->exit_code($self->OK); |
169 | $self->status_message('Daemon is running with pid (' . $self->pidfile->pid . ')'); |
5b9ebe08 |
170 | } |
1d85c76d |
171 | else { |
9c8a33b3 |
172 | $self->exit_code($self->ERROR); |
5b9ebe08 |
173 | $self->status_message('Daemon is not running with pid (' . $self->pidfile->pid . ')'); |
174 | } |
175 | |
176 | return !($self->exit_code); |
177 | } |
178 | |
4327fe98 |
179 | sub restart { |
5b9ebe08 |
180 | my $self = shift; |
181 | |
182 | $self->clear_status_message; |
183 | $self->clear_exit_code; |
184 | |
185 | unless ($self->stop) { |
9c8a33b3 |
186 | $self->exit_code($self->ERROR); |
5b9ebe08 |
187 | $self->status_message('Restart (Stop) failed : ' . $@); |
188 | } |
189 | |
190 | unless ($self->start) { |
9c8a33b3 |
191 | $self->exit_code($self->ERROR); |
5b9ebe08 |
192 | $self->status_message('Restart (Start) failed : ' . $@); |
193 | } |
194 | |
9c8a33b3 |
195 | if ($self->exit_code == $self->OK) { |
196 | $self->exit_code($self->OK); |
92cf56b7 |
197 | $self->status_message("Restart successful"); |
198 | } |
5b9ebe08 |
199 | |
200 | return !($self->exit_code); |
4327fe98 |
201 | } |
202 | |
b916501e |
203 | # Make _kill *really* private |
204 | my $_kill; |
205 | |
3543c999 |
206 | sub stop { |
5b9ebe08 |
207 | my $self = shift; |
208 | |
209 | $self->clear_status_message; |
210 | $self->clear_exit_code; |
211 | |
212 | # if the pid is not running |
213 | # then we dont need to stop |
214 | # anything ... |
215 | if ($self->pidfile->is_running) { |
216 | |
217 | # if we are foreground, then |
218 | # no need to try and kill |
219 | # ourselves |
220 | unless ($self->foreground) { |
221 | |
222 | # kill the process ... |
223 | eval { $self->$_kill($self->pidfile->pid) }; |
224 | # and complain if we can't ... |
225 | if ($@) { |
9c8a33b3 |
226 | $self->exit_code($self->ERROR); |
5b9ebe08 |
227 | $self->status_message('Stop failed : ' . $@); |
228 | } |
229 | # or gloat if we succeed .. |
230 | else { |
9c8a33b3 |
231 | $self->exit_code($self->OK); |
5b9ebe08 |
232 | $self->status_message('Stop succeeded'); |
233 | } |
234 | |
235 | } |
5b9ebe08 |
236 | } |
237 | else { |
238 | # this just returns the OK |
239 | # exit code for now, but |
240 | # we should make this overridable |
1d85c76d |
241 | $self->exit_code($self->OK); |
5b9ebe08 |
242 | $self->status_message("Not running"); |
243 | } |
244 | |
245 | # if we are returning to our script |
246 | # then we actually need the opposite |
247 | # of what the system/OS expects |
248 | return !($self->exit_code); |
3543c999 |
249 | } |
250 | |
b916501e |
251 | $_kill = sub { |
a4952679 |
252 | my ( $self, $pid ) = @_; |
2361a590 |
253 | return unless $pid; |
3543c999 |
254 | unless ( CORE::kill 0 => $pid ) { |
3543c999 |
255 | # warn "$pid already appears dead."; |
256 | return; |
257 | } |
258 | |
259 | if ( $pid eq $$ ) { |
4a24225a |
260 | die "$pid is us! Can't commit suicide."; |
a4952679 |
261 | } |
262 | |
b916501e |
263 | my $timeout = $self->stop_timeout; |
a4952679 |
264 | |
b916501e |
265 | # kill 0 => $pid returns 0 if the process is dead |
266 | # $!{EPERM} could also be true if we cant kill it (permission error) |
a4952679 |
267 | |
b916501e |
268 | # Try SIGINT ... 2s ... SIGTERM ... 2s ... SIGKILL ... 3s ... UNDEAD! |
b6b69aca |
269 | my $terminating_signal; |
b916501e |
270 | for ( [ 2, $timeout ], [15, $timeout], [9, $timeout * 1.5] ) { |
ea9485d8 |
271 | my ($signal, $timeout) = @$_; |
272 | $timeout = int $timeout; |
5b9ebe08 |
273 | |
ea9485d8 |
274 | CORE::kill($signal, $pid); |
5b9ebe08 |
275 | |
ea9485d8 |
276 | while ($timeout) { |
b6b69aca |
277 | unless(CORE::kill 0 => $pid or $!{EPERM}) { |
278 | $terminating_signal = $signal; |
279 | last; |
280 | } |
ea9485d8 |
281 | $timeout--; |
b6b69aca |
282 | sleep(1) if $timeout; |
ea9485d8 |
283 | } |
b6b69aca |
284 | |
285 | last if $terminating_signal; |
a4952679 |
286 | } |
287 | |
b6b69aca |
288 | if($terminating_signal) { |
289 | if($terminating_signal == 9) { |
290 | # clean up the pidfile ourselves iff we used -9 and it worked |
291 | warn "Had to resort to 'kill -9' and it worked, wiping pidfile"; |
292 | eval { $self->pidfile->remove }; |
293 | if ($@) { |
294 | warn "Could not remove pidfile (" |
295 | . $self->pidfile->file |
296 | . ") because : $!"; |
297 | } |
298 | } |
299 | return; |
300 | } |
b916501e |
301 | |
302 | # IF it is still running |
d9e417f4 |
303 | Carp::carp "$pid doesn't seem to want to die."; # AHH EVIL DEAD! |
b916501e |
304 | }; |
a4952679 |
305 | |
306 | 1; |
307 | __END__ |
308 | |
8ac4733f |
309 | =pod |
310 | |
a4952679 |
311 | =head1 NAME |
312 | |
892a51e6 |
313 | MooseX::Daemonize - Role for daemonizing your Moose based application |
a4952679 |
314 | |
315 | =head1 VERSION |
316 | |
4327fe98 |
317 | This document describes MooseX::Daemonize version 0.05 |
a4952679 |
318 | |
319 | =head1 SYNOPSIS |
320 | |
4327fe98 |
321 | package My::Daemon; |
a4952679 |
322 | use Moose; |
5b9ebe08 |
323 | |
a4952679 |
324 | with qw(MooseX::Daemonize); |
5b9ebe08 |
325 | |
4327fe98 |
326 | # ... define your class .... |
5b9ebe08 |
327 | |
328 | after start => sub { |
4327fe98 |
329 | my $self = shift; |
330 | return unless $self->is_daemon; |
331 | # your daemon code here ... |
332 | }; |
a4952679 |
333 | |
5b9ebe08 |
334 | # then in your script ... |
335 | |
4327fe98 |
336 | my $daemon = My::Daemon->new_with_options(); |
5b9ebe08 |
337 | |
4327fe98 |
338 | my ($command) = @{$daemon->extra_argv} |
339 | defined $command || die "No command specified"; |
5b9ebe08 |
340 | |
341 | $daemon->start if $command eq 'start'; |
342 | $daemon->status if $command eq 'status'; |
343 | $daemon->restart if $command eq 'restart'; |
344 | $daemon->stop if $command eq 'stop'; |
345 | |
92cf56b7 |
346 | warn($daemon->status_message); |
5b9ebe08 |
347 | exit($daemon->exit_code); |
348 | |
a4952679 |
349 | =head1 DESCRIPTION |
350 | |
b916501e |
351 | Often you want to write a persistant daemon that has a pid file, and responds |
5b9ebe08 |
352 | appropriately to Signals. This module provides a set of basic roles as an |
4327fe98 |
353 | infrastructure to do that. |
a4952679 |
354 | |
05b96f4d |
355 | =head1 CAVEATS |
356 | |
357 | When going into background MooseX::Daemonize closes all open file |
358 | handles. This may interfere with you logging because it may also close the log |
359 | file handle you want to write to. To prevent this you can either defer opening |
360 | the log file until after start. Alternatively, use can use the |
361 | 'dont_close_all_files' option either from the command line or in your .sh |
362 | script. |
363 | |
364 | Assuming you want to use Log::Log4perl for example you could expand the |
365 | MooseX::Daemonize example above like this. |
366 | |
367 | after start => sub { |
368 | my $self = shift; |
369 | return unless $self->is_daemon; |
370 | Log::Log4perl->init(\$log4perl_config); |
371 | my $logger = Log::Log4perl->get_logger(); |
372 | $logger->info("Daemon started"); |
373 | # your daemon code here ... |
374 | }; |
375 | |
376 | |
a4952679 |
377 | =head1 ATTRIBUTES |
378 | |
4327fe98 |
379 | This list includes attributes brought in from other roles as well |
380 | we include them here for ease of documentation. All of these attributes |
5b9ebe08 |
381 | are settable though L<MooseX::Getopt>'s command line handling, with the |
4327fe98 |
382 | exception of C<is_daemon>. |
383 | |
a4952679 |
384 | =over |
385 | |
4327fe98 |
386 | =item I<progname Path::Class::Dir | Str> |
a4952679 |
387 | |
4327fe98 |
388 | The name of our daemon, defaults to C<$package_name =~ s/::/_/>; |
a4952679 |
389 | |
4327fe98 |
390 | =item I<pidbase Path::Class::Dir | Str> |
a4952679 |
391 | |
1d85c76d |
392 | The base for our PID, defaults to C</var/run/> |
a4952679 |
393 | |
4327fe98 |
394 | =item I<pidfile MooseX::Daemonize::Pid::File | Str> |
a4952679 |
395 | |
1d85c76d |
396 | The file we store our PID in, defaults to C<$pidbase/$progname.pid> |
a4952679 |
397 | |
4327fe98 |
398 | =item I<foreground Bool> |
a4952679 |
399 | |
5b9ebe08 |
400 | If true, the process won't background. Useful for debugging. This option can |
b916501e |
401 | be set via Getopt's -f. |
a4952679 |
402 | |
2ecc2ccb |
403 | =item I<no_double_fork Bool> |
404 | |
405 | If true, the process will not perform the typical double-fork, which is extra |
406 | added protection from your process accidentally aquiring a controlling terminal. |
407 | More information can be found by Googling "double fork daemonize". |
408 | |
409 | =item I<ignore_zombies Bool> |
410 | |
411 | If true, the process will not clean up zombie processes. |
412 | Normally you don't want this. |
413 | |
414 | =item I<dont_close_all_files Bool> |
415 | |
416 | If true, the objects open filehandles will not be closed when daemonized. |
417 | Normally you don't want this. |
418 | |
419 | |
4327fe98 |
420 | =item I<is_daemon Bool> |
421 | |
5b9ebe08 |
422 | If true, the process is the backgrounded daemon process, if false it is the |
423 | parent process. This is useful for example in an C<after 'start' => sub { }> |
424 | block. |
e7a196e7 |
425 | |
4327fe98 |
426 | B<NOTE:> This option is explicitly B<not> available through L<MooseX::Getopt>. |
b916501e |
427 | |
4327fe98 |
428 | =item I<stop_timeout> |
b916501e |
429 | |
430 | Number of seconds to wait for the process to stop, before trying harder to kill |
4327fe98 |
431 | it. Defaults to 2 seconds. |
e7a196e7 |
432 | |
a4952679 |
433 | =back |
434 | |
5b9ebe08 |
435 | These are the internal attributes, which are not available through MooseX::Getopt. |
436 | |
437 | =over 4 |
438 | |
439 | =item I<exit_code Int> |
440 | |
92cf56b7 |
441 | =item I<status_message Str> |
5b9ebe08 |
442 | |
443 | =back |
444 | |
445 | =head1 METHODS |
a4952679 |
446 | |
4327fe98 |
447 | =head2 Daemon Control Methods |
448 | |
5b9ebe08 |
449 | These methods can be used to control the daemon behavior. Every effort |
450 | has been made to have these methods DWIM (Do What I Mean), so that you |
451 | can focus on just writing the code for your daemon. |
a4952679 |
452 | |
5b9ebe08 |
453 | Extending these methods is best done with the L<Moose> method modifiers, |
4327fe98 |
454 | such as C<before>, C<after> and C<around>. |
455 | |
456 | =over 4 |
457 | |
458 | =item B<start> |
a4952679 |
459 | |
460 | Setup a pidfile, fork, then setup the signal handlers. |
461 | |
4327fe98 |
462 | =item B<stop> |
a4952679 |
463 | |
464 | Stop the process matching the pidfile, and unlinks the pidfile. |
465 | |
4327fe98 |
466 | =item B<restart> |
a4952679 |
467 | |
4327fe98 |
468 | Literally this is: |
a4952679 |
469 | |
470 | $self->stop(); |
471 | $self->start(); |
472 | |
5b9ebe08 |
473 | =item B<status> |
474 | |
92cf56b7 |
475 | =item B<shutdown> |
476 | |
4327fe98 |
477 | =back |
478 | |
5b9ebe08 |
479 | |
4327fe98 |
480 | =head2 Pidfile Handling Methods |
481 | |
482 | =over 4 |
483 | |
484 | =item B<init_pidfile> |
485 | |
486 | This method will create a L<MooseX::Daemonize::Pid::File> object and tell |
487 | it to store the PID in the file C<$pidbase/$progname.pid>. |
488 | |
489 | =item B<check> |
490 | |
5b9ebe08 |
491 | This checks to see if the daemon process is currently running by checking |
4327fe98 |
492 | the pidfile. |
a4952679 |
493 | |
4327fe98 |
494 | =item B<get_pid> |
a4952679 |
495 | |
4327fe98 |
496 | Returns the PID of the daemon process. |
a4952679 |
497 | |
4327fe98 |
498 | =item B<save_pid> |
a4952679 |
499 | |
4327fe98 |
500 | Write the pidfile. |
501 | |
502 | =item B<remove_pid> |
503 | |
504 | Removes the pidfile. |
505 | |
506 | =back |
507 | |
508 | =head2 Signal Handling Methods |
509 | |
510 | =over 4 |
511 | |
512 | =item B<setup_signals> |
513 | |
5b9ebe08 |
514 | Setup the signal handlers, by default it only sets up handlers for SIGINT and |
4327fe98 |
515 | SIGHUP. If you wish to add more signals just use the C<after> method modifier |
516 | and add them. |
517 | |
518 | =item B<handle_sigint> |
a4952679 |
519 | |
cecbee2d |
520 | Handle a INT signal, by default calls C<$self->stop()> |
a4952679 |
521 | |
4327fe98 |
522 | =item B<handle_sighup> |
a4952679 |
523 | |
cecbee2d |
524 | Handle a HUP signal. By default calls C<$self->restart()> |
a4952679 |
525 | |
4327fe98 |
526 | =back |
527 | |
9c8a33b3 |
528 | =head2 Exit Code Methods |
529 | |
530 | These are overriable constant methods used for setting the exit code. |
531 | |
532 | =over 4 |
533 | |
534 | =item OK |
535 | |
536 | Returns 0. |
537 | |
538 | =item ERROR |
539 | |
540 | Returns 1. |
541 | |
542 | =back |
543 | |
4327fe98 |
544 | =head2 Introspection |
545 | |
546 | =over 4 |
547 | |
a4952679 |
548 | =item meta() |
549 | |
cecbee2d |
550 | The C<meta()> method from L<Class::MOP::Class> |
a4952679 |
551 | |
552 | =back |
553 | |
554 | =head1 DEPENDENCIES |
555 | |
4327fe98 |
556 | L<Moose>, L<MooseX::Getopt>, L<MooseX::Types::Path::Class> and L<POSIX> |
a4952679 |
557 | |
558 | =head1 INCOMPATIBILITIES |
559 | |
4327fe98 |
560 | None reported. Although obviously this will not work on Windows. |
a4952679 |
561 | |
562 | =head1 BUGS AND LIMITATIONS |
563 | |
a4952679 |
564 | No bugs have been reported. |
565 | |
566 | Please report any bugs or feature requests to |
567 | C<bug-acme-dahut-call@rt.cpan.org>, or through the web interface at |
568 | L<http://rt.cpan.org>. |
569 | |
570 | =head1 SEE ALSO |
571 | |
92b4ca7e |
572 | L<Daemon::Control>, L<Proc::Daemon>, L<Daemon::Generic> |
a4952679 |
573 | |
92cf56b7 |
574 | =head1 AUTHORS |
a4952679 |
575 | |
69186a48 |
576 | Chris Prather C<< <chris@prather.org >> |
a4952679 |
577 | |
92cf56b7 |
578 | Stevan Little C<< <stevan.little@iinteractive.com> >> |
579 | |
7ada91b8 |
580 | =head1 THANKS |
581 | |
5b9ebe08 |
582 | Mike Boyko, Matt S. Trout, Stevan Little, Brandon Black, Ash Berlin and the |
b916501e |
583 | #moose denzians |
a4952679 |
584 | |
637573c4 |
585 | Some bug fixes sponsored by Takkle Inc. |
586 | |
a4952679 |
587 | =head1 LICENCE AND COPYRIGHT |
588 | |
05b96f4d |
589 | Copyright (c) 2007-2011, Chris Prather C<< <chris@prather.org> >>. Some rights |
b916501e |
590 | reserved. |
a4952679 |
591 | |
592 | This module is free software; you can redistribute it and/or |
593 | modify it under the same terms as Perl itself. See L<perlartistic>. |
594 | |
a4952679 |
595 | =head1 DISCLAIMER OF WARRANTY |
596 | |
597 | BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
598 | FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
599 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
600 | PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER |
601 | EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
602 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE |
603 | ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH |
604 | YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL |
605 | NECESSARY SERVICING, REPAIR, OR CORRECTION. |
606 | |
607 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
608 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
609 | REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE |
610 | LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, |
611 | OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE |
612 | THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING |
613 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A |
614 | FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF |
615 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
616 | SUCH DAMAGES. |
8ac4733f |
617 | |
618 | =cut |