File Coverage

blib/lib/Sirc/LimitMan.pm
Criterion Covered Total %
statement 15 80 18.7
branch 0 38 0.0
condition 0 9 0.0
subroutine 5 13 38.4
pod 0 6 0.0
total 20 146 13.7


line stmt bran cond sub pod time code
1             # $Id: LimitMan.pm,v 1.6 2000-07-27 12:04:46-04 roderick Exp $
2             #
3             # Copyright (c) 2000 Roderick Schertler. All rights reserved. This
4             # program is free software; you can redistribute it and/or modify it
5             # under the same terms as Perl itself.
6             #
7             # Documentation is at the __END__.
8              
9 1     1   568 use strict;
  1         2  
  1         41  
10              
11             package Sirc::LimitMan;
12              
13 1     1   5 use Sirc::Chantrack qw(%Chan_limit %Chan_user);
  1         2  
  1         90  
14 1     1   4 use Sirc::LckHash ();
  1         2  
  1         32  
15 1         114 use Sirc::Util qw(addcmd add_hook arg_count_error by_server deltimer
16             have_ops_q ieq newtimer optional_channel
17             settable_boolean settable_int sl
18             tell_error tell_question timer
19 1     1   11 xtell);
  1         2  
20              
21 1     1   5 use vars qw($VERSION);
  1         2  
  1         1456  
22              
23             $VERSION = do{my@r=q$Revision: 1.6 $=~/\d+/g;sprintf '%d.'.'%03d'x$#r,@r};
24             $VERSION .= '-l' if q$Locker: $ =~ /: \S/;
25              
26             # These variables are tied to /set options. XXX Allow setting these
27             # per-channel.
28             my $Enabled = 1; # no limit management done if false
29             my $Tween = 60; # secs between checks
30             my $Skew = $Tween / 2; # random skew if not master
31             my $Skew_offset = $Skew / 2; # constant added to skew
32             my $Debug = 0;
33              
34             my $N_low = 2;
35             my $N_high = 5;
36             my $N_reset = $N_high - 1;
37              
38             settable_boolean 'limitman', \$Enabled;
39             settable_int 'limitman_tween', \$Tween, sub { $_[1] > 0 };
40             settable_int 'limitman_skew', \$Skew, sub { $_[1] > 0 };
41             settable_int 'limitman_skew_offset', \$Skew_offset, sub { $_[1] > 0 };
42             settable_int 'limitman_low', \$N_low, sub { $_[1] > 0 };
43             settable_int 'limitman_high', \$N_high, sub { $_[1] > 0 };
44             settable_int 'limitman_reset', \$N_reset, sub { $_[1] > 0 };
45              
46             settable_boolean 'limitman_debug', \$Debug;
47              
48             # Keys are channels I'm doing limit management for, values are lists:
49             #
50             # 0 timer
51             # 1 true if I'm the master
52              
53             sub F_TIMER () { 0 }
54             sub F_AM_MASTER () { 1 }
55              
56             sub TIMER_NOW () { 0 }
57             sub TIMER_MASTER () { 1 }
58             sub TIMER_SLAVE () { 2 }
59              
60             my %Limitman;
61             tie %Limitman, 'Sirc::LckHash';
62              
63             sub debug {
64 0 0   0 0   xtell 'limitman debug ' . join '', @_
65             if $Debug;
66             }
67              
68             # So I can examine the value.
69              
70             sub debug_fetch {
71 0     0 0   return \%Limitman;
72             }
73              
74             sub am_master {
75 0     0 0   my ($c) = @_;
76 0   0       return $Limitman{$c} && $Limitman{$c}[F_AM_MASTER];
77             }
78              
79             sub set_timer {
80 0 0   0 0   unless (@_ == 2) {
81 0           arg_count_error undef, 2, @_;
82 0           return;
83             }
84 0           my ($c, $type) = @_;
85 0           my ($delay);
86              
87 0 0         if ($type == TIMER_NOW) {
    0          
    0          
88 0           $delay = 3;
89             }
90             elsif ($type == TIMER_MASTER) {
91 0           $delay = $Tween;
92             }
93             elsif ($type == TIMER_SLAVE) {
94 0           $delay = $Tween + $Skew_offset + 1 + int rand $Skew;
95             }
96             else {
97 0           tell_error "set_timer called with invalid timer type $type";
98 0           return;
99             }
100              
101 0           debug "setting timer for $c type $type at now + $delay";
102 0   0 0     timer $delay, sub { limitman_do($c) }, $Limitman{$c}[F_TIMER] ||= newtimer;
  0            
103             }
104              
105             sub limitman_do {
106 0     0 0   my ($c) = @_;
107              
108 0           my $set = 0;
109 0           my $limit = $Chan_limit{$c};
110 0           my $users = keys %{ $Chan_user{$c} };
  0            
111              
112 0 0         if (!$Enabled) {
    0          
    0          
    0          
113 0           debug "not enabled";
114             }
115             elsif (!$Limitman{$c}) {
116 0           tell_error "limitman_do called for $c even though disabled";
117             # Don't set the timer again.
118 0           return;
119             }
120             elsif (!have_ops_q $c) {
121 0           debug "$c not opped";
122             }
123             elsif (!defined $limit) {
124 0           debug "$c no current limit";
125 0           $set = 1;
126             }
127             else {
128 0           my $room = $limit - $users;
129 0           debug "$c limit $limit users $users room $room low $N_low high $N_high";
130 0 0 0       $set = 1 if $room < $N_low || $room > $N_high;
131             }
132              
133 0 0         if ($set) {
134 0           my $new = $users + $N_reset;
135 0           debug "new limit on $c is $new";
136 0           sl "MODE $c +l $new";
137 0           $Limitman{$c}[F_AM_MASTER] = 1;
138             }
139              
140             # Use the standard delay here, even for slaves. The slaves only
141             # pick a new random time when they see a master change the limit, so
142             # that they stay at a certain offset from the master (so they don't
143             # creep around and take over inadvertently).
144 0           set_timer $c, TIMER_MASTER;
145             }
146              
147             # Hook called when somebody changes the limit.
148              
149             sub limit_change {
150 0     0 0   my ($c, $old, $new) = @_;
151              
152 0 0         return if !$Limitman{$c};
153 0 0         return if ieq $::who, $::nick;
154              
155 0           debug "limit change on $c, $old => $new";
156 0 0         if (by_server) {
157 0 0         if (am_master $c) {
158 0           debug "resetting after limit change made by server";
159 0           set_timer $c, TIMER_NOW;
160             }
161             else {
162 0           debug "ignoring server change, I'm not master";
163             }
164             }
165             else {
166             # Somebody else changed it. Let them be the master.
167 0 0         if (am_master $c) {
168 0           debug "abdicating master position";
169 0           $Limitman{$c}[F_AM_MASTER] = 0;
170             }
171             # Regardless of whether I was the master, re-start my timer to
172             # sync to the new master.
173 0           set_timer $c, TIMER_SLAVE;
174             }
175             }
176             add_hook 'limit', \&limit_change;
177              
178             sub main::cmd_limitman {
179 0 0   0     optional_channel or return;
180              
181 0           my @a = split ' ', $::args;
182 0 0         unless (@a == 1) {
183 0           tell_question "Too many args, 0 or 1 expected";
184 0           return;
185             }
186              
187 0           my $c = shift @a;
188 0 0         if ($Limitman{$c}) {
189 0           xtell "Disabling limit managment on $c";
190 0           deltimer ${ delete $Limitman{$c} }[F_TIMER];
  0            
191             }
192             else {
193 0           xtell "Starting limit management on $c";
194 0           $Limitman{$c} = [];
195 0           limitman_do $c;
196             }
197             }
198             addcmd 'limitman';
199              
200             1;
201              
202             __END__