Big collection
This commit is contained in:
849
apply.pl
849
apply.pl
@@ -59,6 +59,774 @@ sub write_smart_bindery_values( $$$ )
|
||||
}
|
||||
|
||||
|
||||
sub mars_nwe_service_active()
|
||||
{
|
||||
my $service = defined( $mars_nwe_service ) && $mars_nwe_service ne '' ? $mars_nwe_service : 'mars-nwe-serv.service';
|
||||
my $systemctl = defined( $smart_systemctl_path ) && $smart_systemctl_path ne '' ? $smart_systemctl_path : '/usr/bin/systemctl';
|
||||
|
||||
return 0 if ! -x $systemctl;
|
||||
|
||||
my $rc = system( $systemctl, 'is-active', '--quiet', $service );
|
||||
|
||||
return $rc == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
sub service_required_page( $ )
|
||||
{
|
||||
my $what = $_[0];
|
||||
$what = 'this operation' unless defined( $what ) && $what ne '';
|
||||
|
||||
print <<EOF;
|
||||
HTTP/1.0 409 Conflict
|
||||
Content-Type: text/html
|
||||
$server_id
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>SMArT Service required</TITLE>
|
||||
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
|
||||
<style>
|
||||
BODY { background:#f6f2ea; color:#2e261d; font-family: Arial, Helvetica, sans-serif; margin:0; padding:24px; }
|
||||
.box { max-width:760px; margin:30px auto; background:#fbf7f1; border:1px solid #ddcfba; border-radius:18px; overflow:hidden; box-shadow:0 6px 18px rgba(80,55,30,0.08); }
|
||||
.head { background:#d7c0a0; padding:14px 16px; font-weight:bold; font-size:22px; }
|
||||
.body { padding:16px; line-height:1.5; }
|
||||
.actions { padding:14px 16px; background:#d7c0a0; }
|
||||
a.button { display:inline-block; margin-right:10px; padding:8px 14px; border-radius:10px; background:#a32020; color:#fff; text-decoration:none; font-weight:bold; }
|
||||
a.back { background:#6d5d53; }
|
||||
TT { color:#5b4b38; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<div class="box">
|
||||
<div class="head">MARS_NWE service is not running</div>
|
||||
<div class="body">
|
||||
<p>Cannot manage <b>$what</b> while <tt>mars-nwe-serv.service</tt> is stopped.</p>
|
||||
<p>Start the MARS_NWE service first, then retry the operation.</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="button" href="/static/start.html" target="OPTS">Open service page</a>
|
||||
<a class="button back" href="javascript:history.back()">Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</BODY>
|
||||
</HTML>
|
||||
EOF
|
||||
}
|
||||
|
||||
sub require_mars_nwe_service( $ )
|
||||
{
|
||||
my $what = $_[0];
|
||||
|
||||
if( ! mars_nwe_service_active() )
|
||||
{
|
||||
service_required_page( $what );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub apply_html_escape( $ )
|
||||
{
|
||||
my $s = $_[0];
|
||||
|
||||
$s = '' unless defined( $s );
|
||||
$s =~ s/&/&/g;
|
||||
$s =~ s/</</g;
|
||||
$s =~ s/>/>/g;
|
||||
$s =~ s/"/"/g;
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
sub validation_error_page( $$ )
|
||||
{
|
||||
my( $title, $msg ) = @_;
|
||||
|
||||
$title = 'Validation error' unless defined( $title ) && $title ne '';
|
||||
$msg = 'The submitted values are not valid.' unless defined( $msg ) && $msg ne '';
|
||||
|
||||
my @detail_rows = ();
|
||||
|
||||
if( $title =~ /User/i )
|
||||
{
|
||||
push( @detail_rows, [ 'User name', defined( $p{name} ) ? $p{name} : ( defined( $c[2] ) ? $c[2] : '' ) ] );
|
||||
push( @detail_rows, [ 'Full name', defined( $p{fullname} ) ? $p{fullname} : '' ] );
|
||||
push( @detail_rows, [ 'Unix user', defined( $p{unix_user} ) ? $p{unix_user} : '' ] );
|
||||
push( @detail_rows, [ 'Password', defined( $p{password} ) && $p{password} ne '' ? '(set)' : '(unchanged)' ] );
|
||||
}
|
||||
elsif( $title =~ /Group/i )
|
||||
{
|
||||
push( @detail_rows, [ 'Group name', defined( $p{name} ) ? $p{name} : ( defined( $c[2] ) ? $c[2] : '' ) ] );
|
||||
push( @detail_rows, [ 'Full name', defined( $p{fullname} ) ? $p{fullname} : '' ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( defined( $p{name} ) || defined( $p{path} ) )
|
||||
{
|
||||
push( @detail_rows, [ 'Volume name', defined( $p{name} ) ? $p{name} : '' ] ) if defined( $p{path} );
|
||||
push( @detail_rows, [ 'Unix path', defined( $p{path} ) ? $p{path} : '' ] ) if defined( $p{path} );
|
||||
}
|
||||
|
||||
if( defined( $p{number} ) || defined( $p{network} ) || defined( $p{interface} ) || defined( $p{frametype} ) || defined( $p{frame_type} ) || defined( $p{delay} ) )
|
||||
{
|
||||
push( @detail_rows, [ 'Network number', defined( $p{number} ) ? $p{number} : ( defined( $p{network} ) ? $p{network} : '' ) ] );
|
||||
push( @detail_rows, [ 'Network interface', defined( $p{interface} ) ? $p{interface} : '' ] );
|
||||
push( @detail_rows, [ 'Frame type', defined( $p{frametype} ) ? $p{frametype} : ( defined( $p{frame_type} ) ? $p{frame_type} : '' ) ] );
|
||||
push( @detail_rows, [ 'Interface delay', defined( $p{delay} ) ? $p{delay} : '' ] );
|
||||
}
|
||||
|
||||
if( defined( $p{command} ) || defined( $p{spool} ) || defined( $p{unix_print} ) || defined( $p{spool_dir} ) )
|
||||
{
|
||||
push( @detail_rows, [ 'Queue name', defined( $p{name} ) ? $p{name} : ( defined( $c[2] ) ? $c[2] : '' ) ] );
|
||||
push( @detail_rows, [ 'Unix printing command', defined( $p{command} ) ? $p{command} : ( defined( $p{unix_print} ) ? $p{unix_print} : '' ) ] );
|
||||
push( @detail_rows, [ 'Spool directory', defined( $p{spool} ) ? $p{spool} : ( defined( $p{spool_dir} ) ? $p{spool_dir} : '' ) ] );
|
||||
}
|
||||
}
|
||||
|
||||
my $details_html = '';
|
||||
|
||||
foreach my $row ( @detail_rows )
|
||||
{
|
||||
my $key = apply_html_escape( $row->[0] );
|
||||
my $val = apply_html_escape( $row->[1] );
|
||||
|
||||
$details_html .= qq| <div class="row"><div class="key">$key</div><div class="val">$val</div></div>\n|;
|
||||
}
|
||||
|
||||
$title = apply_html_escape( $title );
|
||||
$msg = apply_html_escape( $msg );
|
||||
|
||||
print <<EOF;
|
||||
HTTP/1.0 400 Bad Request
|
||||
Content-Type: text/html
|
||||
$server_id
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>SMArT validation error</TITLE>
|
||||
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
|
||||
<style>
|
||||
BODY { background:#f6f2ea; color:#2e261d; font-family: Arial, Helvetica, sans-serif; margin:0; padding:24px; }
|
||||
.box { max-width:820px; margin:30px auto; background:#fbf7f1; border:1px solid #ddcfba; border-radius:18px; overflow:hidden; box-shadow:0 6px 18px rgba(80,55,30,0.08); }
|
||||
.head { background:#d7c0a0; padding:14px 16px; font-weight:bold; font-size:22px; }
|
||||
.body { padding:16px; line-height:1.5; }
|
||||
.err { padding:10px 12px; border:1px solid #e3b0a6; border-radius:12px; background:#fff0ec; color:#7d251e; margin-bottom:14px; }
|
||||
.details { border:1px solid #eadfce; border-radius:12px; overflow:hidden; background:#fffdf9; }
|
||||
.row { display:grid; grid-template-columns:190px 1fr; gap:10px; padding:8px 10px; border-top:1px solid #eadfce; }
|
||||
.row:first-child { border-top:0; }
|
||||
.key { font-weight:bold; }
|
||||
.val { font-family: monospace; color:#5b4b38; word-break:break-word; }
|
||||
.actions { padding:14px 16px; background:#d7c0a0; }
|
||||
a.button { display:inline-block; margin-right:10px; padding:8px 14px; border-radius:10px; background:#6d5d53; color:#fff; text-decoration:none; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<div class="box">
|
||||
<div class="head">$title</div>
|
||||
<div class="body">
|
||||
<div class="err">$msg</div>
|
||||
<div class="details">
|
||||
$details_html </div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="button" href="javascript:history.back()">Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</BODY>
|
||||
</HTML>
|
||||
EOF
|
||||
}
|
||||
|
||||
sub normalize_volume_name( $ )
|
||||
{
|
||||
my $name = $_[0];
|
||||
|
||||
$name = '' unless defined( $name );
|
||||
$name =~ s/^\s+//;
|
||||
$name =~ s/\s+$//;
|
||||
$name = uc( $name );
|
||||
$name =~ s/[^A-Z0-9_\-]/_/g;
|
||||
$name =~ s/_+/_/g;
|
||||
$name = substr( $name, 0, 15 );
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub validate_volume_params()
|
||||
{
|
||||
$p{name} = normalize_volume_name( $p{name} );
|
||||
|
||||
$p{path} = '' unless defined( $p{path} );
|
||||
$p{path} =~ s/^\s+//;
|
||||
$p{path} =~ s/\s+$//;
|
||||
$p{path} =~ s/[\r\n\t']//g;
|
||||
|
||||
if( $p{name} eq '' )
|
||||
{
|
||||
validation_error_page( 'Volume validation error', 'Volume name must not be empty.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{path} eq '' )
|
||||
{
|
||||
validation_error_page( 'Volume validation error', 'Unix path must not be empty.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{path} !~ m#^/# )
|
||||
{
|
||||
validation_error_page( 'Volume validation error', 'Unix path must be an absolute path starting with /.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub normalize_device_network( $ )
|
||||
{
|
||||
my $number = $_[0];
|
||||
|
||||
$number = '' unless defined( $number );
|
||||
$number =~ s/^\s+//;
|
||||
$number =~ s/\s+$//;
|
||||
$number = uc( $number );
|
||||
$number =~ s/^0X//;
|
||||
$number =~ s/[^0-9A-F]//g;
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
sub normalize_device_frame_type( $ )
|
||||
{
|
||||
my $frame = $_[0];
|
||||
|
||||
$frame = '' unless defined( $frame );
|
||||
$frame =~ s/^\s+//;
|
||||
$frame =~ s/\s+$//;
|
||||
|
||||
return '802.2' if $frame eq '8022';
|
||||
return '802.3' if $frame eq '8023';
|
||||
return 'Ethernet_II' if lc( $frame ) eq 'ethernet_ii' || lc( $frame ) eq 'ethernetii';
|
||||
return 'SNAP' if uc( $frame ) eq 'SNAP';
|
||||
|
||||
return $frame;
|
||||
}
|
||||
|
||||
sub validate_device_params()
|
||||
{
|
||||
$p{number} = $p{network} if ! defined( $p{number} ) && defined( $p{network} );
|
||||
$p{frametype} = $p{frame_type} if ! defined( $p{frametype} ) && defined( $p{frame_type} );
|
||||
|
||||
$p{number} = normalize_device_network( $p{number} );
|
||||
|
||||
$p{interface} = '' unless defined( $p{interface} );
|
||||
$p{interface} =~ s/^\s+//;
|
||||
$p{interface} =~ s/\s+$//;
|
||||
$p{interface} =~ s/[\r\n\t']//g;
|
||||
|
||||
$p{frametype} = normalize_device_frame_type( $p{frametype} );
|
||||
|
||||
$p{delay} = '' unless defined( $p{delay} );
|
||||
$p{delay} =~ s/^\s+//;
|
||||
$p{delay} =~ s/\s+$//;
|
||||
|
||||
if( $p{number} eq '' )
|
||||
{
|
||||
validation_error_page( 'Device validation error', 'Network number must not be empty.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{number} !~ /^[0-9A-F]+$/ )
|
||||
{
|
||||
validation_error_page( 'Device validation error', 'Network number must be hexadecimal.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( length( $p{number} ) > 8 )
|
||||
{
|
||||
validation_error_page( 'Device validation error', 'Network number must not be longer than 8 hex digits.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{interface} eq '' )
|
||||
{
|
||||
validation_error_page( 'Device validation error', 'Network interface must not be empty.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{interface} !~ /^[-_.:A-Za-z0-9]+$/ )
|
||||
{
|
||||
validation_error_page( 'Device validation error', 'Network interface contains invalid characters.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{frametype} !~ /^(802\.2|802\.3|Ethernet_II|SNAP)$/ )
|
||||
{
|
||||
validation_error_page( 'Device validation error', 'Frame type must be one of: 802.2, 802.3, Ethernet_II, SNAP.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{delay} eq '' )
|
||||
{
|
||||
$p{delay} = 1;
|
||||
}
|
||||
|
||||
if( $p{delay} !~ /^[0-9]+$/ )
|
||||
{
|
||||
validation_error_page( 'Device validation error', 'Interface delay must be a number.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub normalize_queue_name( $ )
|
||||
{
|
||||
my $name = $_[0];
|
||||
|
||||
$name = '' unless defined( $name );
|
||||
$name =~ s/^\s+//;
|
||||
$name =~ s/\s+$//;
|
||||
$name = uc( $name );
|
||||
$name =~ s/[^A-Z0-9_\-]/_/g;
|
||||
$name =~ s/_+/_/g;
|
||||
$name = substr( $name, 0, 47 );
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub validate_queue_params()
|
||||
{
|
||||
# Real form field names are unix_print and spool_dir. Keep command/spool
|
||||
# aliases for compatibility with older or hand-crafted URLs.
|
||||
$p{command} = $p{unix_print} if ! defined( $p{command} ) && defined( $p{unix_print} );
|
||||
$p{spool} = $p{spool_dir} if ! defined( $p{spool} ) && defined( $p{spool_dir} );
|
||||
|
||||
# Existing queue edit forms do not send a name field. In that case the
|
||||
# queue name is the URL component: /apply/queues/<queue>.
|
||||
if( ( ! defined( $p{name} ) || $p{name} eq '' ) && defined( $c[2] ) && $c[2] ne '' && $c[2] ne 'add_new' )
|
||||
{
|
||||
$p{name} = $c[2];
|
||||
}
|
||||
|
||||
$p{name} = normalize_queue_name( $p{name} );
|
||||
|
||||
$p{command} = '' unless defined( $p{command} );
|
||||
$p{command} =~ s/^\s+//;
|
||||
$p{command} =~ s/\s+$//;
|
||||
$p{command} =~ s/[\r\n\t']//g;
|
||||
$p{unix_print} = $p{command};
|
||||
|
||||
$p{spool} = '' unless defined( $p{spool} );
|
||||
$p{spool} =~ s/^\s+//;
|
||||
$p{spool} =~ s/\s+$//;
|
||||
$p{spool} =~ s/[\r\n\t']//g;
|
||||
$p{spool} =~ s#/{2,}#/#g;
|
||||
$p{spool_dir} = $p{spool};
|
||||
|
||||
if( $p{name} eq '' )
|
||||
{
|
||||
validation_error_page( 'Print queue validation error', 'Queue name must not be empty.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{command} eq '' )
|
||||
{
|
||||
validation_error_page( 'Print queue validation error', 'Unix printing command must not be empty.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
# The spool directory for MARS_NWE print queues may be a NetWare volume
|
||||
# path such as SYS:SYSTEM/0003002.QDR. It is not necessarily a Unix
|
||||
# absolute path. Empty is also allowed because MARS_NWE can generate/fill
|
||||
# this value for queues.
|
||||
if( $p{spool} ne '' )
|
||||
{
|
||||
if( $p{spool} !~ m#^/# && $p{spool} !~ /^[A-Za-z0-9_\-]+:[A-Za-z0-9_\-\.\/]+$/ )
|
||||
{
|
||||
validation_error_page( 'Print queue validation error', 'Spool directory must be empty, a Unix absolute path, or a NetWare path like SYS:SYSTEM/0003002.QDR.' );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub normalize_bindery_name( $ )
|
||||
{
|
||||
my $name = $_[0];
|
||||
|
||||
$name = '' unless defined( $name );
|
||||
$name =~ s/^\s+//;
|
||||
$name =~ s/\s+$//;
|
||||
$name = uc( $name );
|
||||
$name = substr( $name, 0, 47 );
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub bindery_name_has_valid_chars( $ )
|
||||
{
|
||||
my $name = $_[0];
|
||||
|
||||
$name = '' unless defined( $name );
|
||||
|
||||
return $name =~ /^[A-Z0-9_\-]+$/ ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
sub clean_bindery_text( $ )
|
||||
{
|
||||
my $text = $_[0];
|
||||
|
||||
$text = '' unless defined( $text );
|
||||
$text =~ s/[\r\n\t']//g;
|
||||
$text =~ s/^\s+//;
|
||||
$text =~ s/\s+$//;
|
||||
$text = substr( $text, 0, 255 );
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
sub validate_user_params()
|
||||
{
|
||||
# Existing user edit forms do not have to provide a name field.
|
||||
if( ( ! defined( $p{name} ) || $p{name} eq '' ) && defined( $c[2] ) && $c[2] ne '' && $c[2] ne 'add_new' )
|
||||
{
|
||||
$p{name} = $c[2];
|
||||
}
|
||||
|
||||
$p{name} = normalize_bindery_name( $p{name} );
|
||||
$p{fullname} = clean_bindery_text( $p{fullname} );
|
||||
|
||||
$p{unix_user} = '' unless defined( $p{unix_user} );
|
||||
$p{unix_user} =~ s/^\s+//;
|
||||
$p{unix_user} =~ s/\s+$//;
|
||||
$p{unix_user} =~ s/[\r\n\t']//g;
|
||||
|
||||
$p{password} = '' unless defined( $p{password} );
|
||||
|
||||
if( $p{name} eq '' )
|
||||
{
|
||||
validation_error_page( 'User validation error', 'User name must not be empty.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( ! bindery_name_has_valid_chars( $p{name} ) )
|
||||
{
|
||||
validation_error_page( 'User validation error', 'User name contains invalid characters. Allowed characters are A-Z, 0-9, _ and -.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{unix_user} ne '' && $p{unix_user} !~ /^[-_.A-Za-z0-9]+$/ )
|
||||
{
|
||||
validation_error_page( 'User validation error', 'Unix user contains invalid characters.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( $p{password} =~ /[\r\n]/ )
|
||||
{
|
||||
validation_error_page( 'User validation error', 'Password must not contain line breaks.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub validate_group_params()
|
||||
{
|
||||
# Existing group edit forms do not have to provide a name field.
|
||||
if( ( ! defined( $p{name} ) || $p{name} eq '' ) && defined( $c[2] ) && $c[2] ne '' && $c[2] ne 'add_new' )
|
||||
{
|
||||
$p{name} = $c[2];
|
||||
}
|
||||
|
||||
$p{name} = normalize_bindery_name( $p{name} );
|
||||
$p{fullname} = clean_bindery_text( $p{fullname} );
|
||||
|
||||
if( $p{name} eq '' )
|
||||
{
|
||||
validation_error_page( 'Group validation error', 'Group name must not be empty.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( ! bindery_name_has_valid_chars( $p{name} ) )
|
||||
{
|
||||
validation_error_page( 'Group validation error', 'Group name contains invalid characters. Allowed characters are A-Z, 0-9, _ and -.' );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub apply_url_escape( $ )
|
||||
{
|
||||
my $s = $_[0];
|
||||
|
||||
$s = '' unless defined( $s );
|
||||
$s =~ s/([^A-Za-z0-9_\-\. ])/sprintf( "%%%02X", ord( $1 ) )/eg;
|
||||
$s =~ s/ /+/g;
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
sub redirect_msg( $$ )
|
||||
{
|
||||
my( $url, $msg ) = @_;
|
||||
|
||||
$msg = apply_url_escape( $msg );
|
||||
|
||||
if( $url =~ /\?/ )
|
||||
{
|
||||
redirect( $url . '&msg=' . $msg );
|
||||
}
|
||||
else
|
||||
{
|
||||
redirect( $url . '?msg=' . $msg );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub apply_log_line( $$ )
|
||||
{
|
||||
my( $level, $msg ) = @_;
|
||||
|
||||
$level = 'INFO' unless defined( $level ) && $level ne '';
|
||||
$msg = '' unless defined( $msg );
|
||||
|
||||
my( $sec, $min, $hour, $mday, $mon, $year ) = localtime( time() );
|
||||
my $ts = sprintf( "%04d-%02d-%02d %02d:%02d:%02d",
|
||||
$year + 1900, $mon + 1, $mday, $hour, $min, $sec );
|
||||
|
||||
my $version = defined( $smart_version ) && $smart_version ne '' ? $smart_version : '0.99.pl28';
|
||||
my $line = '[' . $ts . '] [' . $level . '] [SMArT ' . $version . '] [apply.pl] ' . $msg . "\n";
|
||||
|
||||
if( defined( $bindery_log_fh ) )
|
||||
{
|
||||
print( $bindery_log_fh $line );
|
||||
return;
|
||||
}
|
||||
|
||||
if( defined( $smart_log_path ) && $smart_log_path ne '' && open( my $fh, '>>', $smart_log_path ) )
|
||||
{
|
||||
print( $fh $line );
|
||||
close( $fh );
|
||||
return;
|
||||
}
|
||||
|
||||
print STDERR '[apply.pl] ' . $msg . "\n";
|
||||
}
|
||||
|
||||
sub bindery_error_page( $$$ )
|
||||
{
|
||||
my( $title, $cmd, $output ) = @_;
|
||||
|
||||
$title = 'Bindery command failed' unless defined( $title ) && $title ne '';
|
||||
$cmd = '' unless defined( $cmd );
|
||||
$output = '' unless defined( $output );
|
||||
|
||||
$title = apply_html_escape( $title );
|
||||
$cmd = apply_html_escape( $cmd );
|
||||
$output = apply_html_escape( $output );
|
||||
|
||||
print <<EOF;
|
||||
HTTP/1.0 500 Internal Server Error
|
||||
Content-Type: text/html
|
||||
$server_id
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>SMArT bindery command failed</TITLE>
|
||||
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
|
||||
<style>
|
||||
BODY { background:#f6f2ea; color:#2e261d; font-family: Arial, Helvetica, sans-serif; margin:0; padding:24px; }
|
||||
.box { max-width:900px; margin:30px auto; background:#fbf7f1; border:1px solid #ddcfba; border-radius:18px; overflow:hidden; box-shadow:0 6px 18px rgba(80,55,30,0.08); }
|
||||
.head { background:#d7c0a0; padding:14px 16px; font-weight:bold; font-size:22px; }
|
||||
.body { padding:16px; line-height:1.5; }
|
||||
.err { padding:10px 12px; border:1px solid #e3b0a6; border-radius:12px; background:#fff0ec; color:#7d251e; margin-bottom:14px; }
|
||||
.details { border:1px solid #eadfce; border-radius:12px; overflow:hidden; background:#fffdf9; }
|
||||
.row { display:grid; grid-template-columns:150px 1fr; gap:10px; padding:8px 10px; border-top:1px solid #eadfce; }
|
||||
.row:first-child { border-top:0; }
|
||||
.key { font-weight:bold; }
|
||||
.val { font-family: monospace; color:#5b4b38; white-space:pre-wrap; word-break:break-word; }
|
||||
.actions { padding:14px 16px; background:#d7c0a0; }
|
||||
a.button { display:inline-block; margin-right:10px; padding:8px 14px; border-radius:10px; background:#6d5d53; color:#fff; text-decoration:none; font-weight:bold; }
|
||||
a.red { background:#a32020; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<div class="box">
|
||||
<div class="head">$title</div>
|
||||
<div class="body">
|
||||
<div class="err">A MARS_NWE bindery command returned an error. The command output was also written to the SMArT log.</div>
|
||||
<div class="details">
|
||||
<div class="row"><div class="key">Command</div><div class="val">$cmd</div></div>
|
||||
<div class="row"><div class="key">Output</div><div class="val">$output</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="button red" href="/static/start.html" target="OPTS">Open service page</a>
|
||||
<a class="button" href="javascript:history.back()">Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</BODY>
|
||||
</HTML>
|
||||
EOF
|
||||
}
|
||||
|
||||
sub apply_shell_quote( $ )
|
||||
{
|
||||
my $s = $_[0];
|
||||
|
||||
$s = '' unless defined( $s );
|
||||
$s =~ s/'/'"'"'/g;
|
||||
|
||||
return "'" . $s . "'";
|
||||
}
|
||||
|
||||
|
||||
our $bindery_log_fh;
|
||||
our $bindery_cmd_ok_count = 0;
|
||||
|
||||
sub open_bindery_command_log()
|
||||
{
|
||||
return 1 if defined( $bindery_log_fh );
|
||||
|
||||
return 0 if ! defined( $smart_log_path ) || $smart_log_path eq '';
|
||||
|
||||
if( open( $bindery_log_fh, '>>', $smart_log_path ) )
|
||||
{
|
||||
select( ( select( $bindery_log_fh ), $| = 1 )[0] );
|
||||
apply_log_line( 'INFO', 'bindery command log handle opened before drop_root' );
|
||||
return 1;
|
||||
}
|
||||
|
||||
print STDERR '[apply.pl] could not pre-open bindery command log: ' . $! . "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
sub bindery_success_page( $$$ )
|
||||
{
|
||||
my( $title, $message, $back_url ) = @_;
|
||||
|
||||
$title = 'Bindery operation completed' unless defined( $title ) && $title ne '';
|
||||
$message = 'The bindery operation completed successfully.' unless defined( $message ) && $message ne '';
|
||||
$back_url = '/settings' unless defined( $back_url ) && $back_url ne '';
|
||||
|
||||
my $count = defined( $bindery_cmd_ok_count ) ? $bindery_cmd_ok_count : 0;
|
||||
|
||||
$title = apply_html_escape( $title );
|
||||
$message = apply_html_escape( $message );
|
||||
$back_url = apply_html_escape( $back_url );
|
||||
|
||||
print <<EOF;
|
||||
HTTP/1.0 200 OK
|
||||
Content-Type: text/html
|
||||
$server_id
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>SMArT bindery operation completed</TITLE>
|
||||
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
|
||||
<style>
|
||||
BODY { background:#f6f2ea; color:#2e261d; font-family: Arial, Helvetica, sans-serif; margin:0; padding:24px; }
|
||||
.box { max-width:820px; margin:30px auto; background:#fbf7f1; border:1px solid #ddcfba; border-radius:18px; overflow:hidden; box-shadow:0 6px 18px rgba(80,55,30,0.08); }
|
||||
.head { background:#d7c0a0; padding:14px 16px; font-weight:bold; font-size:22px; }
|
||||
.body { padding:16px; line-height:1.5; }
|
||||
.ok { padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; margin-bottom:14px; font-weight:bold; }
|
||||
.details { border:1px solid #eadfce; border-radius:12px; overflow:hidden; background:#fffdf9; }
|
||||
.row { display:grid; grid-template-columns:190px 1fr; gap:10px; padding:8px 10px; border-top:1px solid #eadfce; }
|
||||
.row:first-child { border-top:0; }
|
||||
.key { font-weight:bold; }
|
||||
.val { font-family: monospace; color:#5b4b38; word-break:break-word; }
|
||||
.actions { padding:14px 16px; background:#d7c0a0; }
|
||||
a.button { display:inline-block; margin-right:10px; padding:8px 14px; border-radius:10px; background:#6d5d53; color:#fff; text-decoration:none; font-weight:bold; }
|
||||
a.red { background:#a32020; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<div class="box">
|
||||
<div class="head">$title</div>
|
||||
<div class="body">
|
||||
<div class="ok">$message</div>
|
||||
<div class="details">
|
||||
<div class="row"><div class="key">Bindery commands</div><div class="val">$count completed successfully</div></div>
|
||||
<div class="row"><div class="key">Log</div><div class="val">Details were written to the SMArT log.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="button red" href="$back_url">Back to list</a>
|
||||
<a class="button" href="/static/start.html" target="OPTS">Open service page</a>
|
||||
</div>
|
||||
</div>
|
||||
</BODY>
|
||||
</HTML>
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
sub run_bindery_cmd( @ )
|
||||
{
|
||||
my @cmd = @_;
|
||||
my $cmdline = join( ' ', map { apply_shell_quote( $_ ) } @cmd );
|
||||
|
||||
apply_log_line( 'INFO', 'bindery command: ' . $cmdline );
|
||||
|
||||
my $output = `$cmdline 2>&1`;
|
||||
my $rc = $?;
|
||||
|
||||
$output = '' unless defined( $output );
|
||||
$output =~ s/\r//g;
|
||||
|
||||
if( $rc != 0 )
|
||||
{
|
||||
my $exit = $rc >> 8;
|
||||
apply_log_line( 'ERROR', 'bindery command failed rc=' . $exit . ' cmd=' . $cmdline . ' output=' . $output );
|
||||
bindery_error_page( 'Bindery command failed', $cmdline, $output );
|
||||
return 0;
|
||||
}
|
||||
|
||||
$bindery_cmd_ok_count++;
|
||||
apply_log_line( 'INFO', 'bindery command ok cmd=' . $cmdline . ( $output ne '' ? ' output=' . $output : '' ) );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub run_shell_bindery_cmd( $ )
|
||||
{
|
||||
my $cmd = $_[0];
|
||||
$cmd = '' unless defined( $cmd );
|
||||
|
||||
apply_log_line( 'INFO', 'bindery shell command: ' . $cmd );
|
||||
|
||||
my $output = `$cmd 2>&1`;
|
||||
my $rc = $?;
|
||||
|
||||
$output = '' unless defined( $output );
|
||||
$output =~ s/\r//g;
|
||||
|
||||
if( $rc != 0 )
|
||||
{
|
||||
my $exit = $rc >> 8;
|
||||
apply_log_line( 'ERROR', 'bindery shell command failed rc=' . $exit . ' cmd=' . $cmd . ' output=' . $output );
|
||||
bindery_error_page( 'Bindery command failed', $cmd, $output );
|
||||
return 0;
|
||||
}
|
||||
|
||||
apply_log_line( 'INFO', 'bindery shell command ok cmd=' . $cmd . ( $output ne '' ? ' output=' . $output : '' ) );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub handle_request()
|
||||
{
|
||||
if( $c[1] eq 'general' )
|
||||
@@ -128,6 +896,10 @@ sub handle_request()
|
||||
}
|
||||
elsif( $c[1] eq 'volumes' )
|
||||
{
|
||||
if( defined( $p{name} ) || defined( $p{path} ) )
|
||||
{
|
||||
return( 1 ) if ! validate_volume_params();
|
||||
}
|
||||
if( $c[2] ne '' )
|
||||
{
|
||||
delconfigline( '1 ' . $c[2] );
|
||||
@@ -143,10 +915,14 @@ sub handle_request()
|
||||
cbc( 'N' );
|
||||
addconfigline( '1 ' . $p{name} . ' ' . $p{path} . ' ' . $p{case} . $p{trustee} . $p{m} . $p{n} . $p{o} . $p{p} . $p{r} . $p{O} . $p{N} );
|
||||
}
|
||||
redirect( '/settings/volumes' );
|
||||
redirect_msg( '/settings/volumes', 'Volume settings saved.' );
|
||||
}
|
||||
elsif( $c[1] eq 'devices' )
|
||||
{
|
||||
if( defined( $p{number} ) || defined( $p{network} ) || defined( $p{interface} ) || defined( $p{frametype} ) || defined( $p{frame_type} ) || defined( $p{delay} ) )
|
||||
{
|
||||
return( 1 ) if ! validate_device_params();
|
||||
}
|
||||
if( $c[2] ne '' )
|
||||
{
|
||||
delconfigline( '4 ' . $c[2] );
|
||||
@@ -156,7 +932,7 @@ sub handle_request()
|
||||
{
|
||||
addconfigline( '4 ' . $p{number} . ' ' . $p{interface} . ' ' . $p{frametype} . ' ' . $p{delay} );
|
||||
}
|
||||
redirect( '/settings/devices' );
|
||||
redirect_msg( '/settings/devices', 'Device settings saved.' );
|
||||
}
|
||||
elsif( $c[1] eq 'logging' )
|
||||
{
|
||||
@@ -196,20 +972,38 @@ sub handle_request()
|
||||
}
|
||||
elsif( $c[1] eq 'users' )
|
||||
{
|
||||
my $should_update_unix_user = 0;
|
||||
$should_update_unix_user = 1 if defined( $c[2] ) && $c[2] eq 'add_new';
|
||||
$should_update_unix_user = 1 if defined( $p{change_unix_user} ) && $p{change_unix_user} eq '1';
|
||||
apply_log_line( 'INFO', 'UNIX_USER update requested: ' . ( $should_update_unix_user ? 'yes' : 'no' ) );
|
||||
|
||||
if( scalar( keys( %p ) ) > 0 )
|
||||
{
|
||||
return( 1 ) if ! validate_user_params();
|
||||
}
|
||||
return( 1 ) if ! require_mars_nwe_service( 'users' );
|
||||
open_bindery_command_log();
|
||||
drop_root();
|
||||
|
||||
$server = get_server();
|
||||
if( $c[2] eq 'add_new' )
|
||||
{
|
||||
system( 'nwbocreate', '-S', $server, '-t', '1', '-o', $p{name} );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbocreate', '-S', $server, '-t', '1', '-o', $p{name} );
|
||||
$c[2] = $p{name};
|
||||
}
|
||||
else
|
||||
{
|
||||
system( 'nwbprm', '-S', $server, '-t', '1', '-o', $c[2], '-p', 'UNIX_USER' );
|
||||
system( 'nwbprm', '-S', $server, '-t', '1', '-o', $c[2], '-p', 'IDENTIFICATION' );
|
||||
system( 'nwbprm', '-S', $server, '-t', '1', '-o', $c[2], '-p', 'GROUPS_I\'M_IN' );
|
||||
system( 'nwbprm', '-S', $server, '-t', '1', '-o', $c[2], '-p', 'SECURITY_EQUALS' );
|
||||
if( $should_update_unix_user )
|
||||
{
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbprm', '-S', $server, '-t', '1', '-o', $c[2], '-p', 'UNIX_USER' );
|
||||
}
|
||||
else
|
||||
{
|
||||
apply_log_line( 'INFO', 'UNIX_USER removal skipped for existing user ' . $c[2] );
|
||||
}
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbprm', '-S', $server, '-t', '1', '-o', $c[2], '-p', 'IDENTIFICATION' );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbprm', '-S', $server, '-t', '1', '-o', $c[2], '-p', 'GROUPS_I\'M_IN' );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbprm', '-S', $server, '-t', '1', '-o', $c[2], '-p', 'SECURITY_EQUALS' );
|
||||
}
|
||||
|
||||
$id = ( split( ' ', ( grep( /^$c[2] /, split( "\n", `nwbols -S $server` ) ) )[0] ) )[1]; # Woohoo... ;)
|
||||
@@ -219,11 +1013,14 @@ sub handle_request()
|
||||
|
||||
if( scalar( keys( %p ) ) == 0 )
|
||||
{
|
||||
system( 'nwborm', '-S', $server, '-t', '1', '-o', $c[2] );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwborm', '-S', $server, '-t', '1', '-o', $c[2] );
|
||||
}
|
||||
else
|
||||
{
|
||||
write_property_string( $c[2], 1, 'UNIX_USER', 0, 30, $p{unix_user} );
|
||||
if( $should_update_unix_user )
|
||||
{
|
||||
write_property_string( $c[2], 1, 'UNIX_USER', 0, 30, $p{unix_user} );
|
||||
}
|
||||
write_property_string( $c[2], 1, 'IDENTIFICATION', 0, 30, $p{fullname} );
|
||||
|
||||
open( FILE, '|' . 'nwbpset -S ' . $server );
|
||||
@@ -270,17 +1067,23 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
redirect( '/settings/users' );
|
||||
bindery_success_page( 'User saved', 'User bindery settings were saved successfully.', '/settings/users' );
|
||||
return( 1 );
|
||||
}
|
||||
elsif( $c[1] eq 'groups' )
|
||||
{
|
||||
if( scalar( keys( %p ) ) > 0 )
|
||||
{
|
||||
return( 1 ) if ! validate_group_params();
|
||||
}
|
||||
return( 1 ) if ! require_mars_nwe_service( 'groups' );
|
||||
open_bindery_command_log();
|
||||
drop_root();
|
||||
|
||||
$server = get_server();
|
||||
if( $c[2] eq 'add_new' )
|
||||
{
|
||||
system( 'nwbocreate', '-S', $server, '-t', '2', '-o', $p{name} );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbocreate', '-S', $server, '-t', '2', '-o', $p{name} );
|
||||
open( FILE, '|' . 'nwbpset -S ' . $server );
|
||||
print FILE <<EOF;
|
||||
0002
|
||||
@@ -294,13 +1097,13 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
system( 'nwbprm', '-S', $server, '-t', '2', '-o', $c[2], '-p', 'IDENTIFICATION' );
|
||||
system( 'nwbprm', '-S', $server, '-t', '2', '-o', $c[2], '-p', 'OBJ_SUPERVISORS' );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbprm', '-S', $server, '-t', '2', '-o', $c[2], '-p', 'IDENTIFICATION' );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbprm', '-S', $server, '-t', '2', '-o', $c[2], '-p', 'OBJ_SUPERVISORS' );
|
||||
}
|
||||
|
||||
if( scalar( keys( %p ) ) == 0 )
|
||||
{
|
||||
system( 'nwborm', '-S', $server, '-t', '2', '-o', $c[2] );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwborm', '-S', $server, '-t', '2', '-o', $c[2] );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -324,17 +1127,23 @@ EOF
|
||||
close( FILE );
|
||||
}
|
||||
|
||||
redirect( '/settings/groups' );
|
||||
bindery_success_page( 'Group saved', 'Group bindery settings were saved successfully.', '/settings/groups' );
|
||||
return( 1 );
|
||||
}
|
||||
elsif( $c[1] eq 'queues' )
|
||||
{
|
||||
if( defined( $p{name} ) || defined( $p{command} ) || defined( $p{spool} ) || defined( $p{unix_print} ) || defined( $p{spool_dir} ) )
|
||||
{
|
||||
return( 1 ) if ! validate_queue_params();
|
||||
}
|
||||
return( 1 ) if ! require_mars_nwe_service( 'print queues' );
|
||||
open_bindery_command_log();
|
||||
drop_root();
|
||||
|
||||
$server = get_server();
|
||||
if( $c[2] eq 'add_new' )
|
||||
{
|
||||
system( 'nwbocreate', '-S', $server, '-t', '3', '-o', $p{name} );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbocreate', '-S', $server, '-t', '3', '-o', $p{name} );
|
||||
$c[2] = $p{name};
|
||||
|
||||
open( FILE, '|' . 'nwbpset -S ' . $server );
|
||||
@@ -373,13 +1182,13 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
system( 'nwbprm', '-S', $server, '-t', '3', '-o', $c[2], '-p', 'Q_UNIX_PRINT' );
|
||||
system( 'nwbprm', '-S', $server, '-t', '3', '-o', $c[2], '-p', 'Q_DIRECTORY' );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbprm', '-S', $server, '-t', '3', '-o', $c[2], '-p', 'Q_UNIX_PRINT' );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwbprm', '-S', $server, '-t', '3', '-o', $c[2], '-p', 'Q_DIRECTORY' );
|
||||
}
|
||||
|
||||
if( scalar( keys( %p ) ) == 0 )
|
||||
{
|
||||
system( 'nwborm', '-S', $server, '-t', '3', '-o', $c[2] );
|
||||
return( 1 ) if ! run_bindery_cmd( 'nwborm', '-S', $server, '-t', '3', '-o', $c[2] );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -394,7 +1203,7 @@ EOF
|
||||
write_property_string( $c[2], 3, 'Q_DIRECTORY', 0, 31, $p{spool_dir} );
|
||||
}
|
||||
|
||||
redirect( '/settings/queues' );
|
||||
bindery_success_page( 'Print queue saved', 'Print queue bindery settings were saved successfully.', '/settings/queues' );
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
|
||||
521
settings.pl
521
settings.pl
@@ -794,6 +794,423 @@ sub smart_group_checkbox_rows( $ )
|
||||
}
|
||||
|
||||
|
||||
sub volume_name_from_path( $ )
|
||||
{
|
||||
my $path = $_[0];
|
||||
|
||||
$path = '' unless defined( $path );
|
||||
$path =~ s#/*$##;
|
||||
$path =~ s#^.*/##;
|
||||
$path = 'ROOT' if $path eq '';
|
||||
$path = uc( $path );
|
||||
$path =~ s/[^A-Z0-9_\-]/_/g;
|
||||
$path = substr( $path, 0, 15 );
|
||||
$path = 'VOLUME' if $path eq '';
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
sub volume_mountpoint_is_useful( $$ )
|
||||
{
|
||||
my( $mountpoint, $fstype ) = @_;
|
||||
|
||||
return 0 if ! defined( $mountpoint ) || $mountpoint eq '';
|
||||
return 0 if $mountpoint eq '/';
|
||||
|
||||
# Pseudo and system trees are not useful as NetWare volumes.
|
||||
return 0 if $mountpoint =~ m#^/(proc|sys|dev|run)(/|$)#;
|
||||
return 0 if $mountpoint =~ m#^/(tmp|var/tmp)(/|$)#;
|
||||
return 0 if $mountpoint =~ m#^/boot(/|$)#;
|
||||
return 0 if $mountpoint =~ m#^/(etc|usr|bin|sbin|lib|lib64)(/|$)#;
|
||||
return 0 if $mountpoint =~ m#^/system(/|$)#;
|
||||
|
||||
# Do not require -d here. The webui service may have /home masked by
|
||||
# systemd hardening, while the real MARS_NWE service can still use it.
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub local_mountpoints()
|
||||
{
|
||||
my @mounts = ();
|
||||
my %seen = ();
|
||||
|
||||
my $add_mount = sub
|
||||
{
|
||||
my( $mountpoint, $fstype, $source ) = @_;
|
||||
|
||||
$mountpoint = '' unless defined( $mountpoint );
|
||||
$fstype = '' unless defined( $fstype );
|
||||
$source = '' unless defined( $source );
|
||||
|
||||
$mountpoint =~ s#\\040# #g;
|
||||
$mountpoint =~ s#\\011#\t#g;
|
||||
$mountpoint =~ s#\\012#\n#g;
|
||||
$mountpoint =~ s#\\134#\\#g;
|
||||
|
||||
return if $mountpoint eq '';
|
||||
return if $mountpoint =~ /[\r\n\t]/;
|
||||
return if $mountpoint =~ /'/;
|
||||
return if $seen{$mountpoint};
|
||||
|
||||
return if $fstype =~ /^(proc|sysfs|devtmpfs|devpts|tmpfs|securityfs|cgroup|cgroup2|pstore|bpf|tracefs|debugfs|configfs|fusectl|mqueue|hugetlbfs|autofs|overlay|squashfs|portal|binfmt_misc|efivarfs)$/;
|
||||
return if ! volume_mountpoint_is_useful( $mountpoint, $fstype );
|
||||
|
||||
push( @mounts, {
|
||||
path => $mountpoint,
|
||||
fstype => $fstype ne '' ? $fstype : 'mount',
|
||||
source => $source ne '' ? $source : 'mount',
|
||||
name => volume_name_from_path( $mountpoint ),
|
||||
} );
|
||||
|
||||
$seen{$mountpoint} = 1;
|
||||
};
|
||||
|
||||
foreach my $file ( '/proc/1/mountinfo', '/proc/self/mountinfo' )
|
||||
{
|
||||
next if ! open( my $fh, '<', $file );
|
||||
|
||||
while( my $line = <$fh> )
|
||||
{
|
||||
chomp( $line );
|
||||
|
||||
my @parts = split( / - /, $line, 2 );
|
||||
next if scalar( @parts ) != 2;
|
||||
|
||||
my @left = split( /\s+/, $parts[0] );
|
||||
my @right = split( /\s+/, $parts[1] );
|
||||
|
||||
next if scalar( @left ) < 5;
|
||||
next if scalar( @right ) < 3;
|
||||
|
||||
$add_mount->( $left[4], $right[0], $right[1] );
|
||||
}
|
||||
|
||||
close( $fh );
|
||||
}
|
||||
|
||||
if( open( my $fh, '<', '/proc/mounts' ) )
|
||||
{
|
||||
while( my $line = <$fh> )
|
||||
{
|
||||
chomp( $line );
|
||||
|
||||
my( $source, $mountpoint, $fstype ) = split( /\s+/, $line );
|
||||
$add_mount->( $mountpoint, $fstype, $source );
|
||||
}
|
||||
|
||||
close( $fh );
|
||||
}
|
||||
|
||||
@mounts = sort { $a->{path} cmp $b->{path} } @mounts;
|
||||
|
||||
return @mounts;
|
||||
}
|
||||
|
||||
sub volume_mount_import_rows()
|
||||
{
|
||||
my $html = '';
|
||||
my @mounts = local_mountpoints();
|
||||
my %existing = ();
|
||||
|
||||
foreach my $line ( getconfig( 1 ) )
|
||||
{
|
||||
my @f = split( ' ', $line );
|
||||
$existing{lc( $f[1] )} = 1 if defined( $f[1] ) && $f[1] ne '';
|
||||
$existing{'path:' . $f[2]} = 1 if defined( $f[2] ) && $f[2] ne '';
|
||||
}
|
||||
|
||||
if( scalar( @mounts ) == 0 )
|
||||
{
|
||||
return qq|\t<TR BGCOLOR="#ece0cf">\n\t\t<TD COLSPAN=2><B>Local mountpoints detected on host</B></TD>\n\t</TR>\n\t<TR BGCOLOR="#fbf7f1">\n\t\t<TD COLSPAN=2><SMALL>No suitable local mountpoints were found.</SMALL></TD>\n\t</TR>\n|;
|
||||
}
|
||||
|
||||
$html .= qq|\t<TR BGCOLOR="#ece0cf">\n\t\t<TD COLSPAN=2><B>Local mountpoints detected on host</B></TD>\n\t</TR>\n|;
|
||||
|
||||
foreach my $m ( @mounts )
|
||||
{
|
||||
my $name = $m->{name};
|
||||
my $path = $m->{path};
|
||||
my $fstype = $m->{fstype};
|
||||
my $source = $m->{source};
|
||||
my $exists = $existing{lc( $name )} || $existing{'path:' . $path};
|
||||
|
||||
$html .= "\t<TR BGCOLOR=\"#fbf7f1\">\n";
|
||||
$html .= "\t\t<TD><TT>" . html_escape( $path ) . "</TT><BR><SMALL>Volume: <TT>" . html_escape( $name ) . "</TT> Type: <TT>" . html_escape( $fstype ) . "</TT> Source: <TT>" . html_escape( $source ) . "</TT></SMALL></TD>\n";
|
||||
|
||||
if( $exists )
|
||||
{
|
||||
$html .= "\t\t<TD ALIGN=RIGHT><SMALL>already configured</SMALL></TD>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
my $href = '/settings/volumes/add_new?mount_path=' . url_escape( $path ) . '&path=' . url_escape( $path );
|
||||
$html .= "\t\t<TD ALIGN=RIGHT><A HREF=\"" . html_escape( $href ) . "\">Add as volume</A></TD>\n";
|
||||
}
|
||||
|
||||
$html .= "\t</TR>\n";
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
sub volume_mount_defaults_from_query()
|
||||
{
|
||||
my %defaults = ();
|
||||
|
||||
$defaults{name} = '';
|
||||
$defaults{path} = '';
|
||||
|
||||
my $path = '';
|
||||
|
||||
if( defined( $p{mount_path} ) && $p{mount_path} ne '' )
|
||||
{
|
||||
$path = $p{mount_path};
|
||||
}
|
||||
elsif( defined( $p{path} ) && $p{path} ne '' )
|
||||
{
|
||||
$path = $p{path};
|
||||
}
|
||||
|
||||
if( $path ne '' )
|
||||
{
|
||||
$path =~ s/[\r\n\t']//g;
|
||||
$path =~ s#/{2,}#/#g;
|
||||
$path =~ s#/$## if length( $path ) > 1;
|
||||
|
||||
if( volume_mountpoint_is_useful( $path, 'mount' ) )
|
||||
{
|
||||
$defaults{path} = $path;
|
||||
$defaults{name} = volume_name_from_path( $path );
|
||||
}
|
||||
}
|
||||
|
||||
return %defaults;
|
||||
}
|
||||
|
||||
|
||||
sub mars_nwe_service_active()
|
||||
{
|
||||
my $service = defined( $mars_nwe_service ) && $mars_nwe_service ne '' ? $mars_nwe_service : 'mars-nwe-serv.service';
|
||||
my $systemctl = defined( $smart_systemctl_path ) && $smart_systemctl_path ne '' ? $smart_systemctl_path : '/usr/bin/systemctl';
|
||||
|
||||
return 0 if ! -x $systemctl;
|
||||
|
||||
my $rc = system( $systemctl, 'is-active', '--quiet', $service );
|
||||
|
||||
return $rc == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
sub mars_nwe_service_guard_rows( $ )
|
||||
{
|
||||
my $what = $_[0];
|
||||
$what = 'this section' unless defined( $what ) && $what ne '';
|
||||
|
||||
return '' if mars_nwe_service_active();
|
||||
|
||||
return qq|\t<TR BGCOLOR="#fff3e0">\n|
|
||||
. qq|\t\t<TD COLSPAN=2><B>MARS_NWE service is not running.</B><BR>\n|
|
||||
. qq|\t\t<SMALL>Start <TT>mars-nwe-serv.service</TT> before managing | . html_escape( $what ) . qq|. |
|
||||
. qq|<A HREF="/static/start.html" TARGET="OPTS">Open service page</A></SMALL></TD>\n|
|
||||
. qq|\t</TR>\n|;
|
||||
}
|
||||
|
||||
|
||||
sub settings_message_html()
|
||||
{
|
||||
return '' if ! defined( $p{msg} ) || $p{msg} eq '';
|
||||
|
||||
my $msg = $p{msg};
|
||||
$msg =~ s/\+/ /g;
|
||||
|
||||
return qq|<DIV CLASS="smart-message">| . html_escape( $msg ) . qq|</DIV>\n|;
|
||||
}
|
||||
|
||||
|
||||
sub user_group_checkbox_rows( $ )
|
||||
{
|
||||
my $user = $_[0];
|
||||
my $html = '';
|
||||
my %selected = ();
|
||||
|
||||
$user = '' unless defined( $user );
|
||||
|
||||
if( $user ne '' && $user ne 'add_new' )
|
||||
{
|
||||
my $server = get_server();
|
||||
my $groups = `nwbpvalues -S $server -t 1 -o $user -p "GROUPS_I'M_IN" 2>/dev/null`;
|
||||
|
||||
foreach my $line ( split( /\n/, $groups ) )
|
||||
{
|
||||
if( $line =~ /([A-Za-z0-9_\-]+)/ )
|
||||
{
|
||||
$selected{uc( $1 )} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my @groups = getconfig( 12 );
|
||||
my @names = ();
|
||||
|
||||
foreach my $g ( @groups )
|
||||
{
|
||||
my @f = split( /\s+/, $g );
|
||||
next if ! defined( $f[1] ) || $f[1] eq '';
|
||||
push( @names, uc( $f[1] ) );
|
||||
}
|
||||
|
||||
push( @names, 'EVERYONE' ) if ! grep { $_ eq 'EVERYONE' } @names;
|
||||
@names = sort @names;
|
||||
|
||||
foreach my $g ( @names )
|
||||
{
|
||||
my $checked = ( $selected{$g} || ( $g eq 'EVERYONE' && $user eq 'add_new' ) ) ? ' CHECKED' : '';
|
||||
$html .= qq|<LABEL STYLE="margin-left:10px;white-space:nowrap;">| . html_escape( $g )
|
||||
. qq| <INPUT NAME="group_| . html_escape( $g ) . qq|" TYPE=CHECKBOX$checked></LABEL>\n|;
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
|
||||
sub current_bindery_unix_user( $ )
|
||||
{
|
||||
# Do not query the live bindery while rendering the edit form.
|
||||
# Some nwbpvalues versions can block or print unexpected data here.
|
||||
# Existing UNIX_USER stays unchanged unless the admin selects a value.
|
||||
return '';
|
||||
}
|
||||
|
||||
sub smart_unix_users_for_select()
|
||||
{
|
||||
my @users = ();
|
||||
my %seen = ();
|
||||
|
||||
my $helper = '/usr/lib64/mars_nwe/smart_userlist';
|
||||
|
||||
if( -x $helper && open( my $fh, '-|', $helper ) )
|
||||
{
|
||||
while( my $line = <$fh> )
|
||||
{
|
||||
chomp( $line );
|
||||
|
||||
my( $name, $uid, $gid, $gecos, $home, $shell ) = split( /\t/, $line );
|
||||
|
||||
next if ! defined( $name ) || $name eq '';
|
||||
next if $seen{$name};
|
||||
|
||||
push( @users, {
|
||||
name => $name,
|
||||
uid => defined( $uid ) ? $uid : '',
|
||||
fullname => defined( $gecos ) ? $gecos : '',
|
||||
} );
|
||||
|
||||
$seen{$name} = 1;
|
||||
}
|
||||
|
||||
close( $fh );
|
||||
}
|
||||
|
||||
# Fallback: getent passwd. This is only used when smart_userlist is not
|
||||
# available or returned nothing.
|
||||
if( scalar( @users ) == 0 && open( my $fh, '-|', 'getent', 'passwd' ) )
|
||||
{
|
||||
while( my $line = <$fh> )
|
||||
{
|
||||
chomp( $line );
|
||||
|
||||
my( $name, undef, $uid, $gid, $gecos, $home, $shell ) = split( /:/, $line );
|
||||
|
||||
next if ! defined( $name ) || $name eq '';
|
||||
next if defined( $uid ) && $uid ne '' && $uid < 1000;
|
||||
next if defined( $shell ) && $shell =~ m#/(false|nologin)$#;
|
||||
next if $seen{$name};
|
||||
|
||||
push( @users, {
|
||||
name => $name,
|
||||
uid => defined( $uid ) ? $uid : '',
|
||||
fullname => defined( $gecos ) ? $gecos : '',
|
||||
} );
|
||||
|
||||
$seen{$name} = 1;
|
||||
}
|
||||
|
||||
close( $fh );
|
||||
}
|
||||
|
||||
@users = sort { $a->{name} cmp $b->{name} } @users;
|
||||
|
||||
return @users;
|
||||
}
|
||||
|
||||
|
||||
sub unix_user_select_options( $ )
|
||||
{
|
||||
my $selected = $_[0];
|
||||
my $html = '';
|
||||
|
||||
$selected = '' unless defined( $selected );
|
||||
|
||||
foreach my $u ( smart_unix_users_for_select() )
|
||||
{
|
||||
next if ! defined( $u ) || ref( $u ) ne 'HASH';
|
||||
|
||||
my $name = $u->{name};
|
||||
next if ! defined( $name ) || $name eq '';
|
||||
|
||||
my $sel = $name eq $selected ? ' SELECTED' : '';
|
||||
$html .= '<OPTION VALUE="' . html_escape( $name ) . '"' . $sel . '>' . html_escape( $name ) . "</OPTION>
|
||||
";
|
||||
}
|
||||
|
||||
if( $selected ne '' && $html !~ /VALUE="\Q$selected\E"/ )
|
||||
{
|
||||
$html = '<OPTION VALUE="' . html_escape( $selected ) . '" SELECTED>' . html_escape( $selected ) . "</OPTION>\n" . $html;
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
|
||||
sub user_unix_mapping_row( $$ )
|
||||
{
|
||||
my( $user, $selected ) = @_;
|
||||
|
||||
$user = '' unless defined( $user );
|
||||
$selected = '' unless defined( $selected );
|
||||
|
||||
my $unix_user_options = unix_user_select_options( $selected );
|
||||
|
||||
if( $user eq 'add_new' )
|
||||
{
|
||||
return qq| <TR BGCOLOR="#fbf7f1">\n|
|
||||
. qq|\t\t<TD><B>UNIX user:</B></TD>\n|
|
||||
. qq|\t\t<TD ALIGN=RIGHT>\n|
|
||||
. qq|\t\t\t<SELECT NAME="unix_user" SIZE=5 STYLE="width:200px;max-width:200px;">\n|
|
||||
. qq|\t\t\t\t<OPTION VALUE=""></OPTION>\n|
|
||||
. $unix_user_options
|
||||
. qq|\t\t\t</SELECT><BR>\n|
|
||||
. qq|\t\t</TD>\n|
|
||||
. qq|\t</TR>\n|;
|
||||
}
|
||||
|
||||
my $display = $selected ne '' ? $selected : '(unchanged)';
|
||||
|
||||
return qq| <TR BGCOLOR="#fbf7f1">\n|
|
||||
. qq|\t\t<TD><B>UNIX user:</B></TD>\n|
|
||||
. qq|\t\t<TD ALIGN=RIGHT><TT>| . html_escape( $display ) . qq|</TT><BR>\n|
|
||||
. qq|\t\t\t<LABEL><INPUT TYPE=CHECKBOX NAME="change_unix_user" VALUE="1" onclick="document.getElementById('unix_user_change_box').style.display=this.checked?'block':'none'"> Change UNIX user mapping</LABEL>\n|
|
||||
. qq|\t\t\t<DIV ID="unix_user_change_box" STYLE="display:none;margin-top:8px;">\n|
|
||||
. qq|\t\t\t\t<SELECT NAME="unix_user" SIZE=5 STYLE="width:200px;max-width:200px;">\n|
|
||||
. qq|\t\t\t\t\t<OPTION VALUE=""></OPTION>\n|
|
||||
. $unix_user_options
|
||||
. qq|\t\t\t\t</SELECT>\n|
|
||||
. qq|\t\t\t</DIV>\n|
|
||||
. qq|\t\t</TD>\n|
|
||||
. qq|\t</TR>\n|;
|
||||
}
|
||||
|
||||
|
||||
sub handle_request()
|
||||
{
|
||||
if( $c[1] eq 'general' )
|
||||
@@ -849,12 +1266,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/general" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/general" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -1008,12 +1426,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/dirs" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/dirs" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -1137,12 +1556,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/configh" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/configh" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -1271,12 +1691,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/security" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/security" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -1394,12 +1815,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/susers" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/susers" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -1561,12 +1983,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/logging" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/logging" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -1768,6 +2191,7 @@ EOF
|
||||
}
|
||||
elsif( $c[1] eq 'volumes' )
|
||||
{
|
||||
my $volume_mount_import_rows = volume_mount_import_rows();
|
||||
if( $c[2] eq '' )
|
||||
{
|
||||
print <<EOF;
|
||||
@@ -1803,12 +2227,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
@{[ settings_message_html() ]}<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
<B><FONT SIZE=+2>Volumes</FONT></B>
|
||||
@@ -1836,6 +2261,7 @@ EOF
|
||||
print <<EOF;
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD COLSPAN=2>
|
||||
$volume_mount_import_rows
|
||||
<B><A HREF="/settings/volumes/add_new">Add new volume</A></B>
|
||||
</TD>
|
||||
</TR>
|
||||
@@ -1848,9 +2274,36 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
$c = getconfigline( '1 ' . $c[2] );
|
||||
my $volume_action = $c[2];
|
||||
|
||||
$c = getconfigline( '1 ' . $volume_action );
|
||||
@c = split( ' ', $c );
|
||||
$c = '';
|
||||
my %volume_mount_defaults = volume_mount_defaults_from_query();
|
||||
|
||||
if( $volume_action eq 'add_new' )
|
||||
{
|
||||
$c[0] = $volume_mount_defaults{name} if defined( $volume_mount_defaults{name} ) && $volume_mount_defaults{name} ne '';
|
||||
$c[1] = $volume_mount_defaults{path} if defined( $volume_mount_defaults{path} ) && $volume_mount_defaults{path} ne '';
|
||||
$c[2] = 'k' if ! defined( $c[2] ) || $c[2] eq '';
|
||||
}
|
||||
if( $volume_action eq 'add_new' )
|
||||
{
|
||||
my %volume_mount_defaults = volume_mount_defaults_from_query();
|
||||
|
||||
if( defined( $volume_mount_defaults{name} ) && $volume_mount_defaults{name} ne '' )
|
||||
{
|
||||
$c[0] = $volume_mount_defaults{name};
|
||||
}
|
||||
|
||||
if( defined( $volume_mount_defaults{path} ) && $volume_mount_defaults{path} ne '' )
|
||||
{
|
||||
$c[1] = $volume_mount_defaults{path};
|
||||
}
|
||||
|
||||
# Default new imported volumes to lower-case filenames.
|
||||
$c[2] = 'k' if ! defined( $c[2] ) || $c[2] eq '';
|
||||
}
|
||||
|
||||
$d = $c[2];
|
||||
$d =~ s/[^ik]//g;
|
||||
@@ -1928,12 +2381,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/volumes/$c[0]" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/volumes/$c[0]" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -2100,12 +2554,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
@{[ settings_message_html() ]}<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
<B><FONT SIZE=+2>Devices</FONT></B>
|
||||
@@ -2216,12 +2671,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/devices/$title" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/devices/$title" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -2327,12 +2783,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/smart" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/smart" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -2441,12 +2898,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
@{[ settings_message_html() ]}<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
<B><FONT SIZE=+2>Users</FONT></B>
|
||||
@@ -2553,6 +3011,9 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
my $user_group_checkbox_rows = user_group_checkbox_rows( $c[2] );
|
||||
my $current_unix_user = defined( $p{unix_user} ) ? $p{unix_user} : '';
|
||||
my $user_unix_mapping_row = user_unix_mapping_row( $c[2], $current_unix_user );
|
||||
print <<EOF;
|
||||
HTTP/1.0 200 OK
|
||||
Content-Type: text/html
|
||||
@@ -2586,11 +3047,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
@{[ settings_message_html() ]}
|
||||
<FORM ACTION="/apply/users/$c[2]" METHOD=GET>
|
||||
<INPUT TYPE=HIDDEN NAME="unix_user_import_default" VALUE="$default_unix_user">
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
@@ -2635,21 +3098,14 @@ EOF
|
||||
<INPUT NAME="password" TYPE=PASSWORD SIZE=20><BR>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR BGCOLOR="#fbf7f1">
|
||||
<TD>
|
||||
<B>UNIX user:</B>
|
||||
</TD>
|
||||
<TD ALIGN=RIGHT>
|
||||
<SELECT NAME="unix_user" SIZE=5>
|
||||
$unix_user_list </SELECT>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR BGCOLOR="#fbf7f1">
|
||||
$user_unix_mapping_row
|
||||
<TR BGCOLOR="#fbf7f1">
|
||||
<TD>
|
||||
<B>Groups belonged to:</B>
|
||||
</TD>
|
||||
<TD ALIGN=RIGHT>
|
||||
$group_list </TD>
|
||||
$user_group_checkbox_rows
|
||||
</TD>
|
||||
</TR>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -2685,6 +3141,7 @@ EOF
|
||||
}
|
||||
elsif( $c[1] eq 'groups' )
|
||||
{
|
||||
my $groups_service_guard_rows = mars_nwe_service_guard_rows( 'groups' );
|
||||
$server = get_server();
|
||||
|
||||
if( $c[2] eq '' )
|
||||
@@ -2722,12 +3179,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
@{[ settings_message_html() ]}<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
<B><FONT SIZE=+2>Groups</FONT></B>
|
||||
@@ -2830,12 +3288,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/groups/$c[2]" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/groups/$c[2]" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -2895,6 +3354,7 @@ EOF
|
||||
}
|
||||
elsif( $c[1] eq 'queues' )
|
||||
{
|
||||
my $queues_service_guard_rows = mars_nwe_service_guard_rows( 'print queues' );
|
||||
$server = get_server();
|
||||
|
||||
if( $c[2] eq '' )
|
||||
@@ -2932,12 +3392,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
@{[ settings_message_html() ]}<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
<B><FONT SIZE=+2>Print queues</FONT></B>
|
||||
@@ -3028,6 +3489,7 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] {
|
||||
}
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
<SCRIPT TYPE="text/javascript">
|
||||
var smartCupsCommands = {};
|
||||
@@ -3053,7 +3515,7 @@ window.onload = function() {
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/queues/$c[2]" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/queues/$c[2]" METHOD=GET>
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
<TD>
|
||||
@@ -3202,12 +3664,13 @@ INPUT[type=SUBMIT], INPUT[type=RESET], INPUT[type=BUTTON] { border:1px solid #a3
|
||||
A { color:#9f2f26; }
|
||||
TT { color:#5b4b38; }
|
||||
.smallnote { color:#6f6257; font-size:12px; }
|
||||
.smart-message { margin:0 0 12px 0; padding:10px 12px; border:1px solid #b8d9a8; border-radius:12px; background:#edf7e8; color:#2f5b24; font-weight:bold; }
|
||||
</style>
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#f6f2ea">
|
||||
$settings_nav_bar
|
||||
|
||||
<FORM ACTION="/apply/advanced" METHOD=GET>
|
||||
@{[ settings_message_html() ]}<FORM ACTION="/apply/advanced" METHOD=GET>
|
||||
$advanced_hidden
|
||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
||||
<TR BGCOLOR="#d7c0a0">
|
||||
|
||||
@@ -475,7 +475,7 @@ label{display:block;font-weight:bold;margin:0 0 7px}
|
||||
input{width:100%;border:1px solid #cdbb9f;border-radius:12px;padding:10px 12px;background:#fffdf9;color:var(--text);font-size:15px;margin:0 0 16px}
|
||||
button{width:100%;border:1px solid #a33d2f;border-radius:12px;padding:11px 14px;background:#b84434;color:#fff;font-weight:bold;font-size:15px;cursor:pointer}
|
||||
.msg{margin:0 0 16px;padding:10px 12px;border-radius:12px;background:#fff3e0;border:1px solid #ead0a4;color:#7a3d18}
|
||||
.note{margin-top:14px;color:var(--muted);font-size:13px;text-align:center;line-height:1.45;word-break:break-word}.note a{color:var(--muted);text-decoration:none}.note a:hover{text-decoration:underline}
|
||||
.note{margin-top:14px;color:var(--muted);font-size:13px;text-align:center;line-height:1.45}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -501,7 +501,7 @@ EOF
|
||||
<label for="pass">Password</label>
|
||||
<input id="pass" name="pass" type="password" autocomplete="current-password" autofocus>
|
||||
<button type="submit">Login</button>
|
||||
<div class="note">© Copyright 2026 Mario Fetka<br><a href="mailto:mario.fetka@disconnected-by-peer.at">mario.fetka@disconnected-by-peer.at</a></div>
|
||||
<div class="note">© Copyright 2026 Mario Fetka</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -73,7 +73,11 @@
|
||||
font-size: 13px;
|
||||
color: #6c5b52;
|
||||
}
|
||||
</STYLE>
|
||||
|
||||
.runtime-info{margin:18px 12px 0;border:1px solid #ddcfba;border-radius:16px;overflow:hidden;background:#fbf7f1;box-shadow:0 6px 18px rgba(80,55,30,.06)}.runtime-info-title{background:#d7c0a0;padding:10px 14px;font-weight:bold;color:#2e261d}.runtime-info-row{display:grid;grid-template-columns:190px minmax(0,1fr);gap:12px;padding:8px 14px;border-top:1px solid #eadfce;align-items:center}.runtime-info-row code{color:#5b4b38;white-space:normal;word-break:break-word}.runtime-info-note{padding:10px 14px;border-top:1px solid #eadfce;color:#6b5b50;font-size:13px;line-height:1.35}
|
||||
|
||||
.project-footer{margin:16px auto 0;text-align:center;color:#6b5b50;font-size:14px;line-height:1.45;max-width:760px}.project-footer a{color:#9f2f26;text-decoration:none}.project-footer a:hover{text-decoration:underline}.copyright{margin:10px auto 0;text-align:center;color:#6b5b50;font-size:13px;max-width:760px}.copyright a{color:#9f2f26;text-decoration:none}.copyright a:hover{text-decoration:underline}
|
||||
</STYLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<DIV CLASS="wrapper">
|
||||
@@ -105,13 +109,42 @@
|
||||
</TR>
|
||||
<TR>
|
||||
<TD WIDTH="50%"><A CLASS="action secondary" HREF="/service/control?restart">Restart <TT>MARS_NWE</TT></A></TD>
|
||||
<TD WIDTH="50%"><A CLASS="action secondary" HREF="/service/control?status">Status <TT>MARS_NWE</TT></A></TD>
|
||||
<TD WIDTH="50%"><A CLASS="action secondary" HREF="/service/control?status">Status <TT>MARS_NWE</TT></A>
|
||||
</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</DIV>
|
||||
<DIV CLASS="footer">
|
||||
The newest version of SMArT can be downloaded from <A HREF="http://www.lintux.cx/" TARGET="_parent">the project website</A>.<BR><BR>
|
||||
© Copyright 2026 <A HREF="mailto:mario.fetka@disconnected-by-peer.at">Mario Fetka</A>
|
||||
|
||||
<DIV CLASS="runtime-info">
|
||||
<DIV CLASS="runtime-info-title">MARS_NWE runtime information</DIV>
|
||||
<DIV CLASS="runtime-info-row">
|
||||
<DIV>Configuration file</DIV>
|
||||
<DIV><CODE>@MARS_NWE_INSTALL_FULL_CONFDIR@/nwserv.conf</CODE></DIV>
|
||||
</DIV>
|
||||
<DIV CLASS="runtime-info-row">
|
||||
<DIV>SMArT configuration</DIV>
|
||||
<DIV><CODE>@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf</CODE></DIV>
|
||||
</DIV>
|
||||
<DIV CLASS="runtime-info-row">
|
||||
<DIV>WebUI scripts</DIV>
|
||||
<DIV><CODE>@MARS_NWE_INSTALL_FULL_LIBEXECDIR@</CODE></DIV>
|
||||
</DIV>
|
||||
<DIV CLASS="runtime-info-row">
|
||||
<DIV>MARS_NWE service</DIV>
|
||||
<DIV><CODE>mars-nwe-serv.service</CODE></DIV>
|
||||
</DIV>
|
||||
<DIV CLASS="runtime-info-note">
|
||||
User, group and queue operations need a running <B>MARS_NWE</B> service and a reachable bindery.
|
||||
Configuration changes require write access to the SMArT configuration directory.
|
||||
</DIV>
|
||||
</DIV>
|
||||
|
||||
<DIV CLASS="footer">
|
||||
<DIV CLASS="project-footer">
|
||||
<DIV>SMArT is shipped as part of the <B>MARS_NWE</B> package.</DIV>
|
||||
<DIV>Project sources are available from the <A HREF="https://gitea.disconnected-by-peer.at/mars_nwe/mars-nwe" TARGET="_blank">MARS_NWE repository</A>.</DIV>
|
||||
</DIV><BR><BR>
|
||||
<DIV CLASS="copyright">© Copyright 2026 <A HREF="mailto:mario.fetka@disconnected-by-peer.at">Mario Fetka</A></DIV>
|
||||
</DIV>
|
||||
</DIV>
|
||||
</DIV>
|
||||
|
||||
Reference in New Issue
Block a user