1# openbsd-lib.pl 2# Quota functions for openbsd 3 4# quotas_init() 5sub quotas_init 6{ 7return undef; 8} 9 10# quotas_supported() 11# Returns 1 for user quotas, 2 for group quotas or 3 for both 12sub quotas_supported 13{ 14return 3; 15} 16 17# free_space(filesystem) 18# Returns an array containing btotal, bfree, ftotal, ffree 19sub free_space 20{ 21local(@out, @rv); 22my $oldsize = $ENV{'BLOCKSIZE'}; 23$ENV{'BLOCKSIZE'} = 512; 24my $out = &backquote_command("df -i ".quotemeta($_[0])); 25$ENV{'BLOCKSIZE'} = $oldsize; 26if ($out =~ /Mounted on\n\S+\s+(\d+)\s+\d+\s+(\d+)\s+\S+\s+(\d+)\s+(\d+)/) { 27 return ($1, $2, $3+$4, $4); 28 } 29return ( ); 30} 31 32# quota_can(&mnttab, &fstab) 33# Can this filesystem type support quotas? 34# 0 = No quota support (or not turned on in /etc/fstab) 35# 1 = User quotas only 36# 2 = Group quotas only 37# 3 = User and group quotas 38sub quota_can 39{ 40return ($_[1]->[3] =~ /userquota/ ? 1 : 0) + 41 ($_[1]->[3] =~ /groupquota/ ? 2 : 0); 42} 43 44# quota_now(&mnttab, &fstab) 45# Are quotas currently active? 46# 0 = Not active 47# 1 = User quotas active 48# 2 = Group quotas active 49# 3 = Both active 50sub quota_now 51{ 52return $_[0]->[3] =~ /quota/ ? 3 : 0; 53} 54 55# quotaon(filesystem, mode) 56# Activate quotas and create quota files for some filesystem. The mode can 57# be 1 for user only, 2 for group only or 3 for user and group 58sub quotaon 59{ 60return if (&is_readonly_mode()); 61local($out, $qf, @qfile, $flags); 62if ($_[1]%2 == 1) { 63 # turn on user quotas 64 $qf = "$_[0]/quota.user"; 65 if (!(-r $qf)) { 66 &open_tempfile(QUOTAFILE, ">$qf", 0, 1); 67 &close_tempfile(QUOTAFILE); 68 &set_ownership_permissions(undef, undef, 0600, $qf); 69 &system_logged("$config{'quotacheck_command'} $_[0]"); 70 } 71 $out = &backquote_logged("$config{'user_quotaon_command'} $_[0] 2>&1"); 72 if ($?) { return $out; } 73 } 74if ($_[1] > 1) { 75 # turn on group quotas 76 $qf = "$_[0]/quota.group"; 77 if (!(-r $qf)) { 78 &open_tempfile(QUOTAFILE, ">$qf", 0, 1); 79 &close_tempfile(QUOTAFILE); 80 &set_ownership_permissions(undef, undef, 0600, $qf); 81 &system_logged("$config{'quotacheck_command'} $_[0]"); 82 } 83 $out = &backquote_logged("$config{'group_quotaon_command'} $_[0] 2>&1"); 84 if ($?) { return $out; } 85 } 86return undef; 87} 88 89# quotaoff(filesystem, mode) 90# Turn off quotas for some filesystem 91sub quotaoff 92{ 93return if (&is_readonly_mode()); 94local($out); 95if ($_[1]%2 == 1) { 96 $out = &backquote_logged("$config{'user_quotaoff_command'} $_[0] 2>&1"); 97 if ($?) { return $out; } 98 } 99if ($_[1] > 1) { 100 $out = &backquote_logged("$config{'group_quotaoff_command'} $_[0] 2>&1"); 101 if ($?) { return $out; } 102 } 103return undef; 104} 105 106# user_filesystems(user) 107# Fills the array %filesys with details of all filesystem some user has 108# quotas on 109sub user_filesystems 110{ 111local($n, $_, %mtab); 112open(QUOTA, "$config{'user_quota_command'} ".quotemeta($_[0])." |"); 113$n=0; while(<QUOTA>) { 114 chop; 115 if (/^(Disk|\s+Filesystem)/) { next; } 116 if (/^(\S+)$/) { 117 # Bogus wrapped line 118 $filesys{$n,'filesys'} = $1; 119 local $nl = <QUOTA>; 120 $nl =~ /^\s+(\S+)\s+(\S+)\s+(\S+)(.{8}\s+)(\S+)\s+(\S+)\s+(\S+)(.*)/ || 121 $nl =~ /^.{15}(.{8}).(.{7})(.{8}).{8}(.{8}).(.{7})(.{8})/; 122 $filesys{$n,'ublocks'} = int($1); 123 $filesys{$n,'sblocks'} = int($2); 124 $filesys{$n,'hblocks'} = int($3); 125 $filesys{$n,'ufiles'} = int($4); 126 $filesys{$n,'sfiles'} = int($5); 127 $filesys{$n,'hfiles'} = int($6); 128 $n++; 129 } 130 elsif (/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)(.{8}\s+)(\S+)\s+(\S+)\s+(\S+)(.*)/ || 131 /^(.{15})(.{8}).(.{7})(.{8}).{8}(.{8}).(.{7})(.{8})/) { 132 $filesys{$n,'ublocks'} = int($2); 133 $filesys{$n,'sblocks'} = int($3); 134 $filesys{$n,'hblocks'} = int($4); 135 $filesys{$n,'ufiles'} = int($5); 136 $filesys{$n,'sfiles'} = int($6); 137 $filesys{$n,'hfiles'} = int($7); 138 $filesys{$n,'filesys'} = $1; 139 $filesys{$n,'filesys'} =~ s/^\s+//g; 140 $n++; 141 } 142 } 143close(QUOTA); 144return $n; 145} 146 147# group_filesystems(group) 148# Fills the array %filesys with details of all filesystem some group has 149# quotas on 150sub group_filesystems 151{ 152local($n, $_, %mtab); 153open(QUOTA, "$config{'group_quota_command'} ".quotemeta($_[0])." |"); 154$n=0; while(<QUOTA>) { 155 chop; 156 if (/^(Disk|\s+Filesystem)/) { next; } 157 if (/^(\S+)$/) { 158 # Bogus wrapped line 159 $filesys{$n,'filesys'} = $1; 160 local $nl = <QUOTA>; 161 $nl =~ /^\s+(\S+)\s+(\S+)\s+(\S+)(.{8}\s+)(\S+)\s+(\S+)\s+(\S+)(.*)/ || 162 $nl =~ /^.{15}(.{8}).(.{7})(.{8}).{8}(.{8}).(.{7})(.{8})/; 163 $filesys{$n,'ublocks'} = int($1); 164 $filesys{$n,'sblocks'} = int($2); 165 $filesys{$n,'hblocks'} = int($3); 166 $filesys{$n,'ufiles'} = int($4); 167 $filesys{$n,'sfiles'} = int($5); 168 $filesys{$n,'hfiles'} = int($6); 169 $n++; 170 } 171 elsif (/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)(.{8}\s+)(\S+)\s+(\S+)\s+(\S+)(.*)/ || 172 /^(.{15})(.{8}).(.{7})(.{8}).{8}(.{8}).(.{7})(.{8})/) { 173 $filesys{$n,'ublocks'} = int($2); 174 $filesys{$n,'sblocks'} = int($3); 175 $filesys{$n,'hblocks'} = int($4); 176 $filesys{$n,'ufiles'} = int($5); 177 $filesys{$n,'sfiles'} = int($6); 178 $filesys{$n,'hfiles'} = int($7); 179 $filesys{$n,'filesys'} = $1; 180 $filesys{$n,'filesys'} =~ s/^\s+//g; 181 $n++; 182 } 183 } 184close(QUOTA); 185return $n; 186} 187 188# filesystem_users(filesystem) 189# Fills the array %user with information about all users with quotas 190# on this filesystem. This may not be all users on the system.. 191sub filesystem_users 192{ 193local($rep, @rep, $n, $what); 194$rep = `$config{'user_repquota_command'} $_[0] 2>&1`; 195if ($?) { return -1; } 196@rep = split(/\n/, $rep); 197@rep = grep { !/^root\s/ } @rep[3..$#rep]; 198for($n=0; $n<@rep; $n++) { 199 if ($rep[$n] =~ /(\S+)\s*[\-\+]{2}\s+(\d+)\s+(\d+)\s+(\d+)\s(.{0,15})\s(\d+)\s+(\d+)\s+(\d+)(.*)/ || $rep[$n] =~ /(\S+)\s+..(.{8})(.{8})(.{8})(.{7})(.{8})(.{8})(.{8})(.*)/) { 200 $user{$n,'user'} = $1; 201 $user{$n,'ublocks'} = int($2); 202 $user{$n,'sblocks'} = int($3); 203 $user{$n,'hblocks'} = int($4); 204 $user{$n,'gblocks'} = $5; 205 $user{$n,'ufiles'} = int($6); 206 $user{$n,'sfiles'} = int($7); 207 $user{$n,'hfiles'} = int($8); 208 $user{$n,'gfiles'} = $9; 209 $user{$n,'gblocks'} = &trunc_space($user{$n,'gblocks'}); 210 $user{$n,'gfiles'} = &trunc_space($user{$n,'gfiles'}); 211 } 212 } 213return $n; 214} 215 216# filesystem_groups(filesystem) 217# Fills the array %group with information about all groups with quotas 218# on this filesystem. This may not be all groups on the system.. 219sub filesystem_groups 220{ 221local($rep, @rep, $n, $what); 222$rep = `$config{'group_repquota_command'} $_[0] 2>&1`; 223if ($?) { return -1; } 224@rep = split(/\n/, $rep); 225@rep = @rep[3..$#rep]; 226for($n=0; $n<@rep; $n++) { 227 if ($rep[$n] =~ /(\S+)\s*[\-\+]{2}\s+(\d+)\s+(\d+)\s+(\d+)\s(.{0,15})\s(\d+)\s+(\d+)\s+(\d+)(.*)/ || $rep[$n] =~ /(\S+)\s+..(.{8})(.{8})(.{8})(.{7})(.{8})(.{8})(.{8})/) { 228 $group{$n,'group'} = $1; 229 $group{$n,'ublocks'} = int($2); 230 $group{$n,'sblocks'} = int($3); 231 $group{$n,'hblocks'} = int($4); 232 $group{$n,'gblocks'} = $5; 233 $group{$n,'ufiles'} = int($6); 234 $group{$n,'sfiles'} = int($7); 235 $group{$n,'hfiles'} = int($8); 236 $group{$n,'gfiles'} = $9; 237 $group{$n,'gblocks'} = &trunc_space($group{$n,'gblocks'}); 238 $group{$n,'gfiles'} = &trunc_space($group{$n,'gfiles'}); 239 } 240 } 241return $n; 242} 243 244# edit_quota_file(data, filesys, sblocks, hblocks, sfiles, hfiles) 245sub edit_quota_file 246{ 247local($rv, $line, %mtab, @m); 248@line = split(/\n/, $_[0]); 249for($i=0; $i<@line; $i++) { 250 if ($line[$i] =~ /^(\S+): (blocks|kbytes) in use: (\d+), limits \(soft = (\d+), hard = (\d+)\)$/ && $1 eq $_[1]) { 251 # found lines to change, old style 252 $rv .= "$1: $2 in use: $3, limits (soft = $_[2], hard = $_[3])\n"; 253 $line[++$i] =~ /^\s*inodes in use: (\d+), limits \(soft = (\d+), hard = (\d+)\)$/; 254 $rv .= "\tinodes in use: $1, limits (soft = $_[4], hard = $_[5])\n"; 255 } 256 elsif ($line[$i] =~ /^(\S+): in use: (\d+)k, limits \(soft = (\d+)k, hard = (\d+)k\)$/ && $1 eq $_[1]) { 257 # found lines to change, new style 258 $rv .= "$1: in use: ${2}k, limits (soft = $_[2]k, hard = $_[3]k)\n"; 259 $line[++$i] =~ /^\s*inodes in use: (\d+), limits \(soft = (\d+), hard = (\d+)\)$/; 260 $rv .= "\tinodes in use: $1, limits (soft = $_[4], hard = $_[5])\n"; 261 } 262 else { $rv .= "$line[$i]\n"; } 263 } 264return $rv; 265} 266 267# quotacheck(filesystem, mode) 268# Runs quotacheck on some filesystem 269sub quotacheck 270{ 271$out = &backquote_logged("$config{'quotacheck_command'} $_[0] 2>&1"); 272if ($?) { return $out; } 273return undef; 274} 275 276# copy_user_quota(user, [user]+) 277# Copy the quotas for some user to many others 278sub copy_user_quota 279{ 280for($i=1; $i<@_; $i++) { 281 $out = &backquote_logged("$config{'user_copy_command'} ". 282 quotemeta($_[0])." ".quotemeta($_[$i])." 2>&1"); 283 if ($?) { return $out; } 284 } 285return undef; 286} 287 288# copy_group_quota(group, [group]+) 289# Copy the quotas for some group to many others 290sub copy_group_quota 291{ 292for($i=1; $i<@_; $i++) { 293 $out = &backquote_logged("$config{'group_copy_command'} ". 294 quotemeta($_[0])." ".quotemeta($_[$i])." 2>&1"); 295 if ($?) { return $out; } 296 } 297return undef; 298} 299 300# default_grace() 301# Returns 0 if grace time can be 0, 1 if zero grace means default 302sub default_grace 303{ 304return 0; 305} 306 307# get_user_grace(filesystem) 308# Returns an array containing btime, bunits, ftime, funits 309# The units can be 0=sec, 1=min, 2=hour, 3=day 310sub get_user_grace 311{ 312local(@rv, %mtab, @m); 313$ENV{'EDITOR'} = $ENV{'VISUAL'} = "cat"; 314open(GRACE, "$config{'user_grace_command'} $_[0] |"); 315while(<GRACE>) { 316 if (/^(\S+): block grace period: (\d+) (\S+), file grace period: (\d+) (\S+)/ && $1 eq $_[0]) { 317 @rv = ($2, $name_to_unit{$3}, $4, $name_to_unit{$5}); 318 } 319 } 320close(GRACE); 321return @rv; 322} 323 324# get_group_grace(filesystem) 325# Returns an array containing btime, bunits, ftime, funits 326# The units can be 0=sec, 1=min, 2=hour, 3=day 327sub get_group_grace 328{ 329local(@rv, %mtab, @m); 330$ENV{'EDITOR'} = $ENV{'VISUAL'} = "cat"; 331open(GRACE, "$config{'group_grace_command'} $_[0] |"); 332while(<GRACE>) { 333 if (/^(\S+): block grace period: (\d+) (\S+), file grace period: (\d+) (\S+)/ && $1 eq $_[0]) { 334 @rv = ($2, $name_to_unit{$3}, $4, $name_to_unit{$5}); 335 } 336 } 337close(GRACE); 338return @rv; 339} 340 341# edit_grace_file(data, filesystem, btime, bunits, ftime, funits) 342sub edit_grace_file 343{ 344local($rv, $line, @m, %mtab); 345foreach $line (split(/\n/, $_[0])) { 346 if ($line =~ /^(\S+): block grace period: (\d+) (\S+), file grace period: (\d+) (\S+)/ && $1 eq $_[1]) { 347 # replace this line 348 $line = "$1: block grace period: $_[2] $unit_to_name{$_[3]}, file grace period: $_[4] $unit_to_name{$_[5]}"; 349 } 350 $rv .= "$line\n"; 351 } 352return $rv; 353} 354 355# grace_units() 356# Returns an array of possible units for grace periods 357sub grace_units 358{ 359return ($text{'grace_seconds'}, $text{'grace_minutes'}, $text{'grace_hours'}, 360 $text{'grace_days'}); 361} 362 363# fs_block_size(dir, device, filesystem) 364# Returns the size of blocks on some filesystem, or undef if unknown. 365# Always 512 because the ENV setting above forces this. 366sub fs_block_size 367{ 368return 512; 369} 370 371# quota_block_size(dir, device, filesystem) 372# Always returns 1024, as that's the block size BSD appears to use 373sub quota_block_size 374{ 375return 1024; 376} 377 378%name_to_unit = ( "second", 0, "seconds", 0, 379 "minute", 1, "minutes", 1, 380 "hour", 2, "hours", 2, 381 "day", 3, "days", 3, 382 ); 383foreach $k (keys %name_to_unit) { 384 $unit_to_name{$name_to_unit{$k}} = $k; 385 } 386 3871; 388 389