3 Functions for Unix user and group quota management. Some of the functionality
4 is implemented in OS-specific library files which get automatically included
5 into this one, like linux-lib.pl. Check the documentation on that file for
10 foreign_require('quota', 'quota-lib.pl');
11 quota::edit_user_quota('joe', '/home', 1000000, 1200000, 1000, 1200);
12 $n = quota::user_filesystems('joe');
13 for($i=0; $i<$n; $i++) {
14 print "filesystem=",$filesys{$i,'filesys'}," ",
15 "block quota=",$filesys{$i,'hblocks'}," ",
16 "blocks used=",$filesys{$i,'ublocks'},"\n";
21 BEGIN { push(@INC, ".."); };
24 if ($gconfig{'os_type'} =~ /^\S+\-linux$/) {
28 do "$gconfig{'os_type'}-lib.pl";
30 if ($module_info{'usermin'}) {
31 &switch_to_remote_user();
34 %access = &get_module_acl();
35 &foreign_require("mount", "mount-lib.pl");
38 $email_cmd = "$module_config_directory/email.pl";
40 =head2 list_filesystems
42 Returns a list of details of local filesystems on which quotas are supported.
43 Each is an array ref whose values are :
45 =item directory - Mount point, like /home
47 =item device - Source device, like /dev/hda1
49 =item type - Filesystem type, like ext3
51 =item options - Mount options, like rw,usrquota,grpquota
53 =item quotacan - Can this filesystem type support quotas?
55 =item quotanow - Are quotas enabled right now?
57 The values of quotacan and quotanow are :
61 =item 1 - User quotas only
63 =item 2 - Group quotas only
65 =item 3 - User and group quotas
71 local @mtab = &mount::list_mounted();
72 foreach $f (&mount::list_mounts()) {
73 $fmap{$f->[0],$f->[1]} = $f;
75 map { $_->[4] = "a_can($_, $fmap{$_->[0],$_->[1]}) } @mtab;
76 map { $_->[5] = "a_now($_, $fmap{$_->[0],$_->[1]}) } @mtab;
77 return grep { $_->[4] } @mtab;
80 =head2 parse_options(type, options)
82 Convert an options string for some filesystem into the global hash %options.
90 foreach (split(/,/, $_[0])) {
91 if (/^([^=]+)=(.*)$/) { $options{$1} = $2; }
92 else { $options{$_} = ""; }
97 =head2 user_quota(user, filesystem)
99 Returns an array of quotas and usage information for some user on some
100 filesystem, or an empty array if no quota has been assigned. The array
103 =item Number of blocks used.
105 =item Soft block quota.
107 =item Hard block quota.
109 =item Number of files used.
111 =item Soft file quota.
113 =item Hard file quota.
118 local (%user, $n, $i);
119 $n = &filesystem_users($_[1]);
120 for($i=0; $i<$n; $i++) {
121 if ($user{$i,'user'} eq $_[0]) {
122 return ( $user{$i,'ublocks'}, $user{$i,'sblocks'},
123 $user{$i,'hblocks'}, $user{$i,'ufiles'},
124 $user{$i,'sfiles'}, $user{$i,'hfiles'} );
130 =head2 group_quota(group, filesystem)
132 Returns an array of ublocks, sblocks, hblocks, ufiles, sfiles, hfiles
133 for some group on some filesystem, or an empty array if no quota has been
139 local (%group, $n, $i);
140 $n = &filesystem_groups($_[1]);
141 for($i=0; $i<$n; $i++) {
142 if ($group{$i,'group'} eq $_[0]) {
143 return ( $group{$i,'ublocks'}, $group{$i,'sblocks'},
144 $group{$i,'hblocks'}, $group{$i,'ufiles'},
145 $group{$i,'sfiles'}, $group{$i,'hfiles'} );
151 =head2 edit_user_quota(user, filesys, sblocks, hblocks, sfiles, hfiles)
153 Sets the disk quota for some user. The parameters are :
155 =item user - Unix username.
157 =item filesys - Filesystem on which to change quotas.
159 =item sblocks - Soft block limit.
161 =item hblocks - Hard block limit.
163 =item sfiles - Sort files limit.
165 =item hfiles - Hard files limit.
170 if ($config{'user_setquota_command'} &&
171 &has_command((split(/\s+/, $config{'user_setquota_command'}))[0])) {
172 # Use quota setting command
174 if ($user =~ /^#(\d+)$/) {
178 elsif ($user =~ /^\d+$/) {
179 # Username is numeric .. convert to UID
180 local $uid = getpwnam($user);
181 $user = $uid if (defined($uid));
183 local $cmd = $config{'user_setquota_command'}." ".quotemeta($user)." ".
184 int($_[2])." ".int($_[3])." ".int($_[4])." ".int($_[5]).
185 " ".quotemeta($_[1]);
186 local $out = &backquote_logged("$cmd 2>&1 </dev/null");
187 &error("<tt>".&html_escape($out)."</tt>") if ($?);
190 # Call the quota editor
191 $ENV{'EDITOR'} = $ENV{'VISUAL'} = "$module_root_directory/edquota.pl";
192 $ENV{'QUOTA_USER'} = $_[0];
193 $ENV{'QUOTA_FILESYS'} = $_[1];
194 $ENV{'QUOTA_SBLOCKS'} = $_[2];
195 $ENV{'QUOTA_HBLOCKS'} = $_[3];
196 $ENV{'QUOTA_SFILES'} = $_[4];
197 $ENV{'QUOTA_HFILES'} = $_[5];
199 if ($edquota_use_ids) {
200 # Use UID instead of username
201 if ($user =~ /^#(\d+)$/) {
205 local $uid = getpwnam($user);
206 $user = $uid if (defined($uid));
209 &system_logged("$config{'user_edquota_command'} ".
210 quotemeta($user)." >/dev/null 2>&1");
214 =head2 edit_group_quota(group, filesys, sblocks, hblocks, sfiles, hfiles)
216 Sets the disk quota for some group The parameters are :
218 =item user - Unix group name.
220 =item filesys - Filesystem on which to change quotas.
222 =item sblocks - Soft block limit.
224 =item hblocks - Hard block limit.
226 =item sfiles - Sort files limit.
228 =item hfiles - Hard files limit.
233 if ($config{'group_setquota_command'} &&
234 &has_command((split(/\s+/, $config{'group_setquota_command'}))[0])) {
235 # Use quota setting command
236 local $group = $_[0];
237 if ($group =~ /^#(\d+)$/) {
241 elsif ($group =~ /^\d+$/) {
242 # Group name is numeric .. convert to GID
243 local $gid = getgrnam($group);
244 $group = $gid if (defined($gid));
246 local $cmd =$config{'group_setquota_command'}." ".quotemeta($group)." ".
247 int($_[2])." ".int($_[3])." ".int($_[4])." ".int($_[5]).
248 " ".quotemeta($_[1]);
249 local $out = &backquote_logged("$cmd 2>&1 </dev/null");
250 &error("<tt>".&html_escape($out)."</tt>") if ($?);
254 $ENV{'EDITOR'} = $ENV{'VISUAL'} = "$module_root_directory/edquota.pl";
255 $ENV{'QUOTA_USER'} = $_[0];
256 $ENV{'QUOTA_FILESYS'} = $_[1];
257 $ENV{'QUOTA_SBLOCKS'} = $_[2];
258 $ENV{'QUOTA_HBLOCKS'} = $_[3];
259 $ENV{'QUOTA_SFILES'} = $_[4];
260 $ENV{'QUOTA_HFILES'} = $_[5];
261 local $group = $_[0];
262 if ($edquota_use_ids) {
263 # Use GID instead of group name
264 if ($group =~ /^#(\d+)$/) {
268 local $gid = getgrnam($group);
269 $group = $gid if (defined($gid));
272 &system_logged("$config{'group_edquota_command'} ".
273 quotemeta($group)." >/dev/null 2>&1");
277 =head2 edit_user_grace(filesystem, btime, bunits, ftime, funits)
279 Change the grace times for blocks and files on some filesystem. Parameters are:
281 =item filesystem - Filesystem to change the grace time on.
283 =item btime - Number of units after which a user over his soft block limit is turned into a hard limit.
285 =item bunits - Units for the block grace time, such as 'seconds', 'minutes', 'hours' or 'days'.
287 =item ftime - Number of units after which a user over his soft file limit is turned into a hard limit.
289 =item funits - Units for the file grace time, such as 'seconds', 'minutes', 'hours' or 'days'.
294 $ENV{'EDITOR'} = $ENV{'VISUAL'} = "$module_root_directory/edgrace.pl";
295 $ENV{'QUOTA_FILESYS'} = $_[0];
296 $ENV{'QUOTA_BTIME'} = $_[1];
297 $ENV{'QUOTA_BUNITS'} = $_[2];
298 $ENV{'QUOTA_FTIME'} = $_[3];
299 $ENV{'QUOTA_FUNITS'} = $_[4];
300 &system_logged($config{'user_grace_command'});
303 =head2 edit_group_grace(filesystem, btime, bunits, ftime, funits)
305 Change the grace times for groups for blocks and files on some filesystem.
306 The parameters are the same as edit_user_grace.
311 $ENV{'EDITOR'} = $ENV{'VISUAL'} = "$module_root_directory/edgrace.pl";
312 $ENV{'QUOTA_FILESYS'} = $_[0];
313 $ENV{'QUOTA_BTIME'} = $_[1];
314 $ENV{'QUOTA_BUNITS'} = $_[2];
315 $ENV{'QUOTA_FTIME'} = $_[3];
316 $ENV{'QUOTA_FUNITS'} = $_[4];
317 &system_logged($config{'group_grace_command'});
320 =head2 quota_input(name, value, [blocksize])
322 Returns an input for selecting a quota or unlimited, in a table. For internal
328 return &ui_radio($_[0]."_def", $_[1] == 0 ? 1 : 0,
329 [ [ 1, $text{'quota_unlimited'} ], [ 0, " " ] ])." ".
333 =head2 quota_inputbox(name, value, [blocksize])
335 Returns an input for selecting a quota. Mainly for internal use.
341 # We know the real size, so can offer units
342 local $sz = $_[1]*$_[2];
344 if ($sz >= 10*1024*1024*1024) {
345 $units = 1024*1024*1024;
347 elsif ($sz >= 10*1024*1024) {
350 elsif ($sz >= 10*1024) {
356 $sz = $sz == 0 ? "" : sprintf("%.2f", ($sz*1.0)/$units);
357 return &ui_textbox($_[0], $sz, 8).
358 &ui_select($_[0]."_units", $units,
359 [ [ 1, "bytes" ], [ 1024, "kB" ], [ 1024*1024, "MB" ],
360 [ 1024*1024*1024, "GB" ] ]);
364 return &ui_textbox($_[0], $_[1] == 0 ? "" : $_[1], 8);
368 =head2 quota_parse(name, [bsize], [nodef])
370 Parses inputs from the form generated by quota_input.
375 if ($in{$_[0]."_def"} && !$_[2]) {
379 # Include units, and covert to blocks
380 return int($in{$_[0]}*$in{$_[0]."_units"}/$_[1]);
384 return int($in{$_[0]});
388 =head2 can_edit_filesys(filesys)
390 Returns 1 if the current Webmin user can manage quotas on some filesystem.
396 foreach $fs (split(/\s+/, $access{'filesys'})) {
397 return 1 if ($fs eq "*" || $fs eq $_[0]);
402 =head2 can_edit_user(user)
404 Returns 1 if the current Webmin user can manage quotas for some Unix user.
409 if ($access{'umode'} == 0) {
412 elsif ($access{'umode'} == 3) {
413 local @u = getpwnam($_[0]);
414 return $access{'users'} == $u[3];
416 elsif ($access{'umode'} == 4) {
417 local @u = getpwnam($_[0]);
418 return (!$access{'umin'} || $u[2] >= $access{'umin'}) &&
419 (!$access{'umax'} || $u[2] <= $access{'umax'});
422 local %ucan = map { $_, 1 } split(/\s+/, $access{'users'});
423 return $access{'umode'} == 1 && $ucan{$_[0]} ||
424 $access{'umode'} == 2 && !$ucan{$_[0]};
428 =head2 can_edit_group(group)
430 Returns 1 if the current Webmin user can manage quotas for some Unix group.
435 if ($access{'gmode'} == 0) {
438 elsif ($access{'gmode'} == 3) {
441 elsif ($access{'gmode'} == 4) {
442 local @g = getgrnam($_[0]);
443 return (!$access{'gmin'} || $g[2] >= $access{'gmin'}) &&
444 (!$access{'gmax'} || $g[2] <= $access{'gmax'});
447 local %gcan = map { $_, 1 } split(/\s+/, $access{'groups'});
448 return $access{'gmode'} == 1 && $gcan{$_[0]} ||
449 $access{'gmode'} == 2 && !$gcan{$_[0]};
453 =head2 filesystem_info(filesystem, &hash, count, [blocksize])
455 Returns two strings containing information about the amount of disk space
456 granted and used on some filesystem. For internal use.
461 local @fs = &free_space($_[0], $_[3]);
464 foreach $i (0 .. 3) {
465 $fs[$i] = $i < 2 ? &nice_size($fs[$i]*$_[3])
473 for($i=0; $i<$_[2]; $i++) {
474 $bt += $_[1]->{$i,'hblocks'};
475 $ft += $_[1]->{$i,'hfiles'};
478 $bt = &nice_size($bt*$_[3]);
480 return ( "$fs[0] total / $fs[1] free / $bt granted",
481 "$fs[2] total / $fs[3] free / $ft granted" );
484 return ( "$fs[0] total / $fs[1] free",
485 "$fs[2] total / $fs[3] free" );
489 =head2 block_size(dir, [for-filesys])
491 Returns the size (in bytes) of blocks on some filesystem, if known. All
492 quota functions deal with blocks, so they must be multipled by the value
493 returned by this function before display to users.
498 return undef if (!$config{'block_mode'});
499 return undef if (!defined("a_block_size) &&
500 !defined(&fs_block_size));
501 local @mounts = &mount::list_mounted();
502 local ($mount) = grep { $_->[0] eq $_[0] } @mounts;
505 return &fs_block_size(@$mount);
508 if (defined("a_block_size)) {
509 return "a_block_size(@$mount);
512 return &fs_block_size(@$mount);
519 =head2 nice_limit(amount, bsize, no-blocks)
521 Internal function to show a quota limit nicely formatted.
526 local ($amount, $bsize, $noblocks) = @_;
527 return $amount == 0 ? $text{'quota_unlimited'} :
528 $bsize && !$noblocks ? &nice_size($amount*$bsize) : $amount;
531 =head2 find_email_job
533 Returns the cron job hash ref for the quota limit monitoring email job.
538 &foreign_require("cron", "cron-lib.pl");
539 local @jobs = &cron::list_cron_jobs();
540 local ($job) = grep { $_->{'command'} eq $email_cmd } @jobs;
544 =head2 create_email_job
546 Creates the cron job for scheduled emailing, which runs every 10 minutes.
551 &foreign_require("cron", "cron-lib.pl");
552 local $job = &find_email_job();
554 $job = { 'user' => 'root',
555 'command' => $email_cmd,
557 'mins' => '0,10,20,30,40,50',
562 &lock_file(&cron::cron_file($job));
563 &cron::create_cron_job($job);
564 &cron::create_wrapper($email_cmd, $module_name, "email.pl");
565 &unlock_file(&cron::cron_file($job));
569 =head2 trunc_space(string)
571 Removes spaces from the start and end of a string.
582 =head2 to_percent(used, total)
584 Converts an amount used and a total into a percentage.
590 return $_[0]*100/$_[1];
597 =head2 select_grace_units(name, value)
599 Returns a menu for selecting grace time units.
602 sub select_grace_units
604 local @uarr = &grace_units();
605 return &ui_select($_[0], $_[1],
606 [ map { [ $_, $uarr[$_] ] } (0..$#uarr) ]);
609 # resolve_and_simplify(path)
610 # Resolve symlinks from a path, and simplify the result to remove dots
611 sub resolve_and_simplify
614 return &simplify_path(&resolve_links($path));