File Coverage

blib/lib/Pquota.pm
Criterion Covered Total %
statement 86 405 21.2
branch 17 144 11.8
condition 6 96 6.2
subroutine 13 35 37.1
pod 26 27 96.3
total 148 707 20.9


line stmt bran cond sub pod time code
1             #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>#
2             # Pquota.pm #
3             # #
4             # written by david bonner and scott savarese #
5             # dbonner@cs.bu.edu #
6             # savarese@cs.bu.edu #
7             # theft is treason, citizen #
8             # #
9             # copyright(c) 1999 david bonner, scott savarese. all rights reserved. #
10             # this program is free software; you can redistribute and/or modify it #
11             # under the same terms as perl itself #
12             #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<#
13              
14             package Pquota;
15              
16             # make CPAN happy
17             $Pquota::VERSION = 1.1;
18              
19 1     1   29559 use Fcntl;
  1         3  
  1         1014  
20 1     1   1042 use MLDBM;
  1         7359  
  1         14  
21 1     1   46 use Carp;
  1         13  
  1         286  
22 1     1   6 use strict;
  1         2  
  1         14651  
23              
24             # constant for unlimited printer use
25 0     0 0 0 sub UNLIMITED { return 23000000; }
26              
27              
28             #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>#
29             # object methods #
30             #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<#
31              
32              
33             ## object creation
34             sub new {
35 1     1 1 14 my $proto = shift;
36 1   33     10 my $class = ref($proto) || $proto;
37 1         3 my $self = {};
38              
39 1         3 bless $self, $class;
40 1         9 $self->_init(@_);
41              
42 1         5 return $self;
43             }
44              
45              
46             ## close the objct by untie'ing the hashes
47             sub close {
48 0     0 1 0 my $self = shift;
49              
50             # loop through and close all tied dbms
51 0         0 for (keys %{$self->{'dbms'}}) {
  0         0  
52 0         0 untie %{$self->{'dbms'}{$_}};
  0         0  
53             }
54              
55 0         0 return 1;
56             }
57              
58              
59             #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>#
60             # printer database commands #
61             #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<#
62              
63              
64             ## adds a printer to the printers dbm
65             sub printer_add {
66 2     2 1 512 my ($self, $printer, $cost, $dbm) = @_;
67 2         5 my $entry = {};
68              
69             # sanity check
70 2 50 33     27 unless ($printer && defined($cost) && $dbm) {
      33        
71 0         0 carp "Invocation: \$pquota->printer_add (\$printer, \$page_cost, \$user_db)";
72 0         0 return undef;
73             }
74              
75             # initialize the hash values
76 2         5 $entry->{'cost'} = $cost;
77 2         5 $entry->{'dbm'} = $dbm;
78              
79             # get the lock
80 2         8 $self->_get_lock ('_printers');
81              
82             # store it in the hash
83 2         17 $self->{'dbms'}{'_printers'}{$printer} = $entry;
84              
85             # release the lock
86 2         290 $self->_release_lock ('_printers');
87              
88 2         8 return 1;
89             }
90              
91              
92             ## removes a printer from the printers dbm
93             sub printer_rm {
94 1     1 1 250 my ($self, $printer) = @_;
95              
96             # sanity check on the arg passed in
97 1 50       5 unless ($printer) {
98 0         0 carp "Invocation: \$pquota->printer_rm (\$printer)";
99 0         0 return undef;
100             }
101              
102             # check to see if it was there in the first place
103 1 50       11 unless (defined ($self->{'dbms'}{'_printers'}{$printer})) {
104 0         0 carp "Pquota::printer_rm: $printer not in the _printers database";
105 0         0 return undef;
106             }
107              
108             # get the lock
109 1         172 $self->_get_lock ('_printers');
110              
111             # remove the printer from the dbm
112 1         8 delete ($self->{'dbms'}{'_printers'}{$printer});
113              
114             # release the lock
115 1         24 $self->_release_lock ('_printers');
116              
117 1         8 return 1;
118             }
119              
120              
121             ## changes the cost per page of the printer
122             sub printer_set_cost {
123 0     0 1 0 my ($self, $printer, $cost) = @_;
124 0         0 my $entry;
125              
126             # sanity check
127 0 0 0     0 unless ($printer && defined($cost)) {
128 0         0 carp "Invocation: \$pquota->printer_set_cost (\$printer, \$cost)";
129 0         0 return undef;
130             }
131              
132             # make sure the printer exists
133 0 0       0 unless (defined ($self->{'dbms'}{'_printers'}{$printer})) {
134 0         0 carp "Pquota::printer_set_cost: $printer not in _printers database";
135             }
136              
137             # get the entry
138 0         0 $entry = $self->{'dbms'}{'_printers'}{$printer};
139            
140             # get the lock
141 0         0 $self->_get_lock ('_printers');
142              
143             # change the value
144 0         0 $entry->{'cost'} = $cost;
145 0         0 $self->{'dbms'}{'_printers'}{$printer} = $entry;
146              
147             # release the lock
148 0         0 $self->_release_lock ('_printers');
149              
150 0         0 return 1;
151             }
152              
153              
154             ## returns the cost per page of the printer
155             sub printer_get_cost {
156 0     0 1 0 my ($self, $printer) = @_;
157 0         0 my $entry;
158              
159             # sanity check
160 0 0       0 unless ($printer) {
161 0         0 carp "Invocation: \$pquota->printer_get_cost (\$printer)";
162 0         0 return undef;
163             }
164              
165             # make sure the printer exists
166 0 0       0 unless (defined ($self->{'dbms'}{'_printers'}{$printer})) {
167 0         0 carp "Pquota::printer_get_cost: $printer not in _printers database";
168 0         0 return undef;
169             }
170              
171             # get printer entry
172 0         0 $entry = $self->{'dbms'}{'_printers'}{$printer};
173              
174             # return the cost
175 0         0 return $entry->{'cost'};
176             }
177              
178              
179             ## list all the printers and their per-page cost
180             sub printer_get_cost_list {
181 0     0 1 0 my $self = shift;
182 0         0 my $ref = {};
183 0         0 my $entry;
184              
185             # loop through printer hash, store costs in $ref
186 0         0 for (keys (%{$self->{'dbms'}{'_printers'}})) {
  0         0  
187 0         0 $entry = $self->{'dbms'}{'_printers'}{$_};
188 0         0 $ref->{$_} = $entry->{'cost'};
189             }
190              
191 0         0 return $ref;
192             }
193              
194              
195             ## set the user database that a printer uses
196             sub printer_set_user_database {
197 0     0 1 0 my ($self, $printer, $dbm) = @_;
198 0         0 my $entry;
199              
200             # sanity check
201 0 0 0     0 unless ($printer && $dbm) {
202 0         0 carp "Invocation: \$pquota->printer_set_user_database (\$printer, \$database)";
203 0         0 return undef;
204             }
205              
206             # make sure printer is in database
207 0 0       0 unless (defined ($self->{'dbms'}{'_printers'}{$printer})) {
208 0         0 carp "Pquota::printer_set_user_database: $printer not in _printers database";
209 0         0 return undef;
210             }
211              
212             # get printer entry
213 0         0 $entry = $self->{'dbms'}{'_printers'}{$printer};
214              
215             # get file lock
216 0         0 $self->_get_lock ('_printers');
217              
218             # set the dbm field
219 0         0 $entry->{'dbm'} = $dbm;
220 0         0 $self->{'dbms'}{'_printers'}{$printer} = $entry;
221              
222             # release file lock
223 0         0 $self->_release_lock ('_printers');
224              
225 0         0 return 1;
226             }
227              
228              
229             ## gets the user database for a printer
230             sub printer_get_user_database {
231 0     0 1 0 my ($self, $printer) = @_;
232 0         0 my $entry;
233              
234             # sanity check
235 0 0       0 unless ($printer) {
236 0         0 carp "Invocation: \$pquota->printer_get_user_database (\$printer)";
237 0         0 return undef;
238             }
239              
240             # make sure printer exists
241 0 0       0 unless (defined ($self->{'dbms'}{'_printers'}{$printer})) {
242 0         0 carp "Pquota::printer_get_user_database: $printer not in _printers database";
243 0         0 return undef;
244             }
245              
246             # get printer entry
247 0         0 $entry = $self->{'dbms'}{'_printers'}{$printer};
248              
249             # return the user database
250 0         0 return $entry->{'dbm'};
251             }
252              
253              
254             ## list all the printers and the user databases they use
255             sub printer_get_user_database_list {
256 0     0 1 0 my $self = shift;
257 0         0 my $ref = {};
258 0         0 my $entry;
259              
260             # loop through printer hash, store dbms in $ref
261 0         0 for (keys (%{$self->{'dbms'}{'_printers'}})) {
  0         0  
262 0         0 $entry = $self->{'dbms'}{'_printers'}{$_};
263 0         0 $ref->{$_} = $entry->{'dbm'};
264             }
265              
266 0         0 return $ref;
267             }
268              
269              
270             ## set an arbitrary field in a printer entry
271             sub printer_set_field {
272 0     0 1 0 my ($self, $printer, $key, $val) = @_;
273 0         0 my $entry;
274              
275             # sanity check, allow for empty value, but not an undefined one
276 0 0 0     0 unless ($printer && $key && defined ($val)) {
      0        
277 0         0 carp "Invocation: \$pquota->printer_set_field (\$printer, \$key, \$value)";
278 0         0 return undef;
279             }
280              
281             # make sure printer exists
282 0 0       0 unless (defined ($self->{'dbms'}{'_printers'}{$printer})) {
283 0         0 carp "Pquota::printer_set_field: $printer not in _printers database";
284 0         0 return undef;
285             }
286              
287             # get the printer entry
288 0         0 $entry = $self->{'dbms'}{'_printers'}{$printer};
289              
290             # get file lock
291 0         0 $self->_get_lock ('_printers');
292              
293             # set the field
294 0         0 $entry->{$key} = $val;
295 0         0 $self->{'dbms'}{'_printers'}{$printer} = $entry;
296              
297             # release file lock
298 0         0 $self->_release_lock ('_printers');
299              
300 0         0 return 1;
301             }
302              
303              
304             ## get an arbitrary field in a printer entry
305             sub printer_get_field {
306 0     0 1 0 my ($self, $printer, $key);
307 0         0 my $entry;
308              
309             # sanity check
310 0 0 0     0 unless ($printer && $key) {
311 0         0 carp "Invocation: \$pquota->printer_get_field (\$printer, \$key)";
312 0         0 return undef;
313             }
314              
315             # make sure printer exists
316 0 0       0 unless (defined ($self->{'dbms'}{'_printers'}{$printer})) {
317 0         0 carp "Pquota::printer_get_field: $printer not in _printers database";
318 0         0 return undef;
319             }
320              
321             # get the printer entry
322 0         0 $entry = $self->{'dbms'}{'_printers'}{$printer};
323              
324             # return the field
325 0         0 return $entry->{$key};
326             }
327              
328              
329             #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>#
330             # user database methods #
331             #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<#
332              
333              
334             ## add user to a user dbm
335             sub user_add {
336 1     1 1 255 my ($self, $user, $dbm, $periodic) = @_;
337 1         3 my $entry = {};
338              
339             # sanity check
340 1 50 33     15 unless ($dbm && $user && defined($periodic)) {
      33        
341 0         0 carp "Invocation: \$pquota->user_add (\$user, \$database, \$periodic)";
342 0         0 return undef;
343             }
344              
345             # make sure the dbm is open
346 1 50       5 unless ($self->_open_dbm ($dbm)) {
347 0         0 return undef;
348             }
349              
350             # make sure they don't already have an entry
351 1 50       29 if (defined ($self->{'dbms'}{$dbm}{$user})) {
352 0         0 carp "Pquota::user_add: $user already exists in the users database";
353 0         0 return undef;
354             }
355              
356             # set the default values for this user
357 1 50       27 if ($periodic eq 'unlimited') {
358 0         0 $periodic = UNLIMITED();
359             }
360 1         4 $entry->{'periodic'} = $periodic;
361 1         3 $entry->{'current'} = $periodic;
362 1         3 $entry->{'total'} = 0;
363              
364              
365             # get the lock file
366 1         3 $self->_get_lock ($dbm);
367              
368             # store it in the hash
369 1         8 $self->{'dbms'}{$dbm}{$user} = $entry;
370              
371             # release the lock file
372 1         117 $self->_release_lock ($dbm);
373            
374 1         3 return 1;
375             }
376              
377              
378             ## remove a user from a user database
379             sub user_rm {
380 1     1 1 198 my ($self, $user, $dbm) = @_;
381            
382             # sanity check
383 1 50 33     8 unless ($dbm && $user) {
384 0         0 carp "Invocation: \$pquota->user_rm (\$user, \$database)";
385 0         0 return undef;
386             }
387              
388             # make sure the dbm is open
389 1 50       3 unless ($self->_open_dbm ($dbm)) {
390 0         0 return undef;
391             }
392              
393             # make sure the user exists
394 1 50       6 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
395 0         0 carp "Pquota::user_rm: $user not in $dbm database";
396 0         0 return undef;
397             }
398              
399             # get the lock
400 1         117 $self->_get_lock ($dbm);
401            
402             # remove the user
403 1         6 delete ($self->{'dbms'}{$dbm}{$user});
404              
405             # relese the lock
406 1         21 $self->_release_lock ($dbm);
407              
408 1         3 return 1;
409             }
410              
411              
412             ## mark the appropriate amount as having been printed
413             sub user_print_pages {
414 0     0 1 0 my ($self, $user, $printer, $pages) = @_;
415 0         0 my ($user_entry, $printer_entry, $cost);
416 0         0 my $err = 0;
417              
418             # make sure we were called correctly
419 0 0 0     0 unless ($user && $printer && defined($pages)) {
      0        
420 0         0 carp "Invocation: \$pquota->user_print_pages (\$user, \$printer, \$pages)";
421 0         0 return undef;
422             }
423              
424             # make sure printer exists
425 0 0       0 unless (defined ($self->{'dbms'}{'_printers'}{$printer})) {
426 0         0 carp "Pquota::user_print_pages: $printer not in _printers database";
427 0         0 return undef;
428             }
429 0         0 $printer_entry = $self->{'dbms'}{'_printers'}{$printer};
430              
431             # make sure the user dbm is open
432 0 0       0 unless ($self->_open_dbm ($printer_entry->{'dbm'})) {
433 0         0 carp "Pquota::user_print_pages: Unable to open user database $printer_entry->{'dbm'}";
434 0         0 return undef;
435             }
436              
437             # make sure user exists
438 0 0       0 unless (defined ($self->{'dbms'}{$printer_entry->{'dbm'}}{$user})) {
439 0         0 carp "Pquota::user_print_pages: $user not in $printer_entry->{'dbm'} database";
440 0         0 return undef;
441             }
442            
443             # get the lock
444 0         0 $self->_get_lock ($printer_entry->{'dbm'});
445              
446             # mark off the appropriate amount
447 0         0 $user_entry = $self->{'dbms'}{$printer_entry->{'dbm'}}{$user};
448 0         0 $cost = $printer_entry->{'cost'} * $pages;
449              
450             # catch the over quota problem
451 0 0       0 if ($user_entry->{'current'} < $cost) {
452 0         0 carp "Pquota::user_print_pages: $user over quota";
453 0         0 $user_entry->{'current'} = 0;
454 0         0 $user_entry->{'total'} += $cost;
455 0         0 $self->{'dbms'}{$printer_entry->{'dbm'}}{$user} = $user_entry;
456 0         0 $self->_release_lock ($printer_entry->{'dbm'});
457 0         0 return undef;
458             }
459            
460             # update the values
461 0         0 $user_entry->{'current'} -= $cost;
462 0         0 $user_entry->{'total'} += $cost;
463              
464             # store it
465 0         0 $self->{'dbms'}{$printer_entry->{'dbm'}}{$user} = $user_entry;
466              
467             # release the lock
468 0         0 $self->_release_lock ($printer_entry->{'dbm'});
469              
470 0         0 return 1;
471             }
472              
473              
474             ## adds to user's current quota
475             sub user_add_to_current {
476 0     0 1 0 my ($self, $user, $dbm, $amt) = @_;
477 0         0 my $entry;
478              
479             # sanity check
480 0 0 0     0 unless ($dbm && $user && defined($amt)) {
      0        
481 0         0 carp "Invocation: \$pquota->user_add_to_current (\$user, \$database, \$amount)";
482 0         0 return undef;
483             }
484              
485             # make sure dbm is open
486 0 0       0 unless ($self->_open_dbm ($dbm)) {
487 0         0 return undef;
488             }
489              
490             # check for user in users database
491 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
492 0         0 carp "Pquota::user_add_to_current: $user not in users database";
493 0         0 return undef;
494             }
495              
496             # get the lock
497 0         0 $self->_get_lock ($dbm);
498              
499             # get user entry
500 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
501              
502             # modify the current field
503 0         0 $entry->{'current'} += $amt;
504              
505             # store the entry
506 0         0 $self->{'dbms'}{$dbm}{$user} = $entry;
507              
508             # release the lock
509 0         0 $self->_release_lock ($dbm);
510              
511 0         0 return 1;
512             }
513              
514              
515             ## sets a new current value for a user
516             sub user_set_current {
517 0     0 1 0 my ($self, $user, $dbm, $amt) = @_;
518 0         0 my $entry;
519              
520             # sanity check
521 0 0 0     0 unless ($dbm && $user && defined($amt)) {
      0        
522 0         0 carp "Invocation: \$pquota->user_set_current (\$user, \$database, \$amount)";
523 0         0 return undef;
524             }
525              
526             # make sure the dbm is open
527 0 0       0 unless ($self->_open_dbm ($dbm)) {
528 0         0 return undef;
529             }
530              
531             # make sure the user exists
532 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
533 0         0 carp "Pquota::user_set_current: $user not in users database";
534 0         0 return undef;
535             }
536              
537             # get the user entry
538 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
539              
540             # set the new value, reset the current value
541 0 0       0 if ($amt eq 'unlimited') {
542 0         0 $amt = UNLIMITED;
543             }
544 0         0 $entry->{'current'} = $amt;
545              
546             # get the lock
547 0         0 $self->_get_lock ($dbm);
548              
549             # store the entry
550 0         0 $self->{'dbms'}{$dbm}{$user} = $entry;
551              
552             # release the lock
553 0         0 $self->_release_lock ($dbm);
554              
555 0         0 return 1;
556             }
557              
558              
559             ## checks for the current quota of a specified user
560             sub user_get_current_by_dbm {
561 0     0 1 0 my ($self, $user, $dbm) = @_;
562 0         0 my $entry;
563              
564             # sanity check
565 0 0 0     0 unless ($dbm && $user) {
566 0         0 carp "Invocation: \$pquota->user_get_current_by_dbm (\$user, \$database)";
567 0         0 return undef;
568             }
569              
570             # make sure user dbm is open
571 0 0       0 unless ($self->_open_dbm ($dbm)) {
572 0         0 return undef;
573             }
574              
575             # make sure user is in database
576 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
577 0         0 carp "Pquota::user_get_current_by_dbm: $user not in $dbm database";
578 0         0 return undef;
579             }
580              
581             # get entry
582 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
583              
584 0         0 return $entry->{'current'};
585             }
586              
587              
588             ## checks for the current quota of a specified user
589             sub user_get_current_by_printer {
590 0     0 1 0 my ($self, $user, $printer) = @_;
591 0         0 my ($entry, $dbm);
592              
593             # sanity check
594 0 0 0     0 unless ($printer && $user) {
595 0         0 carp "Invocation: \$pquota->user_get_current_by_printer (\$user, \$printer)";
596 0         0 return undef;
597             }
598              
599             # find out which user database to use
600 0 0       0 unless ($dbm = $self->printer_get_user_database ($printer)) {
601 0         0 return undef;
602             }
603              
604             # make sure the dbm is open
605 0 0       0 unless ($self->_open_dbm($dbm)) {
606 0         0 return undef;
607             }
608              
609             # make sure user is in database
610 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
611 0         0 carp "Pquota::user_get_current_by_printer: $user not in $dbm database";
612 0         0 return undef;
613             }
614              
615             # get entry
616 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
617              
618 0         0 return $entry->{'current'};
619             }
620              
621              
622             ## resets the current value to the periodic value
623             sub user_reset_current {
624 0     0 1 0 my ($self, $user, $dbm) = @_;
625 0         0 my $entry;
626              
627             # sanity check
628 0 0 0     0 unless ($dbm && $user) {
629 0         0 carp "Invocation: \$pquota->user_reset current (\$user, \$database)";
630 0         0 return undef;
631             }
632              
633             # make sure the dbm is open
634 0 0       0 unless ($self->_open_dbm ($dbm)) {
635 0         0 return undef;
636             }
637              
638             # make sure the user exists
639 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
640 0         0 carp "Pquota::user_reset_current: $user not in users database";
641 0         0 return undef;
642             }
643              
644             # get the user entry
645 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
646              
647             # set the new value, reset the current value
648 0         0 $entry->{'current'} = $entry->{'periodic'};
649              
650             # get the lock
651 0         0 $self->_get_lock ($dbm);
652              
653             # store the entry
654 0         0 $self->{'dbms'}{$dbm}{$user} = $entry;
655              
656             # release the lock
657 0         0 $self->_release_lock ($dbm);
658              
659 0         0 return 1;
660             }
661              
662              
663              
664             ## adds to user's periodic quota
665             sub user_add_to_periodic {
666 0     0 1 0 my ($self, $user, $dbm, $amt) = @_;
667 0         0 my $entry;
668              
669             # sanity check
670 0 0 0     0 unless ($dbm && $user && defined($amt)) {
      0        
671 0         0 carp "Invocation: \$pquota->user_add_to_periodic (\$user, \$user_db, \$amount)";
672 0         0 return undef;
673             }
674              
675             # make sure dbm is open
676 0 0       0 unless ($self->_open_dbm ($dbm)) {
677 0         0 return undef;
678             }
679              
680             # check for user in users database
681 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
682 0         0 carp "Pquota::user_add_to_periodic: $user not in users database";
683 0         0 return undef;
684             }
685              
686             # get the lock
687 0         0 $self->_get_lock ($dbm);
688              
689             # get user entry
690 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
691              
692             # modify the periodic field
693 0         0 $entry->{'periodic'} += $amt;
694              
695             # store the entry
696 0         0 $self->{'dbms'}{$dbm}{$user} = $entry;
697              
698             # release the lock
699 0         0 $self->_release_lock ($dbm);
700              
701 0         0 return 1;
702             }
703              
704              
705             ## sets a new quota value for a user
706             sub user_set_periodic {
707 0     0 1 0 my ($self, $user, $dbm, $amt) = @_;
708 0         0 my $entry;
709              
710             # sanity check
711 0 0 0     0 unless ($dbm && $user && defined($amt)) {
      0        
712 0         0 carp "Invocation: \$pquota->user_set_periodic (\$user, \$database, \$amount)";
713 0         0 return undef;
714             }
715              
716             # make sure the dbm is open
717 0 0       0 unless ($self->_open_dbm ($dbm)) {
718 0         0 return undef;
719             }
720              
721             # make sure the user exists
722 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
723 0         0 carp "Pquota::user_set_periodic: $user not in users database";
724 0         0 return undef;
725             }
726              
727             # get the user entry
728 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
729              
730             # set the new value
731 0 0       0 if ($amt eq 'unlimited') {
732 0         0 $amt = UNLIMITED;
733             }
734 0         0 $entry->{'periodic'} = $amt;
735 0         0 $entry->{'current'} = $amt;
736              
737             # get the lock
738 0         0 $self->_get_lock ($dbm);
739              
740             # store the entry
741 0         0 $self->{'dbms'}{$dbm}{$user} = $entry;
742              
743             # release the lock
744 0         0 $self->_release_lock ($dbm);
745              
746 0         0 return 1;
747             }
748              
749              
750             ## checks for the periodic quota of a specified user
751             sub user_get_periodic {
752 0     0 1 0 my ($self, $user, $dbm) = @_;
753 0         0 my $entry;
754              
755             # sanity check
756 0 0 0     0 unless ($dbm && $user) {
757 0         0 carp "Invocation: \$pquota->user_get_periodic (\$user, \$database)";
758 0         0 return undef;
759             }
760              
761             # make sure user dbm is open
762 0 0       0 unless ($self->_open_dbm ($dbm)) {
763 0         0 return undef;
764             }
765              
766             # make sure user is in database
767 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
768 0         0 carp "Pquota::user_get_periodic: $user not in $dbm database";
769 0         0 return undef;
770             }
771              
772             # get entry
773 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
774              
775 0         0 return $entry->{'periodic'};
776             }
777              
778              
779             ## resets out the total quota
780             sub user_reset_total {
781 0     0 1 0 my ($self, $user, $dbm) = @_;
782 0         0 my $entry;
783              
784             # sanity check
785 0 0 0     0 unless ($dbm && $user) {
786 0         0 carp "Invocation: \$pquota->user_reset_total (\$user, \$database)";
787 0         0 return undef;
788             }
789              
790             # make sure user dbm is open
791 0 0       0 unless ($self->_open_dbm ($dbm)) {
792 0         0 return undef;
793             }
794              
795             # make sure user is in database
796 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
797 0         0 carp "Pquota::user_reset_total: $user not in $dbm database";
798 0         0 return undef;
799             }
800              
801             # get entry
802 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
803              
804             # reset the total field
805 0         0 $entry->{'total'} = 0;
806            
807             # get the lock
808 0         0 $self->_get_lock ($dbm);
809              
810             # store it
811 0         0 $self->{'dbms'}{$dbm}{$user} = $entry;
812              
813             # give up the lock
814 0         0 $self->_release_lock ($dbm);
815            
816             # return
817 0         0 return 1;
818             }
819              
820              
821             ## set an arbitrary field in a user's record
822             sub user_set_field {
823 0     0 1 0 my ($self, $user, $dbm, $key, $val) = @_;
824 0         0 my $entry;
825              
826             # sanity check, allow for empty value, but not an undefined one
827 0 0 0     0 unless ($dbm && $user && $key && defined ($val)) {
      0        
      0        
828 0         0 carp "Invocation: \$pquota->user_set_field (\$user, \$database, \$key, \$value)";
829 0         0 return undef;
830             }
831              
832             # make sure the dbm is open
833 0 0       0 unless ($self->_open_dbm ($dbm)) {
834 0         0 return undef;
835             }
836              
837             # make sure the user exists
838 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
839 0         0 carp "Pquota::user_set_field: $user not in $dbm database";
840 0         0 return undef;
841             }
842              
843             # get the lock
844 0         0 $self->_get_lock ($dbm);
845              
846             # get the entry, set the value
847 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
848 0         0 $entry->{$key} = $val;
849 0         0 $self->{'dbms'}{$dbm}{$user} = $entry;
850              
851             # release the lock
852 0         0 $self->_release_lock ($dbm);
853              
854 0         0 return 1;
855             }
856              
857              
858             ## get an arbitrary field in a user's record
859             sub user_get_field {
860 0     0 1 0 my ($self, $user, $dbm, $key) = @_;
861 0         0 my $entry;
862              
863             # sanity check
864 0 0 0     0 unless ($dbm && $user && $key) {
      0        
865 0         0 carp "Invocation: \$pquota->user_get_field (\$user, \$database, \$key)";
866 0         0 return undef;
867             }
868              
869             # make sure the dbm is open
870 0 0       0 unless ($self->_open_dbm ($dbm)) {
871 0         0 return undef;
872             }
873              
874             # make sure the user exists
875 0 0       0 unless (defined ($self->{'dbms'}{$dbm}{$user})) {
876 0         0 carp "Pquota::user_set_field: $user not in $dbm database";
877 0         0 return undef;
878             }
879              
880             # get the entry, get the value
881 0         0 $entry = $self->{'dbms'}{$dbm}{$user};
882              
883 0         0 return $entry->{$key};
884             }
885              
886              
887             #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>#
888             # private subroutines #
889             #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<#
890              
891              
892             ## object initialization
893             sub _init {
894 1     1   3 my ($self, $quotadir, $db_opts) = @_;
895              
896             # sanity check on passed argument
897 1 50       30 unless (-d $quotadir) {
898 0         0 croak "Invocation: Pquota->new (\$quotadir[, \$opts])\n";
899             }
900              
901             # set the options
902 1 50       7 if ($db_opts) {
903 0 0       0 if ($db_opts->{'UseDB'}) {
904 0         0 $MLDBM::UseDB = $db_opts->{'UseDB'};
905             }
906 0 0       0 if ($db_opts->{'Serializer'}) {
907 0         0 $MLDBM::Serializer = $db_opts->{'Serializer'};
908             }
909 0 0 0     0 if ($db_opts->{'RO'} && ($db_opts->{'RO'} =~ /true/i)) {
910 0         0 $self->{'RO'} = 1;
911             }
912             }
913              
914             # store the quota directory, tie the _printers hash
915 1         9 $self->{'quotadir'} = $quotadir;
916 1         3 $self->{'dbms'} = {};
917 1         5 $self->_open_dbm ("_printers");
918              
919             # now return
920 1         4 return 1;
921             }
922              
923              
924             ## open a dbm file
925             sub _open_dbm {
926 3     3   5 my ($self, $dbm) = @_;
927 3         6 my $options;
928              
929             # return immediately if it already exists
930 3 100       12 if (defined ($self->{'dbms'}{$dbm})) {
931 1         4 return 1;
932             }
933              
934             # tie the hash
935 2         7 $self->{'dbms'}->{$dbm} = {};
936 2 50       8 $options = ($self->{'RO'}) ? O_RDONLY : O_RDWR|O_CREAT;
937 2 50       5 unless (tie (%{$self->{'dbms'}->{$dbm}}, 'MLDBM', "$self->{'quotadir'}/$dbm",
  2         22  
938             $options, 0644)) {
939 0         0 carp "Pquota::_open_dbm: Unable to open the $dbm database in $self->{'quotadir'}: $!";
940 0         0 return 0;
941             }
942              
943             # now return
944 2         30279 return 1;
945             }
946              
947              
948             ## get exclusive access to a lock file before changing the dbm
949             sub _get_lock {
950 5     5   7 my ($self, $dbm) = @_;
951 5         8 my ($file, $fh);
952              
953             # get a file handle to open the lock file
954 5 50       9 unless ($fh = eval { local *FH }) {
  5         28  
955 0         0 croak "Pquota::_get_lock: Internal error: $!\n";
956             }
957              
958             # wait until I get to open the lock file
959 5         17 $file = "$self->{'quotadir'}/$dbm.lock";
960 5         417 while (!(sysopen ($fh, $file, O_RDWR|O_CREAT|O_EXCL, 0644))) { }
961              
962             # store the file handle so I can close it later
963 5         19 $self->{"lock"} = $fh;
964              
965 5         13 return 1;
966             }
967              
968              
969             ## close the lock file and remove it from the directory
970             sub _release_lock {
971 5     5   10 my ($self, $dbm) = @_;
972 5         8 my $file;
973              
974             # close the file, then unlink it
975 5         63 CORE::close ($self->{"lock"});
976 5         17 $file = "$self->{'quotadir'}/$dbm.lock";
977 5         268 unlink $file;
978 5         16 delete $self->{"lock"};
979              
980 5         175 return 1;
981             }
982              
983             1;
984              
985             ########################### END CODE, BEGIN DOCS ###########################
986              
987             =pod
988              
989             =head1 NAME
990              
991             Pquota - a UNIX print quota module
992              
993             =head1 SYNOPSIS
994              
995             use Pquota;
996              
997             # object creator and destructor
998             $pquota = Pquota->new ("/path/to/quota/directory"[, $opts]);
999             $pquota->close ();
1000              
1001             # printers database commands
1002             $pquota->printer_add ($printer, $page_cost, $user_db);
1003             $pquota->printer_rm ($printer);
1004             $pquota->printer_set_cost ($printer, $cost);
1005             $pquota->printer_get_cost ($printer);
1006             $pquota->printer_get_cost_list ();
1007             $pquota->printer_set_user_database ($printer, $user_db);
1008             $pquota->printer_get_user_database ($printer);
1009             $pquota->printer_get_user_database_list ();
1010             $pquota->printer_set_field ($printer, $key, $value);
1011             $pquota->printer_get_field ($printer, $key);
1012              
1013             # user database commands
1014             $pquota->user_add ($user, $user_db, $periodic_quota);
1015             $pquota->user_rm ($user, $user_db);
1016             $pquota->user_print_pages ($user, $printer, $num_pages);
1017             $pquota->user_add_to_current ($user, $user_db, $amount);
1018             $pquota->user_set_current ($user, $user_db, $periodic_quota);
1019             $pquota->user_get_current_by_dbm ($user, $user_db);
1020             $pquota->user_get_current_by_printer ($user, $printer);
1021             $pquota->user_reset_current ($user, $user_db);
1022             $pquota->user_add_to_periodic ($user, $user_db, $amount);
1023             $pquota->user_set_periodic ($user, $user_db, $amount);
1024             $pquota->user_get_periodic ($user, $user_db);
1025             $pquota->user_reset_total ($user, $user_db);
1026             $pquota->user_set_field ($user, $user_db, $key, $value);
1027             $pquota->user_get_field ($user, $user_db, $key);
1028              
1029             =head1 DESCRIPTION
1030              
1031             This module is an attempt to provide an easy interface to a group
1032             of DBM files used to store print quota information on a UNIX system.
1033             It makes writing printer interface scripts a lot easier. Pquota
1034             requires the MLDBM module.
1035              
1036             As we've said, Pquota is a wrapper module for handling DBM files. We've
1037             structured it so that there is one database that contains information about
1038             the different printers on your system, and any number of user databases. The
1039             printers database, which we've named _printers to (hopefully) avoid any
1040             namespace clashes. An entry in the _printers database looks something like
1041             this:
1042              
1043             $printer_entry = {'cost' => 5,
1044             'dbm' => 'students'};
1045              
1046             Every printer has a cost per page, and an associated user database.
1047             Multiple printers can point to the same user database, but you can't
1048             have multiple databases for the same printer.
1049              
1050             Pquota is designed with a periodic allotment of quota in mind. On our
1051             systems, students get a couple dollars worth every week. So every entry
1052             in a user database looks like this:
1053              
1054             $user_entry = {'periodic' => 300,
1055             'current' => 273,
1056             'total' => 27 };
1057              
1058             And once a week, we run a cron job to reset all the current values
1059             to be equal to the periodic values.
1060              
1061             Pquota also has pessimistic file locking internal to its DBM accesses,
1062             so there won't be any problems with corrupt DBM files. However, we
1063             decided not to register any signal handlers to deal with signals when
1064             the files are locked, because we didn't want to be overriding any
1065             handlers in the enclosing program. Just in case, all of the lock files
1066             are named dbm.lock, where dbm is the name of the DBM that is locked.
1067             They reside in the same directory as the DBMs themselves.
1068              
1069              
1070             =head2 MLDBM Notes
1071              
1072             MLDBM by default uses Data::Dumper to translate Perl data structures into
1073             strings, and SDBM_File to store them to disk. This is because SDBM_File
1074             comes with all UNIX installs of Perl, and Data::Dumper was originally the
1075             only module which could serialize Perl's data structures. However, it
1076             also has the option of using any of the other DBM modules for storage,
1077             and either Storable or FreezeThaw to serialize the structures. As such,
1078             we've added the $opts option to the new method. Just give it
1079             a hash reference, with the keys 'UseDB' or 'Serializer', to set either
1080             the DBM module or the serializing module, respectively. For example,
1081              
1082             $pquota = Pquota->new ("/var/spool/pquota", {'UseDB' => 'DB_File'});
1083              
1084             would tell MLDBM to use the DB_File module to store the structures to
1085             disk.
1086              
1087             Also, to avoid unnecessary locking, we've added an option to open the
1088             databases in read-only mode, so that scripts that won't be writing to the
1089             databases don't lock it. Simply set the 'RO' option to 'true' in order
1090             to open the databases in read-only mode.
1091              
1092             $pquota = Pquota->new ("/var/spool/pquota", {'RO' => 'true'});
1093              
1094              
1095             =head2 Method Notes
1096              
1097             All methods return either the requested information or 1 in case of success,
1098             and undef in case of failure.
1099              
1100             =head2 Object Methods
1101              
1102             =over 4
1103              
1104             =item Pquota::new ($quotadir[, $opts])
1105              
1106             Standard object constructor. $quotadir should contain the path to
1107             a directory to store the DBMs. The optional $opts should be a
1108             reference to a hash, as described in MLDBM Notes.
1109              
1110             =item Pquota::close ()
1111              
1112             Closes all open databases. The database methods will open the DBMs as needed,
1113             but you must call Pquota::close() before exiting your program in order to make
1114             sure the DBMs are properly closed.
1115              
1116             =back
1117              
1118             =head2 Printer Database Methods
1119              
1120             =over 4
1121              
1122             =item Pquota::printer_add ($printer, $page_cost, $user_db)
1123              
1124             Adds a printer to the printers DBM, with the associated per-page cost and
1125             user database.
1126              
1127             =item Pquota::printer_rm ($printer)
1128              
1129             Removes a printer from the printers DBM.
1130              
1131             =item Pquota::printer_set_cost ($printer, $cost)
1132              
1133             Sets the per-page cost for the printer.
1134              
1135             =item Pquota::printer_get_cost ($printer)
1136              
1137             Returns the per-page cost for the printer.
1138              
1139             =item Pquota::printer_get_cost_list ()
1140              
1141             Returns a reference to a hash, with printer names as keys, and their per-page
1142             costs as the values.
1143              
1144             =item Pquota::printer_set_user_database ($printer, $user_db)
1145              
1146             Sets the printer's associated user database.
1147              
1148             =item Pquota::printer_get_user_database ($printer)
1149              
1150             Returns the name of the printer's associated user database.
1151              
1152             =item Pquota::printer_get_user_database_list ()
1153              
1154             Returns a reference to a hash, with printer names as keys, and their per-page
1155             costs as the values.
1156              
1157             =item Pquota::printer_set_field ($printer, $key, $value)
1158              
1159             Sets an arbitrary field in the printer's record. This is in case you want
1160             to store more information about your printers than Pquota supports natively.
1161              
1162             =item Pquota::printer_get_field ($printer, $key);
1163              
1164             Returns the value store in an arbitrary field in the printer's record.
1165              
1166             =back
1167              
1168             =head2 User Database Methods
1169              
1170             =over 4
1171              
1172             =item Pquota::user_add ($user, $user_db, $periodic_quota)
1173              
1174             Adds an entry to a user database, with the indicated periodic quouta.
1175              
1176             =item Pquota::user_rm ($user, $user_db)
1177              
1178             Removes a user from the specified user database.
1179              
1180             =item Pquota::user_print_pages ($user, $printer, $num_pages)
1181              
1182             Modifies the user database to reflect the fact that the user has printed
1183             the indicated number of pages on the specified printer.
1184              
1185             =item Pquota::user_add_to_current ($user, $user_db, $amount)
1186              
1187             Adds the specified amount to the user's current remaining quota.
1188              
1189             =item Pquota::user_set_current ($user, $user_db. $amount)
1190              
1191             Sets the user's current remaining quota.
1192              
1193             =item Pquota::user_get_current_by_dbm ($user, $user_db)
1194              
1195             Returns the user's current remaining quota.
1196              
1197             =item Pquota::user_get_current_by_printer ($user, $printer)
1198              
1199             Returns the user's current remaining quota in the user database associated
1200             with that printer.
1201              
1202             =item Pquota::user_reset_current ($user, $user_db)
1203              
1204             Resets the user's current remaining quota to his periodic quota value.
1205              
1206             =item Pquota::user_add_to_periodic ($user, $user_db, $amount)
1207              
1208             Adds the specified amount to the user's periodic quota allotment.
1209              
1210             =item Pquota::user_set_periodic ($user, $user_db, $amount)
1211              
1212             Sets the user's periodic quota allotment.
1213              
1214             =item Pquota::user_get_periodic ($user, $user_db)
1215              
1216             Returns the user's periodic quota allotment.
1217              
1218             =item Pquota::user_reset_total
1219              
1220             Sets the user's total quota used to 0.
1221              
1222             =item Pquota::user_set_field ($user, $user_db, $key, $value)
1223              
1224             Sets an arbitrary field in the user's record.
1225              
1226             =item Pquota::user_get_field ($user, $user_db, $key)
1227              
1228             Returns the value stored in an arbitrary field in the user's record.
1229              
1230             =back
1231              
1232             =head1 TO DO
1233              
1234             =over 4
1235              
1236              
1237             =item *
1238              
1239             Come up with more functionality. Pquota currently does everything we
1240             need, but we're sure there must be features it lacks.
1241              
1242             =back
1243              
1244             =head1 BUGS
1245              
1246             None that we know of. Please feel free to mail us with any bugs, patches,
1247             suggestions, comments, flames, death threats, etc.
1248              
1249             =head1 AUTHORS
1250              
1251             David Bonner > and Scott Savarese >
1252              
1253             =head1 VERSION
1254              
1255             Version 1.00 April 30, 1999
1256              
1257             =head1 COPYRIGHT
1258              
1259             Copyright (c) 1998, 1999 by David Bonner and Scott Savarese. All rights
1260             reserved. This program is free software; you can redistribute and/or modify
1261             it under the same terms as Perl itself.
1262              
1263             =cut
1264