# # SMArT # # Apply changes # # Copyright 2001 Wilmer van der Gaast (lintux@lintux.cx) # # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # use Fcntl qw( O_WRONLY O_CREAT O_TRUNC ); sub read_smart_bindery_values() { my( $bind_server, $bind_user, $bind_pass ) = ( '', '', '' ); if( open( my $fh, '<' . $smart_nwclient_path ) ) { my $line = <$fh>; close( $fh ); chomp( $line ) if defined( $line ); ( $bind_server, $bind_user, $bind_pass ) = split( '[/ ]', $line ) if defined( $line ); } $bind_server = '' unless defined( $bind_server ); $bind_user = '' unless defined( $bind_user ); $bind_pass = '' unless defined( $bind_pass ); return( $bind_server, $bind_user, $bind_pass ); } sub write_smart_bindery_values( $$$ ) { my( $bind_server, $bind_user, $bind_pass ) = @_; $bind_server = '' unless defined( $bind_server ); $bind_user = '' unless defined( $bind_user ); $bind_pass = '' unless defined( $bind_pass ); open( my $fh, '>' . $smart_nwclient_path ) or die "Could not open $smart_nwclient_path: $!"; print( $fh $bind_server . '/' . $bind_user . ' ' . $bind_pass . "\n" ); close( $fh ); chown( scalar( getpwnam( $nonroot_user ) ), 0, $smart_nwclient_path ); chmod( 0600, $smart_nwclient_path ); } 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 < SMArT Service required
MARS_NWE service is not running

Cannot manage $what while mars-nwe-serv.service is stopped.

Start the MARS_NWE service first, then retry the operation.

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; 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|
$key
$val
\n|; } $title = apply_html_escape( $title ); $msg = apply_html_escape( $msg ); print < SMArT validation error
$title
$msg
$details_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/. 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 = queue_test_html_escape( $output ); print < SMArT bindery command failed
$title
A MARS_NWE bindery command returned an error. The command output was also written to the SMArT log.
Command
$cmd
Output
$output
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 = queue_test_html_escape( $message ); $back_url = apply_html_escape( $back_url ); print < SMArT bindery operation completed
$title
$message
Bindery commands
$count completed successfully
Log
Details were written to the SMArT log.
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; # nwbprm is used as cleanup before setting a property again. If the # property is already absent, nwbprm can return a failure with # "Could not delete the property". That is harmless here and should not # abort the whole save operation. if( defined( $cmd[0] ) && $cmd[0] eq 'nwbprm' && $output =~ /Could not delete the property/i ) { apply_log_line( 'INFO', 'bindery command skipped missing property cmd=' . $cmdline . ' output=' . $output ); return 1; } 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_bindery_pipe( $$ ) { my( $cmd, $payload ) = @_; my $eval_error = ''; my $result = eval { $cmd = '' unless defined( $cmd ); $payload = '' unless defined( $payload ); my $log_payload = $payload; $log_payload =~ s/\r/\\r/g; $log_payload =~ s/\n/\\n/g; $log_payload = substr( $log_payload, 0, 500 ) . '...' if length( $log_payload ) > 500; apply_log_line( 'INFO', 'bindery pipe command: ' . $cmd . ' payload=' . $log_payload ); apply_log_line( 'INFO', 'bindery pipe trace: entered run_bindery_pipe' ); my $tmpdir = '/run/mars-nwe-webui'; $tmpdir = '/tmp' if ! -d $tmpdir || ! -w $tmpdir; my $tmp = $tmpdir . '/smart-bindery-' . $$ . '-' . int( rand( 1000000 ) ) . '.in'; apply_log_line( 'INFO', 'bindery pipe trace: temp file ' . $tmp ); my $tfh; if( ! sysopen( $tfh, $tmp, O_WRONLY | O_CREAT | O_TRUNC, 0600 ) ) { my $output = 'could not create temporary input file ' . $tmp . ': ' . $!; apply_log_line( 'ERROR', 'bindery pipe command failed cmd=' . $cmd . ' output=' . $output ); bindery_error_page( 'Bindery command failed', $cmd, $output ); return 0; } apply_log_line( 'INFO', 'bindery pipe trace: temp file opened' ); apply_log_line( 'INFO', 'bindery pipe trace: writing payload via syswrite length=' . length( $payload ) ); my $offset = 0; my $len = length( $payload ); while( $offset < $len ) { my $written = syswrite( $tfh, $payload, $len - $offset, $offset ); if( ! defined( $written ) ) { my $output = 'could not write temporary input file ' . $tmp . ': ' . $!; close( $tfh ); unlink( $tmp ); apply_log_line( 'ERROR', 'bindery pipe command failed cmd=' . $cmd . ' output=' . $output ); bindery_error_page( 'Bindery command failed', $cmd, $output ); return 0; } if( $written == 0 ) { my $output = 'could not write temporary input file ' . $tmp . ': zero bytes written'; close( $tfh ); unlink( $tmp ); apply_log_line( 'ERROR', 'bindery pipe command failed cmd=' . $cmd . ' output=' . $output ); bindery_error_page( 'Bindery command failed', $cmd, $output ); return 0; } $offset += $written; } apply_log_line( 'INFO', 'bindery pipe trace: payload written bytes=' . $offset ); if( ! close( $tfh ) ) { my $output = 'could not close temporary input file ' . $tmp . ': ' . $!; unlink( $tmp ); apply_log_line( 'ERROR', 'bindery pipe command failed cmd=' . $cmd . ' output=' . $output ); bindery_error_page( 'Bindery command failed', $cmd, $output ); return 0; } apply_log_line( 'INFO', 'bindery pipe trace: temp file closed' ); my $shell_cmd = $cmd . ' < ' . apply_shell_quote( $tmp ); apply_log_line( 'INFO', 'bindery pipe shell command: ' . $shell_cmd ); my $output = `$shell_cmd 2>&1`; my $rc = $?; apply_log_line( 'INFO', 'bindery pipe trace: shell command returned rc=' . ( $rc >> 8 ) ); unlink( $tmp ); apply_log_line( 'INFO', 'bindery pipe trace: temp file removed' ); $output = '' unless defined( $output ); $output =~ s/\r//g; if( $rc != 0 ) { my $exit = $rc >> 8; apply_log_line( 'ERROR', 'bindery pipe command failed rc=' . $exit . ' cmd=' . $cmd . ' output=' . $output ); bindery_error_page( 'Bindery command failed', $cmd, $output ); return 0; } $bindery_cmd_ok_count++; apply_log_line( 'INFO', 'bindery pipe command ok cmd=' . $cmd . ( $output ne '' ? ' output=' . $output : '' ) ); return 1; }; if( ! $result ) { $eval_error = $@ if defined( $@ ) && $@ ne ''; if( $eval_error ne '' ) { apply_log_line( 'ERROR', 'bindery pipe command died cmd=' . $cmd . ' error=' . $eval_error ); bindery_error_page( 'Bindery command failed', $cmd, 'internal error while running bindery pipe command: ' . $eval_error ); return 0; } return 0; } 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' ) { setc( 2, server_name ); setc( 3, internal_net ); if( defined( $p{sync_smart_bind_server} ) && $p{sync_smart_bind_server} eq 'on' && defined( $p{server_name} ) && $p{server_name} ne '' ) { my( $old_bind_server, $old_bind_user, $old_bind_pass ) = read_smart_bindery_values(); write_smart_bindery_values( $p{server_name}, $old_bind_user, $old_bind_pass ); } setc( 16, test ); setc( 210, timing_down ); setc( 211, timing_warn ); delconfigline( 6 ); if( $p{version} ne '' ) { if( $p{burst_enabled} eq 'on' ) { addconfigline( '6 ' . $p{version} . ' 1' ); } else { addconfigline( '6 ' . $p{version} . ' 0' ); } } delconfigline( 30 ); if( $p{burst_read} ne '' ) { addconfigline( '30 ' . $p{burst_read} . ' ' . $p{burst_write} ); } } elsif( $c[1] eq 'dirs' ) { setc( 40, path_cache ); setc( 41, share_lock ); setc( 42, spool ); setc( 45, bindery ); setc( 46, attribute ); setc( 47, trustee ); } elsif( $c[1] eq 'configh' ) { setc( 60, max_conn ); setc( 61, max_vol ); setc( 63, max_dirbase ); if( $p{mmap} eq 'on' ) { $p{mmap} = 1; } else { $p{mmap} = 0; } setc( 68, mmap ); setc( 69, sap ); setc( 70, net_serial ); setc( 71, net_app ); } elsif( $c[1] eq 'security' ) { setc( 7, encrypt ); delconfigline( 9 ); if( $p{creat_dir} ne '' ) { addconfigline( '9 ' . $p{creat_dir} . ' ' . $p{creat_file} ); } } elsif( $c[1] eq 'susers' ) { setc( 10, guest_group ); setc( 11, guest_user ); delconfigline( 12 ); if( ( $p{root_user} ne '' ) and ( $p{root_name} ne '' ) ) { addconfigline( '12 ' . $p{root_name} . ' ' . $p{root_user} . ' ' . $p{root_password} ); } delconfigline( 15 ); if( $p{map} ne '' ) { addconfigline( '15 ' . $p{map} . ' ' . $p{map_password} ); } } 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] ); } if( $p{name} ne '' ) { cbc( 'm' ); cbc( 'n' ); cbc( 'o' ); cbc( 'p' ); cbc( 'r' ); cbc( 'O' ); 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_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] ); } $p{interface} =~ s/[^-_\.A-Za-z0-9:\*]//g; if( $p{number} ne '' ) { addconfigline( '4 ' . $p{number} . ' ' . $p{interface} . ' ' . $p{frametype} . ' ' . $p{delay} ); } redirect_msg( '/settings/devices', 'Device settings saved.' ); } elsif( $c[1] eq 'logging' ) { setc( 100, ipx ); setc( 101, nwserv ); setc( 102, ncpserv ); setc( 103, nwconn ); setc( 104, nwclient ); setc( 105, nwbind ); setc( 106, nwrouted ); setc( 200, startup ); setc( 201, general_log ); delconfigline( 202 ); addconfigline( 202 . ' ' . ( $p{g_creat} + 2 * $p{error} ) ); setc( 300, routing_interval ); setc( 301, routing_log ); delconfigline( 302 ); addconfigline( 302 . ' ' . ( $p{r_creat} + 2 * $p{r_file} ) ); } elsif( $c[1] eq 'smart' ) { if( $p{mars_config} ne $mars_config ) { # Just append the line. Messy but easy. ;) open( FILE, '>>' . $smart_conf_path ) or die "Could not open $smart_conf_path: $!"; print( FILE "\n" . '$mars_config = \'' . $p{mars_config} . '\';' . "\n" ); close( FILE ); } write_smart_bindery_values( $p{bind_server}, $p{bind_user}, $p{bind_pass} ); if( defined( $p{sync_general_server_name} ) && $p{sync_general_server_name} eq 'on' && defined( $p{bind_server} ) && $p{bind_server} ne '' ) { $p{server_name} = $p{bind_server}; setc( 2, server_name ); } } 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' ) { return( 1 ) if ! run_bindery_cmd( 'nwbocreate', '-S', $server, '-t', '1', '-o', $p{name} ); $c[2] = $p{name}; } else { 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... ;) $id =~ s/[^ ][^ ]/$& /g; $id = join( '', reverse( split( ' ', $id ) ) ); # $id =~ s/ //g; if( scalar( keys( %p ) ) == 0 ) { return( 1 ) if ! run_bindery_cmd( 'nwborm', '-S', $server, '-t', '1', '-o', $c[2] ); } else { 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} ); my $bindery_pipe_payload = < /dev/null' ); } return( 1 ) if ! run_bindery_pipe( 'nwbpset -S ' . $server, $bindery_pipe_payload ); my $bindery_pipe_payload = < /dev/null' ); print( STDERR 'nwpasswd -O ' . $c[2] . ' > /dev/null' . "\n" ); print( FILE $p . "\n" . $p{password} . "\n" . $p{password} . "\n" ); close( FILE ); } } 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' ) { return( 1 ) if ! run_bindery_cmd( 'nwbocreate', '-S', $server, '-t', '2', '-o', $p{name} ); my $bindery_pipe_payload = </>/g; $s =~ s/"/"/g; return $s; } sub queue_test_result_page( $$$$ ) { my( $queue, $ok, $message, $output ) = @_; $queue = queue_test_html_escape( $queue ); $message = queue_test_html_escape( $message ); $output = queue_test_html_escape( $output ); my $title = $ok ? 'Print queue test sent' : 'Print queue test failed'; my $color = $ok ? '#2f5b24' : '#8a1f16'; my $bg = $ok ? '#edf7e8' : '#fff0ed'; print < SMArT Print queue test
$title
$message

Queue: $queue

Command output:

$output
Back to print queues
EOF } our $queue_test_command_rc = 0; sub queue_test_shell_log_quote( $ ) { my $s = $_[0]; $s = '' unless defined( $s ); $s =~ s/'/'"'"'/g; return "'" . $s . "'"; } sub run_queue_test_command( @ ) { my @cmd = @_; my $output = ''; my $tmpdir = '/run/mars-nwe-webui'; my $out; my $rc; $queue_test_command_rc = 0; $tmpdir = '/tmp' if ! -d $tmpdir || ! -w $tmpdir; $out = $tmpdir . '/smart-test-print-output-' . $$ . '-' . int( rand( 1000000 ) ) . '.txt'; $rc = system( @cmd, '>', $out, '2>&1' ); # system(@list) does not process shell redirection. If the platform did # not create an output file, fall back to a shell command with fully quoted # arguments only for capturing output. if( ! -e $out ) { my $shell = join( ' ', map { queue_test_shell_log_quote( $_ ) } @cmd ) . ' > ' . queue_test_shell_log_quote( $out ) . ' 2>&1'; $rc = system( $shell ); } $queue_test_command_rc = $rc; if( open( my $fh, '<', $out ) ) { local $/ = undef; $output = <$fh>; close( $fh ); unlink( $out ); } else { $output = 'could not read nprint output file ' . $out . ': ' . $!; } $output = '' unless defined( $output ); $output =~ s/\r//g; return $output; } sub test_print_queue( $$ ) { my( $server, $queue ) = @_; my $eval_error = ''; my $ok = eval { $server = '' unless defined( $server ); $queue = normalize_queue_name( $queue ); if( $queue eq '' ) { queue_test_result_page( $queue, 0, 'Queue name is empty.', '' ); return 1; } my $nprint = defined( $smart_nprint_path ) && $smart_nprint_path ne '' ? $smart_nprint_path : '/usr/bin/nprint'; if( ! -x $nprint ) { my $msg = 'nprint helper not found or not executable: ' . $nprint; apply_log_line( 'ERROR', 'print queue test failed queue=' . $queue . ' error=' . $msg ); queue_test_result_page( $queue, 0, $msg, '' ); return 1; } my $tmpdir = '/run/mars-nwe-webui'; $tmpdir = '/tmp' if ! -d $tmpdir || ! -w $tmpdir; my $tmp = $tmpdir . '/smart-test-print-' . $$ . '-' . int( rand( 1000000 ) ) . '.txt'; if( ! open( my $fh, '>', $tmp ) ) { my $msg = 'Could not create test print file ' . $tmp . ': ' . $!; apply_log_line( 'ERROR', 'print queue test failed queue=' . $queue . ' error=' . $msg ); queue_test_result_page( $queue, 0, $msg, '' ); return 1; } my $ts = scalar( localtime( time() ) ); print( $fh "SMArT / MARS_NWE print queue test\n" ); print( $fh "Server: $server\n" ); print( $fh "Queue: $queue\n" ); print( $fh "Time: $ts\n" ); print( $fh "\nIf you can read this, the NetWare print queue accepted a test job.\n" ); if( ! close( $fh ) ) { my $msg = 'Could not close test print file ' . $tmp . ': ' . $!; unlink( $tmp ); apply_log_line( 'ERROR', 'print queue test failed queue=' . $queue . ' error=' . $msg ); queue_test_result_page( $queue, 0, $msg, '' ); return 1; } my @cmd = ( $nprint, '-S', $server, '-q', $queue, '-d', 'SMArT test print', $tmp ); apply_log_line( 'INFO', 'print queue test command: ' . join( ' ', map { queue_test_shell_log_quote( $_ ) } @cmd ) ); my $output = run_queue_test_command( @cmd ); unlink( $tmp ); if( $queue_test_command_rc != 0 ) { my $exit = $queue_test_command_rc >> 8; apply_log_line( 'ERROR', 'print queue test failed rc=' . $exit . ' queue=' . $queue . ' output=' . $output ); queue_test_result_page( $queue, 0, 'nprint returned an error.', $output ); return 1; } apply_log_line( 'INFO', 'print queue test ok queue=' . $queue . ( $output ne '' ? ' output=' . $output : '' ) ); queue_test_result_page( $queue, 1, 'Test job was submitted with nprint.', $output ); return 1; }; if( ! $ok ) { $eval_error = $@ if defined( $@ ) && $@ ne ''; $eval_error = 'unknown internal error' if $eval_error eq ''; apply_log_line( 'ERROR', 'print queue test died queue=' . ( defined( $queue ) ? $queue : '' ) . ' error=' . $eval_error ); queue_test_result_page( defined( $queue ) ? $queue : '', 0, 'Internal error while running queue test.', $eval_error ); return 0; } return 1; } sub queue_config_line( $$$ ) { my( $name, $spool, $command ) = @_; $name = normalize_queue_name( $name ); $spool = '' unless defined( $spool ); $command = '' unless defined( $command ); $spool =~ s/^\s+//; $spool =~ s/\s+$//; $command =~ s/^\s+//; $command =~ s/\s+$//; return '21 ' . $name . ' ' . $spool . ' ' . $command; } sub queue_exists_in_bindery( $$ ) { my( $server, $name ) = @_; $name = normalize_queue_name( $name ); return 0 if $name eq ''; my @queues = split( "\n", `nwbols -t 3 -S $server 2>/dev/null` ); foreach my $q ( @queues ) { my @c = split( ' ', $q ); return 1 if defined( $c[0] ) && uc( $c[0] ) eq uc( $name ); } return 0; } sub putp() { $cc =~ s/.*\?//; $cc =~ s/&/\n/g; print <