# iscsi-server-lib.pl # Common functions for managing and configuring an iSCSI server BEGIN { push(@INC, ".."); }; use strict; use warnings; no warnings 'redefine'; no warnings 'uninitialized'; use WebminCore; &init_config(); &foreign_require("raid"); &foreign_require("fdisk"); &foreign_require("lvm"); &foreign_require("mount"); our (%text, %config, %gconfig, $module_config_file); # check_config() # Returns undef if the iSCSI server is installed, or an error message if # missing sub check_config { return &text('check_etargets', "<tt>$config{'targets_file'}</tt>") if (!-r $config{'targets_file'}); return &text('check_eserver', "<tt>$config{'iscsi_server'}</tt>") if (!&has_command($config{'iscsi_server'})); return undef; } # get_iscsi_config() # Returns an array ref of entries from the iSCSI server config file sub get_iscsi_config { my @rv; my $fh = "CONFIG"; my $lnum = 0; &open_readfile($fh, $config{'targets_file'}) || return [ ]; while(<$fh>) { s/\r|\n//g; s/#.*$//; my @w = split(/\s+/, $_); if (@w && $w[0] =~ /^extent(\d+)/) { # An extent is a sub-section of some file or device my $ext = { 'type' => 'extent', 'num' => $1, 'line' => $lnum, 'device' => $w[1], 'start' => &parse_bytes($w[2]), 'size' => &parse_bytes($w[3]), }; push(@rv, $ext); } elsif (@w && $w[0] =~ /^device(\d+)/) { # A device is a collection of extents my $dev = { 'type' => 'device', 'num' => $1, 'line' => $lnum, 'mode' => $w[1], 'extents' => [ @w[2..$#w] ], }; push(@rv, $dev); } elsif (@w && $w[0] =~ /^target(\d+)/) { # A target is the export of an extent if (@w == 3) { # If flags are missing, assume read/write @w = ( $w[0], "ro", $w[1], $w[2] ); } my $tar = { 'type' => 'target', 'num' => $1, 'line' => $lnum, 'flags' => $w[1], 'export' => $w[2], 'network' => $w[3] }; push(@rv, $tar); } $lnum++; } close($fh); return \@rv; } # find(&config, type, [number]) # Returns all config objects with the given type and optional number sub find { my ($conf, $type, $num) = @_; my @t = grep { $_->{'type'} eq $type } @$conf; if (defined($num)) { @t = grep { $_->{'num'} eq $num } @t; } return wantarray ? @t : $t[0]; } # save_directive(&config, &old, &new) # Creates, updates or deletes some directive sub save_directive { my ($conf, $o, $n) = @_; my $lref = &read_file_lines($config{'targets_file'}); my $line = $n ? &make_directive_line($n) : undef; if ($o && $n) { # Update a line $lref->[$o->{'line'}] = $line; } elsif ($o && !$n) { # Remove a line splice(@$lref, $o->{'line'}, 1); foreach my $c (@$conf) { if ($c->{'line'} > $o->{'line'}) { $c->{'line'}--; } } my $idx = &indexof($o, @$conf); if ($idx >= 0) { splice(@$conf, $idx, 1); } } elsif (!$o && $n) { # Add a line. Extents should come before any devices, and devices # before any targets my @allsame = &find($conf, $n->{'type'}); my $lastsame = @allsame ? pop(@allsame) : undef; my $addline = scalar(@$lref); if ($lastsame) { # Add after last of the same type $addline = $lastsame->{'line'}+1; } elsif ($n->{'type'} eq 'device') { # Add before any targets my $firsttarget = &find($conf, "target"); $addline = $firsttarget->{'line'} if ($addline); } elsif ($n->{'type'} eq 'extent') { # Add before any devices my $firstdevice = &find($conf, "device"); $addline = $firstdevice->{'line'} if ($addline); } $n->{'line'} = $addline; splice(@$lref, $addline, 0, $line); foreach my $c (@$conf) { if ($c->{'line'} >= $addline) { $c->{'line'}++; } } push(@$conf, $n); } &flush_file_lines($config{'targets_file'}); } # make_directive_line(&dir) # Returns the line of text for some directive sub make_directive_line { my ($dir) = @_; my @rv; if ($dir->{'type'} eq 'extent') { @rv = ( $dir->{'type'}.$dir->{'num'}, $dir->{'device'}, &convert_bytes($dir->{'start'}), &convert_bytes($dir->{'size'}) ); } elsif ($dir->{'type'} eq 'device') { @rv = ( $dir->{'type'}.$dir->{'num'}, $dir->{'mode'}, @{$dir->{'extents'}} ); } elsif ($dir->{'type'} eq 'target') { @rv = ( $dir->{'type'}.$dir->{'num'}, $dir->{'flags'}, $dir->{'export'}, $dir->{'network'} ); } return join(" ", @rv); } # parse_bytes(str) # Converts a string like 100MB into a number of bytes sub parse_bytes { my ($str) = @_; if ($str =~ /^(\d+)TB/i) { return $1 * 1024 * 1024 * 1024 * 1024; } elsif ($str =~ /^(\d+)GB/i) { return $1 * 1024 * 1024 * 1024; } elsif ($str =~ /^(\d+)MB/i) { return $1 * 1024 * 1024; } elsif ($str =~ /^(\d+)KB/i) { return $1 * 1024; } elsif ($str =~ /^\d+$/) { return $str; } else { &error("Unknown size number $str"); } } # convert_bytes(num) # Converts a number into a smaller number with a suffix like MB or GB sub convert_bytes { my ($n) = @_; if ($n == 0) { return $n; } elsif ($n % (1024*1024*1024*1024) == 0) { return ($n / (1024*1024*1024*1024))."TB"; } elsif ($n % (1024*1024*1024) == 0) { return ($n / (1024*1024*1024))."GB"; } elsif ($n % (1024*1024) == 0) { return ($n / (1024*1024))."MB"; } elsif ($n % (1024) == 0) { return ($n / (1024))."KB"; } else { return $n; } } # is_iscsi_server_running() # Returns the PID if the server process is running, or 0 if not sub is_iscsi_server_running { return &check_pid_file($config{'pid_file'}); } # start_iscsi_server() # Launch the iscsi server process, and return undef if successful sub start_iscsi_server { my $str = &get_iscsi_options_string(); my $out = &backquote_logged("$config{'iscsi_server'} -f $config{'targets_file'} $str 2>&1 </dev/null"); return $? ? $out : undef; } # stop_iscsi_server() # Kill the running iscsi server process sub stop_iscsi_server { my $pid = &is_iscsi_server_running(); return "Not running" if (!$pid); return kill('TERM', $pid) ? undef : "Kill failed : $!"; } # restart_iscsi_server() # Kill and re-start the iscsi server process sub restart_iscsi_server { &stop_iscsi_server(); return &start_iscsi_server(); } # find_free_num(&config, type) # Returns the max used device number of some type, plus 1 sub find_free_num { my ($conf, $type) = @_; my $max = -1; foreach my $c (&find($conf, $type)) { if ($c->{'num'} > $max) { $max = $c->{'num'}; } } return $max + 1; } # get_device_size(device, "part"|"raid"|"lvm"|"other") # Returns the size in bytes of some device, which can be a partition, RAID # device, logical volume or regular file. For devices, removes 1 MB as this # seems to be needed for overhead or rounding or something. sub get_device_size { my ($dev, $type) = @_; if ($type eq "part") { # A partition or whole disk foreach my $d (&fdisk::list_disks_partitions()) { if ($d->{'device'} eq $dev) { # Whole disk return $d->{'cylinders'} * $d->{'cylsize'}; } foreach my $p (@{$d->{'parts'}}) { if ($p->{'device'} eq $dev) { return ($p->{'end'} - $p->{'start'} + 1) * $d->{'cylsize'} - (1024 * 1024); } } } return undef; } elsif ($type eq "raid") { # A RAID device my $conf = &raid::get_raidtab(); foreach my $c (@$conf) { if ($c->{'value'} eq $dev) { return ($c->{'size'} * 1024) - (1024 * 1024); } } return undef; } elsif ($type eq "lvm") { # LVM volume group foreach my $v (&lvm::list_volume_groups()) { foreach my $l (&lvm::list_logical_volumes($v->{'name'})) { if ($l->{'device'} eq $dev) { return ($l->{'size'} * 1024) - (1024 * 1024); } } } } else { # A regular file my @st = stat($dev); return @st ? $st[7] : undef; } } # find_extent_users(&config, &extent|&device) # Returns a list of all targets or devices using some extent or device sub find_extent_users { my ($conf, $obj) = @_; my $name = $obj->{'type'}.$obj->{'num'}; my @rv; foreach my $c (@$conf) { if ($c->{'type'} eq 'target' && $c->{'export'} eq $name) { push(@rv, $c); } elsif ($c->{'type'} eq 'device' && &indexof($name, @{$c->{'extents'}}) >= 0) { push(@rv, $c); } } return @rv; } # describe_object(&object) # Returns a human-readable description of some extent, device or target sub describe_object { my ($obj) = @_; if ($obj->{'type'} eq 'extent') { return &text('desc_extent', &mount::device_name($obj->{'device'})); } elsif ($obj->{'type'} eq 'device') { return &text('desc_device', "<tt>$obj->{'type'}$obj->{'num'}</tt>"); } elsif ($obj->{'type'} eq 'target') { return &text('desc_target', $obj->{'network'}); } else { return "Unknown $obj->{'type'} object"; } } # expand_extents(&config, &seen, name, ...) # Returns the recursively expanded list of sub-devices of the listed devices sub expand_extents { my ($conf, $seen, @names) = @_; my @rv; foreach my $n (@names) { push(@rv, $n); if ($n =~ /^device(\d+)$/) { my $d = &find($conf, "device", $1); if ($d && !$seen->{$n}++) { push(@rv, &expand_extents($conf, $seen, @{$d->{'extents'}})); } } } return @rv; } # get_iscsi_options_file() # Returns the file containing command-line options, for use when locking sub get_iscsi_options_file { if ($gconfig{'os_type'} eq 'freebsd') { my %iconfig = &foreign_config("init"); my @rcdirs = split(/\s+/, $iconfig{'rc_dir'}); foreach my $d (@rcdirs) { my $file = $d."/".$config{'init_name'}.".sh"; return $file if (-r $file); $file = $d."/".$config{'init_name'}; return $file if (-r $file); } return $rcdirs[$#rcdirs]."/".$config{'init_name'}.".sh"; } else { return $config{'opts_file'}; } } # get_iscsi_options_string() # Returns all flags as a string sub get_iscsi_options_string { my $file = &get_iscsi_options_file(); if ($gconfig{'os_type'} eq 'freebsd') { # Stored in FreeBSD rc script, in command_args line my $lref = &read_file_lines($file, 1); foreach my $l (@$lref) { if ($l =~ /^\s*command_args\s*=\s*"(.*)"/) { return $1; } } } else { # Stored in an environment variables file my %env; &read_env_file($file, \%env); return $env{'NETBSD_ISCSI_OPTS'}; } } # get_iscsi_options() # Returns a hash ref of command line options sub get_iscsi_options { my $str = &get_iscsi_options_string(); my %opts; while($str =~ /\S/) { if ($str =~ /^\s*\-(b|f|p|m|t|v)\s+(\S+)(.*)/) { $str = $3; $opts{$1} = $2; } elsif ($str =~ /^\s*\-((4|6|D|V)+)(.*)/) { $str = $3; foreach my $o (split(//, $1)) { $opts{$o} = ""; } } else { &error("Unknown option $str"); } } return \%opts; } # save_iscsi_options_string(str) # Update the options file with command line options from a string sub save_iscsi_options_string { my ($str) = @_; my $file = &get_iscsi_options_file(); if ($gconfig{'os_type'} eq 'freebsd') { my $lref = &read_file_lines($file); foreach my $l (@$lref) { if ($l =~ /^\s*command_args\s*=\s*"(.*)"/) { $l = "command_args=\"$str\""; } } &flush_file_lines($file); } else { my %env; &read_env_file($file, \%env); $env{'NETBSD_ISCSI_OPTS'} = $str; &write_env_file($file, \%env); } } # save_iscsi_options(&opts) # Update the options file with command line options from a hash sub save_iscsi_options { my ($opts) = @_; my @str; foreach my $o (keys %$opts) { if ($opts->{$o} eq "") { push(@str, "-".$o); } else { push(@str, "-".$o." ".$opts->{$o}); } } &save_iscsi_options_string(join(" ", @str)); } # list_iscsi_users() # Parses the auths file and returns an array of users sub list_iscsi_users { my @rv; my $fh = "AUTHS"; my $lnum = 0; &open_readfile($fh, $config{'auths_file'}) || return ( ); while(<$fh>) { s/\r|\n//; s/\s+$//; s/#.*$//; my ($user, $mode, $pass, @rest) = split(/:/, $_); if ($user) { my $uinfo = { 'user' => $user, 'mode' => $mode, 'pass' => $pass, 'rest' => \@rest, 'line' => $lnum }; push(@rv, $uinfo); } $lnum++; } close($fh); return @rv; } # create_iscsi_user(&user) # Add a new iSCSI user sub create_iscsi_user { my ($user) = @_; my $fh = "AUTHS"; &open_tempfile($fh, ">>$config{'auths_file'}", 0, 1); &print_tempfile($fh, join(":", $user->{'user'}, $user->{'mode'}, $user->{'pass'})."\n"); &close_tempfile($fh); } # modify_iscsi_user(&user) # Update an existing user sub modify_iscsi_user { my ($user) = @_; my $lref = &read_file_lines($config{'auths_file'}); $lref->[$user->{'line'}] = join(":", $user->{'user'}, $user->{'mode'}, $user->{'pass'})."\n"; &flush_file_lines($config{'auths_file'}); } # delete_iscsi_user(&user) # Remove one user sub delete_iscsi_user { my ($user) = @_; my $lref = &read_file_lines($config{'auths_file'}); splice(@$lref, $user->{'line'}, 1); &flush_file_lines($config{'auths_file'}); } 1;
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
help | Folder | 0755 |
|
|
images | Folder | 0755 |
|
|
lang | Folder | 0755 |
|
|
CHANGELOG | File | 106 B | 0644 |
|
atboot.cgi | File | 959 B | 0755 |
|
backup_config.pl | File | 677 B | 0755 |
|
config | File | 192 B | 0644 |
|
config-freebsd | File | 179 B | 0644 |
|
config.info | File | 238 B | 0644 |
|
config.info.ca | File | 298 B | 0644 |
|
config.info.de | File | 265 B | 0644 |
|
config.info.no | File | 250 B | 0644 |
|
delete_devices.cgi | File | 1.48 KB | 0755 |
|
delete_extents.cgi | File | 1.48 KB | 0755 |
|
delete_targets.cgi | File | 1.2 KB | 0755 |
|
delete_users.cgi | File | 858 B | 0755 |
|
edit_device.cgi | File | 1.84 KB | 0755 |
|
edit_extent.cgi | File | 3.16 KB | 0755 |
|
edit_manual.cgi | File | 654 B | 0755 |
|
edit_opts.cgi | File | 1.03 KB | 0755 |
|
edit_target.cgi | File | 2.15 KB | 0755 |
|
edit_user.cgi | File | 1.33 KB | 0755 |
|
index.cgi | File | 1.78 KB | 0755 |
|
install_check.pl | File | 353 B | 0755 |
|
iscsi-server-lib.pl | File | 12.27 KB | 0644 |
|
list_devices.cgi | File | 1.31 KB | 0755 |
|
list_extents.cgi | File | 1.21 KB | 0755 |
|
list_targets.cgi | File | 1.46 KB | 0755 |
|
list_users.cgi | File | 1.02 KB | 0755 |
|
log_parser.pl | File | 682 B | 0755 |
|
module.info | File | 214 B | 0644 |
|
module.info.af | File | 0 B | 0644 |
|
module.info.af.auto | File | 133 B | 0644 |
|
module.info.ar | File | 0 B | 0644 |
|
module.info.ar.auto | File | 179 B | 0644 |
|
module.info.be | File | 0 B | 0644 |
|
module.info.be.auto | File | 206 B | 0644 |
|
module.info.bg | File | 0 B | 0644 |
|
module.info.bg.auto | File | 191 B | 0644 |
|
module.info.ca | File | 149 B | 0644 |
|
module.info.cs | File | 0 B | 0644 |
|
module.info.cs.auto | File | 123 B | 0644 |
|
module.info.da | File | 0 B | 0644 |
|
module.info.da.auto | File | 122 B | 0644 |
|
module.info.de | File | 126 B | 0644 |
|
module.info.el | File | 0 B | 0644 |
|
module.info.el.auto | File | 226 B | 0644 |
|
module.info.es | File | 0 B | 0644 |
|
module.info.es.auto | File | 145 B | 0644 |
|
module.info.eu | File | 0 B | 0644 |
|
module.info.eu.auto | File | 122 B | 0644 |
|
module.info.fa | File | 0 B | 0644 |
|
module.info.fa.auto | File | 188 B | 0644 |
|
module.info.fi | File | 0 B | 0644 |
|
module.info.fi.auto | File | 120 B | 0644 |
|
module.info.fr | File | 0 B | 0644 |
|
module.info.fr.auto | File | 147 B | 0644 |
|
module.info.he | File | 0 B | 0644 |
|
module.info.he.auto | File | 148 B | 0644 |
|
module.info.hr | File | 0 B | 0644 |
|
module.info.hr.auto | File | 128 B | 0644 |
|
module.info.hu | File | 0 B | 0644 |
|
module.info.hu.auto | File | 152 B | 0644 |
|
module.info.it | File | 0 B | 0644 |
|
module.info.it.auto | File | 140 B | 0644 |
|
module.info.ja | File | 0 B | 0644 |
|
module.info.ja.auto | File | 179 B | 0644 |
|
module.info.ko | File | 0 B | 0644 |
|
module.info.ko.auto | File | 160 B | 0644 |
|
module.info.lt | File | 0 B | 0644 |
|
module.info.lt.auto | File | 139 B | 0644 |
|
module.info.lv | File | 0 B | 0644 |
|
module.info.lv.auto | File | 130 B | 0644 |
|
module.info.ms | File | 144 B | 0644 |
|
module.info.mt | File | 0 B | 0644 |
|
module.info.mt.auto | File | 126 B | 0644 |
|
module.info.nl | File | 0 B | 0644 |
|
module.info.nl.auto | File | 133 B | 0644 |
|
module.info.no | File | 122 B | 0644 |
|
module.info.pl | File | 0 B | 0644 |
|
module.info.pl.auto | File | 127 B | 0644 |
|
module.info.pt | File | 0 B | 0644 |
|
module.info.pt.auto | File | 131 B | 0644 |
|
module.info.pt_BR | File | 0 B | 0644 |
|
module.info.pt_BR.auto | File | 137 B | 0644 |
|
module.info.ro | File | 0 B | 0644 |
|
module.info.ro.auto | File | 130 B | 0644 |
|
module.info.ru | File | 0 B | 0644 |
|
module.info.ru.auto | File | 218 B | 0644 |
|
module.info.sk | File | 0 B | 0644 |
|
module.info.sk.auto | File | 121 B | 0644 |
|
module.info.sl | File | 0 B | 0644 |
|
module.info.sl.auto | File | 112 B | 0644 |
|
module.info.sv | File | 0 B | 0644 |
|
module.info.sv.auto | File | 115 B | 0644 |
|
module.info.th | File | 0 B | 0644 |
|
module.info.th.auto | File | 261 B | 0644 |
|
module.info.tr | File | 0 B | 0644 |
|
module.info.tr.auto | File | 135 B | 0644 |
|
module.info.uk | File | 0 B | 0644 |
|
module.info.uk.auto | File | 202 B | 0644 |
|
module.info.ur | File | 0 B | 0644 |
|
module.info.ur.auto | File | 227 B | 0644 |
|
module.info.vi | File | 0 B | 0644 |
|
module.info.vi.auto | File | 134 B | 0644 |
|
module.info.zh | File | 0 B | 0644 |
|
module.info.zh.auto | File | 116 B | 0644 |
|
module.info.zh_TW | File | 0 B | 0644 |
|
module.info.zh_TW.auto | File | 122 B | 0644 |
|
restart.cgi | File | 355 B | 0755 |
|
save_device.cgi | File | 1.96 KB | 0755 |
|
save_extent.cgi | File | 2.45 KB | 0755 |
|
save_manual.cgi | File | 456 B | 0755 |
|
save_opts.cgi | File | 1.14 KB | 0755 |
|
save_target.cgi | File | 1.85 KB | 0755 |
|
save_user.cgi | File | 1.06 KB | 0755 |
|
start.cgi | File | 332 B | 0755 |
|
stop.cgi | File | 307 B | 0755 |
|