mirror of
https://github.com/webmin/webmin.git
synced 2026-03-13 06:02:06 +00:00
Compare commits
219 Commits
2.600
...
dev/no-log
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c5470e3fc | ||
|
|
f1d580de1b | ||
|
|
f0b9152ae5 | ||
|
|
5862cde30f | ||
|
|
2de16faafc | ||
|
|
098544473f | ||
|
|
2c82255179 | ||
|
|
fe5cf97cef | ||
|
|
01d650ca06 | ||
|
|
e19dca6882 | ||
|
|
3934ae3e73 | ||
|
|
1a0aa44dba | ||
|
|
2c211e557e | ||
|
|
b3ec013fc9 | ||
|
|
b8481cc1e5 | ||
|
|
c0900ffaf8 | ||
|
|
302f635651 | ||
|
|
d5feb72572 | ||
|
|
cd7f867c09 | ||
|
|
16245144eb | ||
|
|
083b4c7826 | ||
|
|
019cc10b79 | ||
|
|
5d860a6728 | ||
|
|
2e4ec03670 | ||
|
|
b4984e495d | ||
|
|
d7434c61a2 | ||
|
|
f204480957 | ||
|
|
04f1426f33 | ||
|
|
f10385417c | ||
|
|
a502eff346 | ||
|
|
a3416dc830 | ||
|
|
3354a0cc2f | ||
|
|
2a7806be31 | ||
|
|
d4850c3aa3 | ||
|
|
7cbe00ade2 | ||
|
|
540181ae22 | ||
|
|
38cae2fae2 | ||
|
|
9a14c437b8 | ||
|
|
bd3fd5d49b | ||
|
|
fb9f7ead7c | ||
|
|
92ac52893e | ||
|
|
2397653d55 | ||
|
|
209a2cbbc3 | ||
|
|
29c2c6f59d | ||
|
|
c89dc4996f | ||
|
|
bfc1f10b38 | ||
|
|
56a1c323c8 | ||
|
|
f7384bbf05 | ||
|
|
9cd60f4741 | ||
|
|
f778af84a0 | ||
|
|
2eb2be2318 | ||
|
|
38352f5c01 | ||
|
|
07a11f7de6 | ||
|
|
8b76f2ffc8 | ||
|
|
6d014e31cb | ||
|
|
3b05ce756d | ||
|
|
7f322a5df6 | ||
|
|
c47047953b | ||
|
|
f30560d61b | ||
|
|
e13123bed1 | ||
|
|
821548354d | ||
|
|
3a1ea4682d | ||
|
|
3713ee01b8 | ||
|
|
de87e037d4 | ||
|
|
e805f95b48 | ||
|
|
b782a124b5 | ||
|
|
c85d04cc74 | ||
|
|
a6bd5c6ebc | ||
|
|
9fd37b7404 | ||
|
|
ac9456b368 | ||
|
|
399d7a8651 | ||
|
|
c87712ef4e | ||
|
|
c47b63bfcf | ||
|
|
3a86d961ce | ||
|
|
402b21abac | ||
|
|
0816c0a71e | ||
|
|
a38114e623 | ||
|
|
a655f875cf | ||
|
|
79b5b307ed | ||
|
|
5a3b0cfd2d | ||
|
|
fa32009fbb | ||
|
|
a9f4fdc8ca | ||
|
|
b593501cff | ||
|
|
107a2e42f4 | ||
|
|
3572049284 | ||
|
|
d9be1b956f | ||
|
|
b2f54c36ca | ||
|
|
cbc96170c4 | ||
|
|
9d01f3cd9e | ||
|
|
ca7c57b181 | ||
|
|
859580a224 | ||
|
|
30b2e8b6d4 | ||
|
|
63fd68b2f9 | ||
|
|
8402db53d5 | ||
|
|
736c320a60 | ||
|
|
0ecbb04e00 | ||
|
|
2dfa6cfc1b | ||
|
|
4f438906d2 | ||
|
|
c8fcde9579 | ||
|
|
e6184a0036 | ||
|
|
54bb3ee293 | ||
|
|
bfb496ca29 | ||
|
|
58c9efd7ea | ||
|
|
534c529705 | ||
|
|
2d0063129c | ||
|
|
ad37eabdfe | ||
|
|
6d3da61b95 | ||
|
|
4d05e8a2d0 | ||
|
|
e8e804ddca | ||
|
|
202a1b0b78 | ||
|
|
3c1c327530 | ||
|
|
2d7900d550 | ||
|
|
032f4447db | ||
|
|
e9586fb2d8 | ||
|
|
9d95b5c977 | ||
|
|
d9c651d06d | ||
|
|
c70bae48aa | ||
|
|
83abdc8858 | ||
|
|
2833450b48 | ||
|
|
a0c023637f | ||
|
|
6721e13a6b | ||
|
|
2b2814fdf4 | ||
|
|
4dacdc31f6 | ||
|
|
2627ba289e | ||
|
|
b4a67a0d90 | ||
|
|
63af275296 | ||
|
|
5f1bbc4ac2 | ||
|
|
124147205f | ||
|
|
e9b3ed9624 | ||
|
|
ccde77c0e2 | ||
|
|
ffd8171e18 | ||
|
|
65ab502176 | ||
|
|
013aa5a5c6 | ||
|
|
12dca80535 | ||
|
|
89ee635de3 | ||
|
|
af3ddd652f | ||
|
|
e6d214b776 | ||
|
|
45521e9c30 | ||
|
|
123beb03d8 | ||
|
|
72b122dbce | ||
|
|
e9be9b1d53 | ||
|
|
7beeea2106 | ||
|
|
27c5ad998e | ||
|
|
474ca81d4a | ||
|
|
a63c92f77c | ||
|
|
db96a9fd09 | ||
|
|
198cc1c4d8 | ||
|
|
4f681a2db0 | ||
|
|
ab46ec806f | ||
|
|
b1fe988f57 | ||
|
|
94c0826499 | ||
|
|
191d8a255c | ||
|
|
d4ab826097 | ||
|
|
a84c2af1d8 | ||
|
|
476915850a | ||
|
|
2510b985b1 | ||
|
|
db4f5b5e9c | ||
|
|
f3dda46138 | ||
|
|
7a278ad894 | ||
|
|
179d36859a | ||
|
|
eec14b6c1f | ||
|
|
bc44562637 | ||
|
|
a55a9e142b | ||
|
|
7911eaeae1 | ||
|
|
a2cb8daaee | ||
|
|
c4dc034da7 | ||
|
|
8a8d1bc12f | ||
|
|
025ab50ecb | ||
|
|
1f71aebe49 | ||
|
|
03379b0052 | ||
|
|
86a27eff18 | ||
|
|
b338875039 | ||
|
|
e88d16a888 | ||
|
|
f0486443ae | ||
|
|
f55515fdb0 | ||
|
|
ebefe283fd | ||
|
|
d7c49a51c7 | ||
|
|
018ad0062b | ||
|
|
50075dd71d | ||
|
|
f4e573278d | ||
|
|
9bce05f48a | ||
|
|
238756902b | ||
|
|
b597bfb0bd | ||
|
|
00adb84d69 | ||
|
|
0cf0af85cd | ||
|
|
ae89844754 | ||
|
|
9ba627e461 | ||
|
|
bae6e39889 | ||
|
|
5377098de3 | ||
|
|
48291845b4 | ||
|
|
36194ade8e | ||
|
|
d8e9ad116a | ||
|
|
4c5911887c | ||
|
|
392d3aceff | ||
|
|
15a3c91f9d | ||
|
|
58c6d41295 | ||
|
|
7b9bca5b67 | ||
|
|
ba47e2a0d0 | ||
|
|
3b06c4dccb | ||
|
|
1e6611ade5 | ||
|
|
dfa5ac8b21 | ||
|
|
d7cf050d71 | ||
|
|
7945a9f0e6 | ||
|
|
6cd04c131c | ||
|
|
9f4a4fadf7 | ||
|
|
70c7ddf3bb | ||
|
|
17764483dc | ||
|
|
cdc1c82e83 | ||
|
|
d29e5aea99 | ||
|
|
c3a2396986 | ||
|
|
da37f364dd | ||
|
|
0342f06fc8 | ||
|
|
ef05252413 | ||
|
|
74f879dc2b | ||
|
|
94cbabea9e | ||
|
|
2634afb859 | ||
|
|
4bf296447e | ||
|
|
cf458db765 | ||
|
|
af7cb99298 |
8
.github/workflows/webmin.dev+webmin.yml
vendored
8
.github/workflows/webmin.dev+webmin.yml
vendored
@@ -6,15 +6,17 @@ on:
|
||||
- master
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
- prereleased
|
||||
- released
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: webmin/webmin-ci-cd/.github/workflows/master-workflow.yml@main
|
||||
with:
|
||||
build-type: package
|
||||
project-name: webmin
|
||||
project-name: ${{ github.event.repository.name }}
|
||||
is-release: ${{ github.event_name == 'release' }}
|
||||
is-prerelease: ${{ github.event.release.prerelease || false }}
|
||||
secrets:
|
||||
DEV_IP_ADDR: ${{ secrets.DEV_IP_ADDR }}
|
||||
DEV_IP_KNOWN_HOSTS: ${{ secrets.DEV_IP_KNOWN_HOSTS }}
|
||||
@@ -22,4 +24,4 @@ jobs:
|
||||
DEV_UPLOAD_SSH_DIR: ${{ secrets.DEV_UPLOAD_SSH_DIR }}
|
||||
PRERELEASE_UPLOAD_SSH_DIR: ${{ secrets.PRERELEASE_UPLOAD_SSH_DIR }}
|
||||
DEV_SSH_PRV_KEY: ${{ secrets.DEV_SSH_PRV_KEY }}
|
||||
DEV_SIGN_BUILD_REPOS_CMD: ${{ secrets.DEV_SIGN_BUILD_REPOS_CMD }}
|
||||
ALL_GPG_PH2: ${{ secrets.ALL_GPG_PH2 }}
|
||||
|
||||
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,5 +1,49 @@
|
||||
## Changelog
|
||||
|
||||
#### 2.621 (January 25, 2026)
|
||||
* Fix to prevent NAT from dropping idle RPC sessions during long transfers
|
||||
* Fix to improve the message when socket authentication is used in the MySQL/MariaDB module
|
||||
* Fix to make upload tracking work correctly in all situations and on all systems
|
||||
* Fix to correctly display the PHP version in the PHP Configuration module when managing packages
|
||||
* Update Xterm.js to the latest version with lots of improvements and fixes
|
||||
* Update Authentic theme to the latest version with various improvements and fixes:
|
||||
* Fix the support for the cloned Terminal module
|
||||
* Fix error handling for file uploads when the user is out of quota or the system is out of disk space in the File Manager module
|
||||
* Fix to stop loading full file into memory for upload check to prevent memory leak on large uploads in the File Manager module
|
||||
* Fix to permanently save the state of the navigation menu and right-side slider when toggled
|
||||
|
||||
|
||||
#### 2.620 (January 9, 2026)
|
||||
* Add ability to use correct driver depending on the database in MySQL/MariaDB module
|
||||
* Add improvements to BIND DNS module for better key management
|
||||
* Add support for Ubuntu 26.04 development preview
|
||||
* Add a config option to increase the RPC timeout
|
||||
* Add support for EC SSL certificate and key in the ProFTPd module
|
||||
* Add support for using `gpart` in FreeBSD disk management module
|
||||
* Add support for Ed25519 public key in User and Groups module
|
||||
* Fix RPC session timeout during large file transfers
|
||||
* Fix selection and configuration of TLS certificate and key in the ProFTPd module
|
||||
* Update Authentic theme to the latest version with various improvements and fixes:
|
||||
* Add support for multiple scrollable tabs in the File Manager
|
||||
* Fix displaying of the right-side toolbar in File Manager when using Safari
|
||||
* Fix to print menu separator when no virtual servers are added yet in Virtualmin
|
||||
* Fix bugs in white palette
|
||||
* Fix exported file name in data tables
|
||||
|
||||
#### 2.610 (November 23, 2025)
|
||||
* Fix to drop dependency on `IO::Pty` Perl module
|
||||
* Fix `virtual-server` module server-side search to work correctly
|
||||
* Update the Authentic theme to the latest version with various improvements and fixes:
|
||||
- Add a range slider to adjust content page margins more precisely
|
||||
- Add an option to enable rounded corners for content page
|
||||
- Add more customization options for pie charts
|
||||
- Fix to increase clickable area for checkboxes in File Manager
|
||||
- Fix to correct rotation of pin and unpin button for right side slider
|
||||
- Fix color of selected items in the multiselect dropdown
|
||||
- Fix to improve the visibility of disabled checkboxes
|
||||
- Fix to send saved params in the post body when saving theme configuration
|
||||
[More details...](https://github.com/webmin/authentic-theme/releases/tag/26.20)
|
||||
|
||||
#### 2.600 (November 9, 2025)
|
||||
* Add an options to enable the slow query log in the MySQL/MariaDB module [#2560](https://github.com/webmin/webmin/issues/2560)
|
||||
* Add ability to install multiple PHP extensions at once in the PHP Configuration module
|
||||
@@ -256,7 +300,7 @@
|
||||
* Fix to using the `qrencode` command to generate QR codes locally instead of the remote Google Chart API
|
||||
* Fix a number of various other issues
|
||||
|
||||
#### 2.105 (November 09, 2023)
|
||||
#### 2.105 (November 9, 2023)
|
||||
* Fix param to read only headers [sourceforge.net/usermin-bugs#501](https://sourceforge.net/p/webadmin/usermin-bugs/501/)
|
||||
* Fix not to set `reuse` flag on initial Let's Encrypt request
|
||||
* Fix to correctly escape mail file names upon deletion
|
||||
@@ -271,7 +315,7 @@
|
||||
* Fix the absent init script for legacy systems after the initial installation
|
||||
* Update the Authentic theme to the latest version with various fixes and improvements
|
||||
|
||||
#### 2.103 (October 08, 2023)
|
||||
#### 2.103 (October 8, 2023)
|
||||
* Add support for hostname detection using `hostnamectl` command
|
||||
* Add support for other ACME services
|
||||
* Add ability to hide dotfiles in File Manager [#1578](https://github.com/webmin/authentic-theme/issues/1578)
|
||||
@@ -320,7 +364,7 @@
|
||||
* Fix clearing packages caches before checking for updates in status collection #1863
|
||||
* Update the Authentic theme to the latest version
|
||||
|
||||
#### 2.020 (March 08, 2023)
|
||||
#### 2.020 (March 8, 2023)
|
||||
* Add full locale support
|
||||
* Add slave zone file format option in BIND DNS module
|
||||
* Add support for editing ACLs in File Manager
|
||||
@@ -490,10 +534,10 @@ This release updates the built-in Let's Encrypt client, adds support for creatin
|
||||
#### Version 1.930 (August 18, 2019)
|
||||
These updates fix a [security vulnerability](http://webmin.com/security.html) and should be installed IMMEDIATELY by all users. Although it is not exploitable in a Webmin install with the default configuration, upgrading is strongly recommended.
|
||||
|
||||
#### Version 1.920 (July 04, 2019)
|
||||
#### Version 1.920 (July 4, 2019)
|
||||
This update includes the latest theme version, translation updates, the ability to disable hosts file entries, easier monitoring of bootup actions, and a bunch of bugfixes.
|
||||
|
||||
#### Version 1.910 (May 09, 2019)
|
||||
#### Version 1.910 (May 9, 2019)
|
||||
This release includes theme and translation updates, a page for editing package repositories, cron and status module improvements, and a bunch of other bugfixes and small improvements.
|
||||
|
||||
#### Version 1.900 (November 19, 2018)
|
||||
@@ -505,7 +549,7 @@ This version includes Ubuntu 18 network config support, translation updates, mul
|
||||
#### Version 1.880 (March 16, 2018)
|
||||
This version includes German, Catalan and Bulgarian translation updates, a new version of the Authentic theme, support for directly editing the MySQL and PostgreSQL config files, Let's Encrypt bugfixes, more control over system status email notifications, and more.
|
||||
|
||||
#### Version 1.870 (December 08, 2018)
|
||||
#### Version 1.870 (December 8, 2018)
|
||||
This release includes many translation updates, fixes for Let's Encrypt support, UI cleanups, and most importantly a new major version of the Authentic theme.
|
||||
|
||||
#### Version 1.860 (October 10, 2017)
|
||||
@@ -514,7 +558,7 @@ This release includes Let's Encrypt DNS fixes, Majordomo module improvements, XS
|
||||
#### Version 1.850 (June 28, 2017)
|
||||
This release includes Let's Encrypt fixes, Majordomo module improvements, FirewallD forwarding support, translation updates, an update to the Authentic theme, and a bunch of other bugfixes.
|
||||
|
||||
#### Version 1.840 (May 08, 2017)
|
||||
#### Version 1.840 (May 8, 2017)
|
||||
This major release includes a large theme update, XSS security fixes, per-domain SSL cert support, thin-provisioned LVM support, Let's Encrypt improvements, translation updates, and the usual gang of bugfixes. Also available is Usermin 1.710, which contains many of the same updates.
|
||||
|
||||
#### Version 1.830 (December 29, 2016)
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
**Webmin** is a web-based system administration tool for Unix-like servers, and services with about _1,000,000_ yearly installations worldwide. Using it, it is possible to configure operating system internals, such as users, disk quotas, services or configuration files, as well as modify, and control open-source apps, such as BIND DNS Server, Apache HTTP Server, PHP, MySQL, and many more.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://webmin.com/screenshots/#gh-light-mode-only" target="_blank">
|
||||
<img width="1440" alt="Dashboard screenshot" src="https://user-images.githubusercontent.com/4426533/218264253-c08fb45a-8d75-44bf-93b3-37a2ecae3d20.png">
|
||||
<a href="https://webmin.com/screenshots/?theme=light#gh-light-mode-only" target="_blank">
|
||||
<img width="1440" alt="Dashboard screenshot" src="https://github.com/user-attachments/assets/a01d0a78-4130-4665-9284-814955ae1c97">
|
||||
</a>
|
||||
<a href="https://webmin.com/screenshots/#gh-dark-mode-only" target="_blank">
|
||||
<img width="1440" alt="Dashboard screenshot" src="https://user-images.githubusercontent.com/4426533/218265232-31140aa6-ada1-4019-bd75-04240aeabc83.png">
|
||||
<a href="https://webmin.com/screenshots/?theme=dark#gh-dark-mode-only" target="_blank">
|
||||
<img width="1440" alt="Dashboard screenshot" src="https://github.com/user-attachments/assets/da4b90a0-c002-4e10-8b34-5acb251bbec9">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -291,7 +291,13 @@ return &ui_checkbox("d", $user->{'name'}, "", 0).
|
||||
($ro ? "<b>" : "").
|
||||
&ui_link("$cgi?$param=".&urlize($user->{'name'}),
|
||||
$user->{'name'}).
|
||||
($user->{'twofactor_id'} ? "*" : "").
|
||||
($user->{'twofactor_id'}
|
||||
? &ui_tag('sup', '⚷',
|
||||
{ title => $text{'index_twofactor_enabled'},
|
||||
class => 'twofactor-enabled-icon',
|
||||
style => 'font-size: 11px; margin-left: 5px; cursor: default;'.
|
||||
'display: inline-block; transform: rotate(90deg);' } )
|
||||
: "").
|
||||
($ro ? "</b>" : "").
|
||||
($lck ? "</i>" : "");
|
||||
}
|
||||
|
||||
12
acl/lang/en
12
acl/lang/en
@@ -6,6 +6,7 @@ index_screate=Create a new safe user.
|
||||
index_convert=Convert Unix To Webmin Users
|
||||
index_cert=Request an SSL Certificate
|
||||
index_twofactor=Two-Factor Authentication
|
||||
index_twofactor_enabled=Two-factor authentication is enabled for this user
|
||||
index_certmsg=Click this button to request an SSL certificate that will allow you to securely login to Webmin without having to enter a username and password.
|
||||
index_return=user list
|
||||
index_none=None
|
||||
@@ -513,9 +514,18 @@ twofactor_enable=Enroll For Two-Factor Authentication
|
||||
twofactor_header=Two-factor authentication enrollment details
|
||||
twofactor_enrolling=Enrolling for two-factor authentication with provider $1 ..
|
||||
twofactor_failed=.. enrollment failed : $1
|
||||
twofactor_done=.. complete. Your ID with this provider is <tt>$1</tt>.
|
||||
twofactor_done=.. completed, with ID <tt>$1</tt>
|
||||
twofactor_setup=Two-factor authentication has not been enabled on this system yet, but can be turned on using the <a href='$1'>Webmin Configuration</a> module.
|
||||
twofactor_ebutton=No button clicked!
|
||||
twofactor_testdesc=Before logging out, you can test your new two-factor authentication setup here by entering a token. If for some reason it doesn't work, turn off two-factor authentication and try setting it up again.
|
||||
twofactor_testfield=Two-factor token
|
||||
twofactor_test=Validate Token
|
||||
twofactor_terr=Failed to test two-factor setup
|
||||
twofactor_etestuser=Login does not have two-factor enabled!
|
||||
twofactor_testing=Testing two-factor validation with $1 ..
|
||||
twofactor_testfailed=.. test failed! Maybe the wrong token was entered, or your authentication app has not been configured correctly?
|
||||
twofactor_testok=.. test passed! You can now safely login using two-factor authentication.
|
||||
twofactor_testdis=Disable Two-Factor Now
|
||||
|
||||
forgot_title=Send Password Reset Link
|
||||
forgot_err=Failed to send password reset link
|
||||
|
||||
@@ -55,7 +55,7 @@ if ($in{'enable'}) {
|
||||
my $mfunc = "webmin::message_twofactor_".
|
||||
$miniserv{'twofactor_provider'};
|
||||
if (defined(&{\&{$mfunc}})) {
|
||||
print &{\&{$mfunc}}($user);
|
||||
print "<p></p>".&{\&{$mfunc}}($user);
|
||||
}
|
||||
|
||||
# Save user
|
||||
@@ -65,6 +65,15 @@ if ($in{'enable'}) {
|
||||
&webmin_log("twofactor", "user", $user->{'name'},
|
||||
{ 'provider' => $user->{'twofactor_provider'},
|
||||
'id' => $user->{'twofactor_id'} });
|
||||
|
||||
# Show a test form, so the user can validate
|
||||
print &ui_form_start("test_twofactor.cgi");
|
||||
print $text{'twofactor_testdesc'},"<p>\n";
|
||||
print "$text{'twofactor_testfield'} \n",
|
||||
&ui_textbox("test", undef, 12),"\n";
|
||||
print &ui_hidden("user", $in{'user'}) if ($in{'user'});
|
||||
print "<p>\n";
|
||||
print &ui_form_end([ [ undef, $text{'twofactor_test'} ] ]);
|
||||
}
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
|
||||
47
acl/test_twofactor.cgi
Executable file
47
acl/test_twofactor.cgi
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Validate a user-supplied two-factor token
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './acl-lib.pl';
|
||||
our (%in, %text, %access, $base_remote_user);
|
||||
&foreign_require("webmin");
|
||||
&error_setup($text{'twofactor_terr'});
|
||||
&ReadParse();
|
||||
|
||||
# Get the user
|
||||
my @users = &list_users();
|
||||
my $user;
|
||||
if ($in{'user'}) {
|
||||
&can_edit_user($in{'user'}) || &error($text{'edit_euser'});
|
||||
($user) = grep { $_->{'name'} eq $in{'user'} } @users;
|
||||
}
|
||||
else {
|
||||
($user) = grep { $_->{'name'} eq $base_remote_user } @users;
|
||||
}
|
||||
$user || &error($text{'twofactor_euser'});
|
||||
$user->{'twofactor_provider'} || &error($text{'twofactor_etestuser'});
|
||||
my @provs = &webmin::list_twofactor_providers();
|
||||
my ($prov) = grep { $_->[0] eq $user->{'twofactor_provider'} } @provs;
|
||||
|
||||
# Call the validation function
|
||||
&ui_print_header(undef, $text{'twofactor_title'}, "");
|
||||
|
||||
print &text('twofactor_testing', $prov->[1]),"<br>\n";
|
||||
my $func = "webmin::validate_twofactor_".$user->{'twofactor_provider'};
|
||||
my $err = &{\&{$func}}($user->{'twofactor_id'}, $in{'test'},
|
||||
$user->{'twofactor_apikey'});
|
||||
if ($err) {
|
||||
print &text('twofactor_testfailed', $err),"<p>\n";
|
||||
|
||||
print &ui_form_start("save_twofactor.cgi");
|
||||
print &ui_hidden("user", $in{'user'}) if ($in{'user'});
|
||||
print &ui_form_end([ [ "disable", $text{'twofactor_testdis'} ] ]);
|
||||
}
|
||||
else {
|
||||
print $text{'twofactor_testok'},"<p>\n";
|
||||
}
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
@@ -12,7 +12,7 @@ $conf = &get_config();
|
||||
&indexof($in{'file'}, @files) >= 0 || &error($text{'manual_efile'});
|
||||
|
||||
$temp = &transname();
|
||||
&execute_command("cp ".quotemeta($in{'file'})." $temp");
|
||||
&execute_command("cp ".quotemeta($in{'file'})." ".quotemeta($temp));
|
||||
$in{'data'} =~ s/\r//g;
|
||||
&lock_file($in{'file'});
|
||||
&open_tempfile(FILE, ">$in{'file'}");
|
||||
@@ -22,7 +22,7 @@ $in{'data'} =~ s/\r//g;
|
||||
if ($config{'test_manual'}) {
|
||||
$err = &test_config();
|
||||
if ($err) {
|
||||
&execute_command("mv $temp '$in{'file'}'");
|
||||
&execute_command("mv ".quotemeta($temp)." ".quotemeta($in{'file'}));
|
||||
&error(&text('manual_etest', "<pre>$err</pre>"));
|
||||
}
|
||||
}
|
||||
@@ -30,4 +30,3 @@ unlink($temp);
|
||||
&format_config_file($in{'file'});
|
||||
&webmin_log("manual", undef, undef, { 'file' => $in{'file'} });
|
||||
&redirect("index.cgi?mode=global");
|
||||
|
||||
|
||||
@@ -812,9 +812,16 @@ foreach my $d (@$conf) {
|
||||
# Convert a relative path to being under the server root
|
||||
sub server_root
|
||||
{
|
||||
if (!$_[0]) { return undef; }
|
||||
elsif ($_[0] =~ /^\//) { return $_[0]; }
|
||||
else { return "$config{'httpd_dir'}/$_[0]"; }
|
||||
my ($path) = @_;
|
||||
if (!$path) {
|
||||
return undef;
|
||||
}
|
||||
elsif ($path =~ /^\//) {
|
||||
return $path;
|
||||
}
|
||||
else {
|
||||
return "$config{'httpd_dir'}/$path";
|
||||
}
|
||||
}
|
||||
|
||||
sub dump_config
|
||||
@@ -1432,7 +1439,7 @@ sub allowed_auth_file
|
||||
local $_;
|
||||
return 1 if ($access{'dir'} eq '/');
|
||||
return 0 if ($_[0] =~ /\.\./);
|
||||
local $f = &server_root($_[0], &get_config());
|
||||
local $f = &server_root($_[0]);
|
||||
return 0 if (-l $f && !&allowed_auth_file(readlink($f)));
|
||||
local $l = length($access{'dir'});
|
||||
return length($f) >= $l && substr($f, 0, $l) eq $access{'dir'};
|
||||
@@ -1442,7 +1449,7 @@ return length($f) >= $l && substr($f, 0, $l) eq $access{'dir'};
|
||||
# Returns 1 if the directory in some path exists
|
||||
sub directory_exists
|
||||
{
|
||||
local $path = &server_root($_[0], &get_config());
|
||||
local $path = &server_root($_[0]);
|
||||
if ($path =~ /^(\S*\/)([^\/]+)$/) {
|
||||
return -d $1;
|
||||
}
|
||||
@@ -1618,7 +1625,7 @@ local $conf = &get_config();
|
||||
local $pidfilestr = &find_directive_struct("PidFile", $conf);
|
||||
local $pidfile = $pidfilestr ? $pidfilestr->{'words'}->[0]
|
||||
: "logs/httpd.pid";
|
||||
return &server_root($pidfile, $conf);
|
||||
return &server_root($pidfile);
|
||||
}
|
||||
|
||||
# restart_apache()
|
||||
@@ -1803,8 +1810,8 @@ if (!&is_apache_running()) {
|
||||
return $text{'start_eunknown'};
|
||||
}
|
||||
else {
|
||||
$errorlog = &server_root($errorlog, $conf);
|
||||
$out = `tail -5 $errorlog`;
|
||||
$errorlog = &server_root($errorlog);
|
||||
$out = &backquote_command("tail -5 ".quotemeta($errorlog));
|
||||
return "$text{'start_eafter'} : <pre>$out</pre>";
|
||||
}
|
||||
}
|
||||
@@ -1820,7 +1827,7 @@ local $conf = &get_config();
|
||||
local $errorlogstr = &find_directive_struct("ErrorLog", $conf);
|
||||
local $errorlog = $errorlogstr ? $errorlogstr->{'words'}->[0]
|
||||
: "logs/error_log";
|
||||
$errorlog = &server_root($errorlog, $conf);
|
||||
$errorlog = &server_root($errorlog);
|
||||
return $errorlog;
|
||||
}
|
||||
|
||||
@@ -2297,4 +2304,3 @@ sub clear_apache_modules_cache
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
@@ -14,23 +14,23 @@ push(@rv, map { $_->{'file'} } @$conf);
|
||||
# Add mime types file
|
||||
local $mfile = &find_directive("TypesConfig", $conf);
|
||||
if (!$mfile) { $mfile = $config{'mime_types'}; }
|
||||
if (!$mfile) { $mfile = &server_root("etc/mime.types", $conf); }
|
||||
if (!-r $mfile) { $mfile = &server_root("conf/mime.types", $conf); }
|
||||
if (!$mfile) { $mfile = &server_root("etc/mime.types"); }
|
||||
if (!-r $mfile) { $mfile = &server_root("conf/mime.types"); }
|
||||
if ($mfile) {
|
||||
push(@rv, &server_root($mfile, $conf));
|
||||
push(@rv, &server_root($mfile));
|
||||
}
|
||||
|
||||
# Add mime magic file
|
||||
local $magic = &find_directive("MimeMagicFile", $conf);
|
||||
if ($magic) {
|
||||
push(@rv, &server_root($magic, $conf));
|
||||
push(@rv, &server_root($magic));
|
||||
}
|
||||
|
||||
# Add all auth files
|
||||
local $auth;
|
||||
foreach $auth (&find_all_directives($conf, "AuthUserFile"),
|
||||
&find_all_directives($conf, "AuthGroupFile")) {
|
||||
push(@rv, &server_root($auth, $conf));
|
||||
push(@rv, &server_root($auth));
|
||||
}
|
||||
|
||||
return &unique(@rv);
|
||||
|
||||
@@ -119,8 +119,10 @@ elsif ($in{'fmode'} == 1) {
|
||||
else {
|
||||
# Use a user-specified file
|
||||
$f = $in{'file'};
|
||||
&allowed_auth_file($f) ||
|
||||
&error(&text('cvirt_efile', &html_escape($f), $!));
|
||||
}
|
||||
-r $f || open(FILE, ">>$f") || &error(&text('cvirt_efile', &html_escape($f), $!));
|
||||
-r $f || open(FILE, ">>", $f) || &error(&text('cvirt_efile', &html_escape($f), $!));
|
||||
close(FILE);
|
||||
|
||||
&lock_apache_files();
|
||||
|
||||
@@ -22,9 +22,9 @@ print &ui_form_end([ [ "", $text{'save'} ] ]);
|
||||
if ($in{'type'} == 6) {
|
||||
$mfile = &find_directive("TypesConfig", $conf);
|
||||
if (!$mfile) { $mfile = $config{'mime_types'}; }
|
||||
if (!$mfile) { $mfile = &server_root("etc/mime.types", $conf); }
|
||||
if (!-r $mfile) { $mfile = &server_root("conf/mime.types", $conf); }
|
||||
$mfile = &server_root($mfile, $conf);
|
||||
if (!$mfile) { $mfile = &server_root("etc/mime.types"); }
|
||||
if (!-r $mfile) { $mfile = &server_root("conf/mime.types"); }
|
||||
$mfile = &server_root($mfile);
|
||||
print &ui_hr();
|
||||
print &ui_subheading($text{'global_mime'});
|
||||
print "$text{'global_mimedesc'}<p>\n";
|
||||
|
||||
@@ -5,9 +5,19 @@
|
||||
require './apache-lib.pl';
|
||||
&ReadParse();
|
||||
$access{'global'}==1 || &error($text{'mime_ecannot'});
|
||||
|
||||
# Validate that the file parameter matches the actual MIME types file
|
||||
$conf = &get_config();
|
||||
$mfile = &find_directive("TypesConfig", $conf);
|
||||
if (!$mfile) { $mfile = $config{'mime_types'}; }
|
||||
if (!$mfile) { $mfile = &server_root("etc/mime.types", $conf); }
|
||||
if (!-r $mfile) { $mfile = &server_root("conf/mime.types", $conf); }
|
||||
$mfile = &server_root($mfile);
|
||||
$in{'file'} eq $mfile || &error($text{'mime_ecannot'});
|
||||
|
||||
if (defined($in{'line'})) {
|
||||
&ui_print_header(undef, $text{'mime_edit'}, "");
|
||||
open(MIME, "<$in{'file'}");
|
||||
open(MIME, "<", $in{'file'});
|
||||
for($i=0; $i<=$in{'line'}; $i++) {
|
||||
$line = <MIME>;
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ manual_efile=Invalid Apache config file
|
||||
manual_etest=Configuration file error detected : $1
|
||||
manual_editfile=Edit config file:
|
||||
manual_switch=Edit
|
||||
manual_evirt=Virtual host could not be found after manual changes - maybe the ServerName was changed?
|
||||
|
||||
dir_title=Per-Directory Options
|
||||
dir_proxyall=All proxy requests
|
||||
|
||||
@@ -11,7 +11,7 @@ $conf = &get_config();
|
||||
&error(&text('authg_ecannot', $in{'file'}));
|
||||
$desc = &text('authg_header', "<tt>$in{'file'}</tt>");
|
||||
&ui_print_header($desc, $text{'authg_title'}, "");
|
||||
$f = &server_root($in{'file'}, $conf);
|
||||
$f = &server_root($in{'file'});
|
||||
|
||||
@groups = sort { $a->{'name'} cmp $b->{'name'} } &list_authgroups($in{'file'});
|
||||
if (@groups) {
|
||||
|
||||
@@ -11,7 +11,7 @@ $conf = &get_config();
|
||||
&error(&text('authu_ecannot', $in{'file'}));
|
||||
$desc = &text('authu_header', "<tt>$in{'file'}</tt>");
|
||||
&ui_print_header($desc, $text{'authu_title'}, "");
|
||||
$f = &server_root($in{'file'}, $conf);
|
||||
$f = &server_root($in{'file'});
|
||||
|
||||
@users = sort { $a cmp $b } &list_authusers($f);
|
||||
if (@users) {
|
||||
|
||||
@@ -73,6 +73,14 @@ if ($config{'test_manual'}) {
|
||||
&error(&text('manual_etest',
|
||||
"<pre>".&html_escape($err)."</pre>"));
|
||||
}
|
||||
if (defined($in{'virt'}) && !defined($in{'idx'})) {
|
||||
undef(@get_config_cache);
|
||||
($conf, $v) = &get_virtual_config($in{'virt'});
|
||||
if (!$v) {
|
||||
©_source_dest($temp, $file);
|
||||
&error($text{'manual_evirt'});
|
||||
}
|
||||
}
|
||||
}
|
||||
unlink($temp);
|
||||
&unlock_file($file);
|
||||
|
||||
@@ -34,11 +34,10 @@ if (!$running) {
|
||||
&error($text{'restart_eunknown'});
|
||||
}
|
||||
else {
|
||||
$errorlog = &server_root($errorlog, $conf);
|
||||
$out = `tail -5 $errorlog`;
|
||||
$errorlog = &server_root($errorlog);
|
||||
$out = &backquote_command("tail -5 ".quotemeta($errorlog));
|
||||
&error("<pre>$out</pre>");
|
||||
}
|
||||
}
|
||||
&webmin_log("apply");
|
||||
&redirect($in{'redir'});
|
||||
|
||||
|
||||
@@ -6,13 +6,22 @@ require './apache-lib.pl';
|
||||
&ReadParse();
|
||||
$access{'global'}==1 || &error($text{'mime_ecannot'});
|
||||
|
||||
# Validate that the file parameter matches the actual MIME types file
|
||||
$conf = &get_config();
|
||||
$mfile = &find_directive("TypesConfig", $conf);
|
||||
if (!$mfile) { $mfile = $config{'mime_types'}; }
|
||||
if (!$mfile) { $mfile = &server_root("etc/mime.types", $conf); }
|
||||
if (!-r $mfile) { $mfile = &server_root("conf/mime.types", $conf); }
|
||||
$mfile = &server_root($mfile);
|
||||
$in{'file'} eq $mfile || &error($text{'mime_ecannot'});
|
||||
|
||||
&error_setup($text{'mime_err'});
|
||||
if ($in{'type'} !~ /^(\S+)\/(\S+)$/) {
|
||||
&error(&text('mime_etype', $in{'type'}));
|
||||
}
|
||||
|
||||
&lock_file($in{'file'});
|
||||
open(MIME, "<$in{'file'}");
|
||||
open(MIME, "<", $in{'file'});
|
||||
@mime = <MIME>;
|
||||
close(MIME);
|
||||
$line = "$in{'type'} ".join(" ", split(/\s+/, $in{'exts'}))."\n";
|
||||
|
||||
721
bin/webmin
721
bin/webmin
@@ -1,408 +1,435 @@
|
||||
#!/usr/bin/env perl
|
||||
# Webmin CLI - Allows performing a variety of common Webmin-related
|
||||
# functions on the command line.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
BEGIN { $Pod::Usage::Formatter = 'Pod::Text::Color'; }
|
||||
use 5.010; # Version in CentOS 6
|
||||
use Getopt::Long qw(:config permute pass_through);
|
||||
use 5.010;
|
||||
use Getopt::Long qw(:config no_ignore_case permute pass_through);
|
||||
use Term::ANSIColor qw(:constants);
|
||||
use Pod::Usage;
|
||||
|
||||
# Check if root
|
||||
if ($> != 0) {
|
||||
die BRIGHT_RED, "Error: ", RESET, BRIGHT_YELLOW,"webmin", RESET,
|
||||
" command must be run as root\n";
|
||||
exit 1;
|
||||
}
|
||||
die BRIGHT_RED,
|
||||
"Error: ", RESET, BRIGHT_YELLOW,"webmin", RESET,
|
||||
" command must be run as root\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $a0 = $ARGV[0];
|
||||
|
||||
sub main {
|
||||
my ( %opt, $subcmd );
|
||||
GetOptions(
|
||||
'help|h' => \$opt{'help'},
|
||||
'config|c=s' => \$opt{'config'},
|
||||
'list-commands|l' => \$opt{'list'},
|
||||
'describe|d' => \$opt{'describe'},
|
||||
'man|m' => \$opt{'man'},
|
||||
'version|v' => \$opt{'version'},
|
||||
'versions' => \$opt{'versions'},
|
||||
'<>' => sub {
|
||||
# Handle unrecognized options, inc. subcommands.
|
||||
my($arg) = @_;
|
||||
if ($arg =~ m{^-}) {
|
||||
say "Usage error: Unknown option $arg.";
|
||||
pod2usage(0);
|
||||
} else {
|
||||
# It must be a subcommand.
|
||||
$subcmd = $arg;
|
||||
die "!FINISH";
|
||||
}
|
||||
}
|
||||
);
|
||||
sub main
|
||||
{
|
||||
my ( %opt, $subcmd );
|
||||
GetOptions(
|
||||
'help|h' => \$opt{'help'},
|
||||
'config|c=s' => \$opt{'config'},
|
||||
'list-commands|l' => \$opt{'list'},
|
||||
'describe|d' => \$opt{'describe'},
|
||||
'man|m' => \$opt{'man'},
|
||||
'version|v' => \$opt{'version'},
|
||||
'versions|V' => \$opt{'versions'},
|
||||
'<>' => sub {
|
||||
# Handle unrecognized options, inc. subcommands.
|
||||
my($arg) = @_;
|
||||
if ($arg =~ m{^-}) {
|
||||
say "Usage error: Unknown option $arg.";
|
||||
pod2usage(0);
|
||||
}
|
||||
else {
|
||||
# It must be a subcommand.
|
||||
$subcmd = $arg;
|
||||
die "!FINISH";
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# Set defaults
|
||||
$opt{'config'} ||= "/etc/webmin";
|
||||
$opt{'commands'} = $a0;
|
||||
# Set defaults
|
||||
$opt{'config'} ||= "/etc/webmin";
|
||||
$opt{'commands'} = $a0;
|
||||
|
||||
# Load libs
|
||||
loadlibs(\%opt);
|
||||
# Load libs
|
||||
loadlibs(\%opt);
|
||||
|
||||
my @remain = @ARGV;
|
||||
# List commands?
|
||||
if ($opt{'list'}) {
|
||||
list_commands(\%opt);
|
||||
exit 0;
|
||||
} elsif ($opt{'version'} || $opt{'versions'}) {
|
||||
# Load libs
|
||||
my $ver_checked = sub {
|
||||
my ($ver_remote, $ver_curr) = @_;
|
||||
if ($ver_remote && $ver_curr &&
|
||||
compare_version_numbers($ver_remote, $ver_curr) > 0 ) {
|
||||
return (BRIGHT_RED, $ver_curr, RESET, DARK, " (" . RESET, BRIGHT_GREEN, $ver_remote, RESET, DARK . " is available)", RESET);
|
||||
} else {
|
||||
return GREEN, $ver_curr, RESET;
|
||||
}
|
||||
};
|
||||
my $print_mod_vers = sub {
|
||||
my ($module_type, $modules_list, $prod_root, $prod_ver, $versions_remote_local) = @_;
|
||||
my @minfo;
|
||||
if (ref($modules_list)) {
|
||||
my $head;
|
||||
my @modules_list = sort(@{$modules_list});
|
||||
foreach my $mod (@modules_list) {
|
||||
my %mod_info;
|
||||
read_file($mod, \%mod_info);
|
||||
my $mod_ver = $mod_info{'version_actual'} || $mod_info{'version'};
|
||||
my $mod_desc = $mod_info{'desc'};
|
||||
if ($mod_ver && $prod_ver && $mod_desc && $prod_ver !~ /^$mod_ver/) {
|
||||
say CYAN, " $module_type: ", RESET if (!$head++);
|
||||
my ($mod_dir) = $mod =~ m/$prod_root\/(.*?)\//;
|
||||
push(@minfo, {'desc' => $mod_desc, 'ver' => $mod_ver, 'dir' => $mod_dir});
|
||||
}
|
||||
}
|
||||
@minfo = sort { $a->{'desc'} cmp $b->{'desc'} } @minfo;
|
||||
foreach my $mod (@minfo) {
|
||||
say " $mod->{'desc'}: " , &$ver_checked($versions_remote_local->{$mod->{'dir'}}, $mod->{'ver'}), DARK " [$mod->{'dir'}]", RESET;
|
||||
}
|
||||
}
|
||||
};
|
||||
my @remain = @ARGV;
|
||||
# List commands?
|
||||
if ($opt{'list'}) {
|
||||
list_commands(\%opt);
|
||||
exit 0;
|
||||
}
|
||||
elsif ($opt{'version'} || $opt{'versions'}) {
|
||||
# Load libs
|
||||
my $print_mod_vers = sub {
|
||||
my ($module_type, $modules_list, $prod_root, $prod_ver) = @_;
|
||||
return if (!ref($modules_list));
|
||||
# Gather module info
|
||||
my @mods;
|
||||
foreach my $mod (@{$modules_list}) {
|
||||
my %mi;
|
||||
read_file($mod, \%mi);
|
||||
my $ver = $mi{'version'};
|
||||
my ($dir) = $mod =~ m/$prod_root\/(.*?)\//;
|
||||
next if (!$ver || !$mi{'desc'} || !$dir);
|
||||
next if ($prod_ver =~ /^\Q$ver\E/);
|
||||
push(@mods, { desc => $mi{'desc'}, ver => $ver,
|
||||
dir => $dir });
|
||||
}
|
||||
# Print sorted by description
|
||||
my $head;
|
||||
foreach my $m (sort { $a->{'desc'} cmp $b->{'desc'} } @mods) {
|
||||
my $mod_ver = $m->{'ver'};
|
||||
if (-r "$prod_root/$m->{'dir'}/module.info") {
|
||||
eval { no warnings 'once';
|
||||
local $main::error_must_die = 1;
|
||||
&foreign_require($m->{'dir'}) };
|
||||
# Get module edition if available
|
||||
my $ed;
|
||||
$ed = eval { &foreign_call(
|
||||
$m->{'dir'},
|
||||
"get_module_edition")
|
||||
} if (&foreign_defined($m->{'dir'},
|
||||
"get_module_edition"));
|
||||
$mod_ver .= " $ed" if ($ed);
|
||||
}
|
||||
say CYAN, " $module_type: ", RESET if (!$head++);
|
||||
say " $m->{'desc'}: ", GREEN, $mod_ver, RESET,
|
||||
DARK " [$m->{'dir'}]", RESET;
|
||||
}
|
||||
};
|
||||
|
||||
my $root = root($opt{'config'});
|
||||
if ($root && -d $root) {
|
||||
require("$root/web-lib-funcs.pl");
|
||||
my $root = root($opt{'config'});
|
||||
if ($root && -d $root) {
|
||||
$ENV{'WEBMIN_CONFIG'} = $opt{'config'};
|
||||
no warnings 'once';
|
||||
@main::root_directories = ($root);
|
||||
$main::root_directory = $root;
|
||||
*unique = sub { my %seen; grep { !$seen{$_}++ } @_ }
|
||||
if (!defined(&unique));
|
||||
use warnings;
|
||||
require("$root/web-lib-funcs.pl");
|
||||
|
||||
# Try to get remote versions first
|
||||
my %versions_remote;
|
||||
if ($opt{'versions'}) {
|
||||
my ($latest_known_versions_remote, $latest_known_versions_remote_error);
|
||||
http_download("virtualmin.com", 443, '/software-latest',
|
||||
\$latest_known_versions_remote, \$latest_known_versions_remote_error,
|
||||
undef, 1, undef, undef, 5);
|
||||
if ($latest_known_versions_remote &&
|
||||
!$latest_known_versions_remote_error) {
|
||||
%versions_remote = map {
|
||||
my ($k, $v) = split(/=/, $_, 2);
|
||||
defined($v) ? ($k => $v) : ();
|
||||
} split(/\n/, $latest_known_versions_remote);
|
||||
} elsif ($latest_known_versions_remote_error) {
|
||||
say BRIGHT_YELLOW, "Warning: ", RESET, "Cannot fetch remote packages versions list - $latest_known_versions_remote_error";
|
||||
}
|
||||
}
|
||||
# Get Webmin version installed
|
||||
my $ver1 = "$root/version";
|
||||
my $ver2 = "$opt{'config'}/version";
|
||||
my $ver = read_file_contents($ver1) ||
|
||||
read_file_contents($ver2);
|
||||
my $verrel_file = "$root/release";
|
||||
my $verrel = -r $verrel_file
|
||||
? read_file_contents($verrel_file) : "";
|
||||
if ($verrel) {
|
||||
$verrel = ":@{[trim($verrel)]}";
|
||||
}
|
||||
$ver = trim($ver);
|
||||
if ($ver) {
|
||||
if ($opt{'version'}) {
|
||||
say "$ver$verrel";
|
||||
exit 0;
|
||||
}
|
||||
else {
|
||||
say CYAN, "Webmin: ", RESET, GREEN,
|
||||
"$ver$verrel", RESET,
|
||||
DARK " [$root]", RESET;
|
||||
}
|
||||
}
|
||||
else {
|
||||
say RED, "Error: ", RESET,
|
||||
"Cannot determine Webmin version";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Get Webmin version installed
|
||||
my $ver1 = "$root/version";
|
||||
my $ver2 = "$opt{'config'}/version";
|
||||
my $ver = read_file_contents($ver1) || read_file_contents($ver2);
|
||||
my $verrel_file = "$root/release";
|
||||
my $verrel = -r $verrel_file ? read_file_contents($verrel_file) : "";
|
||||
if ($verrel) {
|
||||
$verrel = ":@{[trim($verrel)]}";
|
||||
}
|
||||
$ver = trim($ver);
|
||||
if ($ver) {
|
||||
if ($opt{'version'}) {
|
||||
say "$ver$verrel";
|
||||
exit 0;
|
||||
} else {
|
||||
say CYAN, "Webmin: ", RESET, &$ver_checked($versions_remote{'webmin'}, "$ver$verrel"), DARK " [$root]", RESET;
|
||||
}
|
||||
} else {
|
||||
say RED, "Error: ", RESET, "Cannot determine Webmin version";
|
||||
exit 1;
|
||||
}
|
||||
# Get other Webmin themes/modules versions if available
|
||||
my ($dir, @themes, @mods);
|
||||
if (opendir($dir, $root)) {
|
||||
while (my $file = readdir($dir)) {
|
||||
my $theme_info_file =
|
||||
"$root/$file/theme.info";
|
||||
push(@themes, $theme_info_file)
|
||||
if (-r $theme_info_file);
|
||||
my $mod_info_file = "$root/$file/module.info";
|
||||
push(@mods, $mod_info_file)
|
||||
if (-r $mod_info_file);
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
&$print_mod_vers('Themes', \@themes, $root, $ver);
|
||||
&$print_mod_vers('Modules', \@mods, $root, $ver);
|
||||
|
||||
# Get other Webmin themes/modules versions if available
|
||||
my ($dir, @themes, @mods);
|
||||
if (opendir($dir, $root)) {
|
||||
while (my $file = readdir($dir)) {
|
||||
my $theme_info_file = "$root/$file/theme.info";
|
||||
push(@themes, $theme_info_file)
|
||||
if (-r $theme_info_file);
|
||||
# Check for Usermin
|
||||
my $wmumconfig = "$opt{'config'}/usermin/config";
|
||||
if (-r $wmumconfig) {
|
||||
my %wmumconfig;
|
||||
read_file($wmumconfig, \%wmumconfig);
|
||||
|
||||
my $mod_info_file = "$root/$file/module.info";
|
||||
push(@mods, $mod_info_file)
|
||||
if (-r $mod_info_file);
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
&$print_mod_vers('Themes', \@themes, $root, $ver, \%versions_remote);
|
||||
&$print_mod_vers('Modules', \@mods, $root, $ver, \%versions_remote);
|
||||
# Usermin config dir
|
||||
$wmumconfig = $wmumconfig{'usermin_dir'};
|
||||
if ($wmumconfig) {
|
||||
my %uminiserv;
|
||||
read_file("$wmumconfig/miniserv.conf",
|
||||
\%uminiserv);
|
||||
my $uroot = $uminiserv{'root'};
|
||||
|
||||
# Check for Usermin
|
||||
my $wmumconfig = "$opt{'config'}/usermin/config";
|
||||
if (-r $wmumconfig) {
|
||||
my %wmumconfig;
|
||||
read_file($wmumconfig, \%wmumconfig);
|
||||
# Get Usermin version installed
|
||||
if ($uroot && -d $uroot) {
|
||||
my $uver1 = "$uroot/version";
|
||||
my $uver2 = "$wmumconfig/version";
|
||||
my $uver = read_file_contents($uver1) ||
|
||||
read_file_contents($uver2);
|
||||
my $uverrel_file = "$uroot/release";
|
||||
my $uverrel = -r $uverrel_file
|
||||
? read_file_contents($uverrel_file) : "";
|
||||
$uverrel = ":@{[trim($uverrel)]}" if ($uverrel);
|
||||
$uver = trim($uver) . $uverrel;
|
||||
if ($uver) {
|
||||
say CYAN, "Usermin: ", RESET, GREEN, $uver, RESET, DARK " [$uroot]", RESET;
|
||||
my ($udir, @uthemes, @umods);
|
||||
if (opendir($udir, "$uroot")) {
|
||||
while (my $file = readdir($udir)) {
|
||||
my $theme_info_file = "$uroot/$file/theme.info";
|
||||
push(@uthemes, $theme_info_file)
|
||||
if (-r $theme_info_file);
|
||||
|
||||
# Usermin config dir
|
||||
$wmumconfig = $wmumconfig{'usermin_dir'};
|
||||
if ($wmumconfig) {
|
||||
my %uminiserv;
|
||||
read_file("$wmumconfig/miniserv.conf", \%uminiserv);
|
||||
my $uroot = $uminiserv{'root'};
|
||||
my $mod_info_file = "$uroot/$file/module.info";
|
||||
push(@umods, $mod_info_file)
|
||||
if (-r $mod_info_file);
|
||||
|
||||
# Get Usermin version installed
|
||||
if ($uroot && -d $uroot) {
|
||||
my $uver1 = "$uroot/version";
|
||||
my $uver2 = "$wmumconfig/version";
|
||||
my $uver = read_file_contents($uver1) || read_file_contents($uver2);
|
||||
my $uverrel_file = "$uroot/release";
|
||||
my $uverrel = -r $uverrel_file ? read_file_contents($uverrel_file) : "";
|
||||
if ($uverrel) {
|
||||
$uverrel = ":@{[trim($uverrel)]}";
|
||||
}
|
||||
$uver = trim($uver) . $uverrel;
|
||||
if ($uver) {
|
||||
say CYAN, "Usermin: ", RESET, &$ver_checked($versions_remote{'usermin'}, $uver), DARK " [$uroot]", RESET;
|
||||
my ($udir, @uthemes, @umods);
|
||||
if (opendir($udir, "$uroot")) {
|
||||
while (my $file = readdir($udir)) {
|
||||
my $theme_info_file = "$uroot/$file/theme.info";
|
||||
push(@uthemes, $theme_info_file)
|
||||
if (-r $theme_info_file);
|
||||
}
|
||||
}
|
||||
closedir($udir);
|
||||
&$print_mod_vers('Themes', \@uthemes, $uroot, $uver);
|
||||
&$print_mod_vers('Modules', \@umods, $uroot, $uver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
elsif ($opt{'man'} || $opt{'help'} || !defined($remain[0])) {
|
||||
# Show the full manual page
|
||||
man_command(\%opt, $subcmd);
|
||||
exit 0;
|
||||
}
|
||||
elsif ($subcmd) {
|
||||
run_command( \%opt, $subcmd, \@remain );
|
||||
}
|
||||
|
||||
my $mod_info_file = "$uroot/$file/module.info";
|
||||
push(@umods, $mod_info_file)
|
||||
if (-r $mod_info_file);
|
||||
|
||||
}
|
||||
}
|
||||
closedir($udir);
|
||||
&$print_mod_vers('Themes', \@uthemes, $uroot, $uver, \%versions_remote);
|
||||
&$print_mod_vers('Modules', \@umods, $uroot, $uver, \%versions_remote);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit 0;
|
||||
} elsif ($opt{'man'} || $opt{'help'} || !defined($remain[0])) {
|
||||
# Show the full manual page
|
||||
man_command(\%opt, $subcmd);
|
||||
exit 0;
|
||||
} elsif ($subcmd) {
|
||||
run_command( \%opt, $subcmd, \@remain );
|
||||
}
|
||||
|
||||
exit 0;
|
||||
exit 0;
|
||||
}
|
||||
exit main( \@ARGV ) if !caller(0);
|
||||
|
||||
# run_command - Run a subcommand
|
||||
# $optref is a reference to an options object passed down from global options
|
||||
# like --help or a --config path.
|
||||
sub run_command {
|
||||
my ( $optref, $subcmd, $remainref ) = @_;
|
||||
sub run_command
|
||||
{
|
||||
my ( $optref, $subcmd, $remainref ) = @_;
|
||||
|
||||
# Load libs
|
||||
loadlibs($optref);
|
||||
# Load libs
|
||||
loadlibs($optref);
|
||||
|
||||
# Figure out the Webmin root directory
|
||||
my $root = root($optref->{'config'});
|
||||
# Figure out the Webmin root directory
|
||||
my $root = root($optref->{'config'});
|
||||
|
||||
my (@commands) = list_commands($optref);
|
||||
if (! grep( /^$subcmd$/, @commands ) ) {
|
||||
say RED, "Error: ", RESET, "Command \`$subcmd\` doesn't exist", RESET;
|
||||
exit 1;
|
||||
}
|
||||
my (@commands) = list_commands($optref);
|
||||
if (! grep( /^$subcmd$/, @commands ) ) {
|
||||
say RED, "Error: ", RESET, "Command \`$subcmd\` doesn't exist", RESET;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $command_path = get_command_path($root, $subcmd, $optref);
|
||||
my $command_path = get_command_path($root, $subcmd, $optref);
|
||||
|
||||
# Merge the options
|
||||
# Only handling config, right now...
|
||||
# XXX Should we do this with libraries instead of commands?
|
||||
# Maybe detect .pm for that possibility.
|
||||
my @allopts = ("--config", "$optref->{'config'}", @$remainref);
|
||||
# Run that binch
|
||||
system($command_path, @allopts);
|
||||
# Try to exit with the passed through exit code (rarely used, but
|
||||
# why not?)
|
||||
if ($? == -1) {
|
||||
say RED, "Error: ", RESET, "Failed to execute \`$command_path\`: $!";
|
||||
exit 1;
|
||||
} else {
|
||||
exit $? >> 8;
|
||||
}
|
||||
# Merge the options
|
||||
my @allopts = ("--config", "$optref->{'config'}", @$remainref);
|
||||
# Run
|
||||
system($command_path, @allopts);
|
||||
# Try to exit with the passed through exit code (rarely used, but why not?)
|
||||
if ($? == -1) {
|
||||
say RED, "Error: ", RESET, "Failed to execute \`$command_path\`: $!";
|
||||
exit 1;
|
||||
}
|
||||
else {
|
||||
exit $? >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
sub get_command_path {
|
||||
my ($root, $subcmd, $optref) = @_;
|
||||
sub get_command_path
|
||||
{
|
||||
my ($root, $subcmd, $optref) = @_;
|
||||
|
||||
# Load libs
|
||||
loadlibs($optref);
|
||||
# Load libs
|
||||
loadlibs($optref);
|
||||
|
||||
# Check for a root-level command (in "$root/bin")
|
||||
my $command_path;
|
||||
if ($subcmd) {
|
||||
$command_path = File::Spec->catfile($root, 'bin', $subcmd);
|
||||
} else {
|
||||
$command_path = File::Spec->catfile($root, 'bin', 'webmin');
|
||||
}
|
||||
my $module_name;
|
||||
my $command;
|
||||
if ( -x $command_path) {
|
||||
$command = $command_path;
|
||||
} else {
|
||||
# Try to extract a module name from the command
|
||||
# Get list of directories
|
||||
opendir (my $DIR, $root);
|
||||
my @module_dirs = grep { -d "$root/$_" } readdir($DIR);
|
||||
# See if any of them are a substring of $subcmd
|
||||
for my $dir (@module_dirs) {
|
||||
if (index($subcmd, $dir) == 0) {
|
||||
$module_name = $dir;
|
||||
my $barecmd = substr($subcmd, -(length($subcmd)-length($module_name)-1));
|
||||
$command = File::Spec->catfile($root, $dir, 'bin', $barecmd);
|
||||
# Could be .pl or no extension
|
||||
if ( -x $command ) {
|
||||
last;
|
||||
} elsif ( -x $command . ".pl" ) {
|
||||
$command = $command . ".pl";
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($optref->{'commands'} &&
|
||||
$optref->{'commands'} =~ /^(stats|status|start|stop|restart|reload|force-restart|force-reload|kill)$/) {
|
||||
exit system("$0 server $optref->{'commands'}");
|
||||
} elsif ($command) {
|
||||
return $command;
|
||||
} else {
|
||||
die RED, "Unrecognized subcommand: $subcmd", RESET , "\n";
|
||||
}
|
||||
# Check for a root-level command (in "$root/bin")
|
||||
my $command_path;
|
||||
if ($subcmd) {
|
||||
$command_path = File::Spec->catfile($root, 'bin', $subcmd);
|
||||
}
|
||||
else {
|
||||
$command_path = File::Spec->catfile($root, 'bin', 'webmin');
|
||||
}
|
||||
|
||||
my $module_name;
|
||||
my $command;
|
||||
if ( -x $command_path) {
|
||||
$command = $command_path;
|
||||
}
|
||||
else {
|
||||
# Try to extract a module name from the command
|
||||
# Get list of directories
|
||||
opendir (my $DIR, $root);
|
||||
my @module_dirs = grep { -d "$root/$_" } readdir($DIR);
|
||||
closedir($DIR);
|
||||
# See if any of them are a substring of $subcmd
|
||||
for my $dir (@module_dirs) {
|
||||
if (index($subcmd, $dir) == 0) {
|
||||
$module_name = $dir;
|
||||
my $barecmd = substr($subcmd, -(length($subcmd)-length($module_name)-1));
|
||||
$command = File::Spec->catfile($root, $dir, 'bin', $barecmd);
|
||||
# Could be .pl or no extension
|
||||
if ( -x $command ) {
|
||||
last;
|
||||
}
|
||||
elsif ( -x $command . ".pl" ) {
|
||||
$command = $command . ".pl";
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($optref->{'commands'} &&
|
||||
$optref->{'commands'} =~ /^(stats|status|start|stop|restart|reload|force-restart|force-reload|kill)$/) {
|
||||
exit system("$0 server $optref->{'commands'}");
|
||||
}
|
||||
elsif ($command) {
|
||||
return $command;
|
||||
}
|
||||
else {
|
||||
die RED, "Unrecognized subcommand: $subcmd", RESET , "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub list_commands {
|
||||
my ($optref) = @_;
|
||||
sub list_commands
|
||||
{
|
||||
my ($optref) = @_;
|
||||
|
||||
my $root = root($optref->{'config'});
|
||||
my @commands;
|
||||
my $root = root($optref->{'config'});
|
||||
my @commands;
|
||||
|
||||
# Find and list global commands
|
||||
for my $command (glob ("$root/bin/*")) {
|
||||
my ($bin, $path) = fileparse($command);
|
||||
if ($bin =~ "webmin") {
|
||||
next;
|
||||
}
|
||||
if ($optref->{'describe'}) {
|
||||
# Display name and description
|
||||
say YELLOW, "$bin", RESET;
|
||||
pod2usage( -verbose => 99,
|
||||
-sections => [ qw(DESCRIPTION) ],
|
||||
-input => $command,
|
||||
-exitval => "NOEXIT");
|
||||
} else {
|
||||
if (wantarray) {
|
||||
push(@commands, $bin);
|
||||
} else {
|
||||
# Just list the names
|
||||
say "$bin";
|
||||
}
|
||||
}
|
||||
}
|
||||
# Find and list global commands
|
||||
for my $command (glob ("$root/bin/*")) {
|
||||
my ($bin, $path) = fileparse($command);
|
||||
if ($bin =~ "webmin") {
|
||||
next;
|
||||
}
|
||||
if ($optref->{'describe'}) {
|
||||
# Display name and description
|
||||
say YELLOW, "$bin", RESET;
|
||||
pod2usage( -verbose => 99,
|
||||
-sections => [ qw(DESCRIPTION) ],
|
||||
-input => $command,
|
||||
-exitval => "NOEXIT");
|
||||
}
|
||||
else {
|
||||
if (wantarray) {
|
||||
push(@commands, $bin);
|
||||
}
|
||||
else {
|
||||
# Just list the names
|
||||
say "$bin";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my @modules;
|
||||
# Find all module directories with something in bin
|
||||
for my $command (glob ("$root/*/bin/*")) {
|
||||
my ($bin, $path) = fileparse($command);
|
||||
my $module = (split /\//, $path)[-2];
|
||||
if ($optref->{'describe'}) {
|
||||
# Display name and description
|
||||
say YELLOW, "$module-$bin", RESET;
|
||||
pod2usage( -verbose => 99,
|
||||
-sections => [ qw(DESCRIPTION) ],
|
||||
-input => $command,
|
||||
-exitval => "NOEXIT");
|
||||
} else {
|
||||
if (wantarray) {
|
||||
push(@modules, "$module-$bin");
|
||||
} else {
|
||||
# Just list the names
|
||||
say "$module-$bin";
|
||||
}
|
||||
}
|
||||
}
|
||||
my @modules;
|
||||
# Find all module directories with something in bin
|
||||
for my $command (glob ("$root/*/bin/*")) {
|
||||
my ($bin, $path) = fileparse($command);
|
||||
my $module = (split /\//, $path)[-2];
|
||||
if ($optref->{'describe'}) {
|
||||
# Display name and description
|
||||
say YELLOW, "$module-$bin", RESET;
|
||||
pod2usage( -verbose => 99,
|
||||
-sections => [ qw(DESCRIPTION) ],
|
||||
-input => $command,
|
||||
-exitval => "NOEXIT");
|
||||
}
|
||||
else {
|
||||
if (wantarray) {
|
||||
push(@modules, "$module-$bin");
|
||||
}
|
||||
else {
|
||||
# Just list the names
|
||||
say "$module-$bin";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wantarray) {
|
||||
return (@commands, @modules);
|
||||
}
|
||||
if (wantarray) {
|
||||
return (@commands, @modules);
|
||||
}
|
||||
}
|
||||
|
||||
# Display either a short usage message (--help) or a full manual (--man)
|
||||
sub man_command {
|
||||
my ($optref, $subcmd) = @_;
|
||||
sub man_command
|
||||
{
|
||||
my ($optref, $subcmd) = @_;
|
||||
|
||||
my $root = root($optref->{'config'});
|
||||
my $command_path = get_command_path($root, $subcmd, $optref);
|
||||
my $root = root($optref->{'config'});
|
||||
my $command_path = get_command_path($root, $subcmd, $optref);
|
||||
|
||||
$ENV{'PAGER'} ||= "more";
|
||||
open(my $PAGER, "|-", "$ENV{'PAGER'}");
|
||||
if ($optref->{'help'}) {
|
||||
pod2usage( -input => $command_path );
|
||||
} else {
|
||||
pod2usage( -verbose => 99,
|
||||
-input => $command_path,
|
||||
-output => $PAGER);
|
||||
}
|
||||
$ENV{'PAGER'} ||= "more";
|
||||
open(my $PAGER, "|-", "$ENV{'PAGER'}");
|
||||
if ($optref->{'help'}) {
|
||||
pod2usage( -input => $command_path );
|
||||
}
|
||||
else {
|
||||
pod2usage( -verbose => 99,
|
||||
-input => $command_path,
|
||||
-output => $PAGER);
|
||||
}
|
||||
close($PAGER);
|
||||
}
|
||||
|
||||
sub root {
|
||||
my ($config) = @_;
|
||||
open(my $CONF, "<", "$config/miniserv.conf") || die RED,
|
||||
"Failed to open $config/miniserv.conf", RESET , "\n";
|
||||
my $root;
|
||||
while (<$CONF>) {
|
||||
if (/^root=(.*)/) {
|
||||
$root = $1;
|
||||
}
|
||||
}
|
||||
close($CONF);
|
||||
# Does the Webmin root exist?
|
||||
if ( $root ) {
|
||||
die "$root is not a directory. Is --config correct?\n" unless (-d $root);
|
||||
} else {
|
||||
die "Unable to determine Webmin installation directory from $ENV{'WEBMIN_CONFIG'}\n";
|
||||
}
|
||||
sub root
|
||||
{
|
||||
my ($config) = @_;
|
||||
open(my $CONF, "<", "$config/miniserv.conf") ||
|
||||
die RED, "Failed to open $config/miniserv.conf", RESET , "\n";
|
||||
my $root;
|
||||
while (<$CONF>) {
|
||||
if (/^root=(.*)/) {
|
||||
$root = $1;
|
||||
}
|
||||
}
|
||||
close($CONF);
|
||||
|
||||
return $root;
|
||||
# Does the Webmin root exist?
|
||||
if ( $root ) {
|
||||
die "$root is not a directory. Is --config correct?\n" unless (-d $root);
|
||||
}
|
||||
else {
|
||||
die "Unable to determine Webmin installation directory ".
|
||||
"from $ENV{'WEBMIN_CONFIG'}\n";
|
||||
}
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
# loadlibs - Load libraries from the Webmin vendor dir
|
||||
# as those may not be installed as dependency, because
|
||||
# Webmin already provides them from package manager
|
||||
# perspective.
|
||||
sub loadlibs {
|
||||
my ($optref) = @_;
|
||||
$optref->{'config'} ||= "/etc/webmin";
|
||||
my $root = root($optref->{'config'});
|
||||
my $libroot = "$root/vendor_perl";
|
||||
eval "use lib '$libroot'";
|
||||
eval "use File::Basename";
|
||||
eval "use File::Spec";
|
||||
sub loadlibs
|
||||
{
|
||||
my ($optref) = @_;
|
||||
$optref->{'config'} ||= "/etc/webmin";
|
||||
my $root = root($optref->{'config'});
|
||||
my $libroot = "$root/vendor_perl";
|
||||
eval "use lib '$libroot'";
|
||||
eval "use File::Basename";
|
||||
eval "use File::Spec";
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -462,7 +489,7 @@ Returns Webmin and other modules and themes versions installed (only those for w
|
||||
|
||||
=head1 LICENSE AND COPYRIGHT
|
||||
|
||||
Copyright 2018 Jamie Cameron <jcameron@webmin.com>
|
||||
Joe Cooper <joe@virtualmin.com>
|
||||
Ilia Ross <ilia@virtualmin.com>
|
||||
Copyright 2018 Jamie Cameron <jamie@webmin.com>
|
||||
Joe Cooper <joe@virtualmin.com>
|
||||
Ilia Ross <ilia@virtualmin.com>
|
||||
|
||||
|
||||
@@ -70,12 +70,18 @@ sub get_rand_flag
|
||||
if ($gconfig{'os_type'} =~ /-linux$/ &&
|
||||
$config{'force_random'} eq '0' &&
|
||||
-r "/dev/urandom" &&
|
||||
&compare_version_numbers($bind_version, 9) >= 0 &&
|
||||
&compare_version_numbers($bind_version, '9.14.2') < 0) {
|
||||
# Version: 9.14.2 deprecated the use of -r option
|
||||
# in favor of using /dev/random [bugs:#5370]
|
||||
return "-r /dev/urandom";
|
||||
&compare_version_numbers($bind_version, 9) >= 0) {
|
||||
if (&compare_version_numbers($bind_version, '9.14.2') < 0) {
|
||||
return "-r /dev/urandom";
|
||||
}
|
||||
else {
|
||||
# Version: 9.14.2 deprecated the use of -r option
|
||||
# in favor of using /dev/random [bugs:#5370]. So no
|
||||
# entropy generation is needed.
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
# No random flag, and entropy is needed
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1118,7 +1124,7 @@ else {
|
||||
@v = ( );
|
||||
}
|
||||
if ($type eq "A" || $type eq "AAAA") {
|
||||
print &ui_table_row($text{'value_A1'},
|
||||
print &ui_table_row($text{"value_${type}1"},
|
||||
&ui_textbox("value0", $v[0], 20)." ".
|
||||
(!defined($_[5]) && $type eq "A" ?
|
||||
&free_address_button("value0") : ""), 3);
|
||||
@@ -3458,7 +3464,8 @@ closedir(ZONEDIR);
|
||||
|
||||
# Fork a background job to do lots of IO, to generate entropy
|
||||
my $pid;
|
||||
if (!&get_rand_flag()) {
|
||||
my $flag = &get_rand_flag();
|
||||
if (defined($flag) && !$flag) {
|
||||
$pid = fork();
|
||||
if (!$pid) {
|
||||
exec("find / -type f >/dev/null 2>&1");
|
||||
@@ -3507,7 +3514,7 @@ else {
|
||||
"cd ".quotemeta($fn)." && ".
|
||||
"$config{'keygen'} -a ".quotemeta($alg).
|
||||
" -b ".quotemeta($zonesize).
|
||||
" -n ZONE ".&get_rand_flag()." $dom 2>&1");
|
||||
" -n ZONE ".($flag || "")." ".quotemeta($dom)." 2>&1");
|
||||
if ($?) {
|
||||
kill('KILL', $pid) if ($pid);
|
||||
return $out;
|
||||
@@ -3519,7 +3526,8 @@ else {
|
||||
"cd ".quotemeta($fn)." && ".
|
||||
"$config{'keygen'} -a ".quotemeta($alg).
|
||||
" -b ".quotemeta($size).
|
||||
" -n ZONE -f KSK ".&get_rand_flag()." $dom 2>&1");
|
||||
" -n ZONE -f KSK ".($flag || "")." ".
|
||||
quotemeta($dom)." 2>&1");
|
||||
kill('KILL', $pid) if ($pid);
|
||||
if ($?) {
|
||||
return $out;
|
||||
@@ -3589,7 +3597,8 @@ $zonekey || return "Could not find DNSSEC zone key";
|
||||
|
||||
# Fork a background job to do lots of IO, to generate entropy
|
||||
my $pid;
|
||||
if (!&get_rand_flag()) {
|
||||
my $flag = &get_rand_flag();
|
||||
if (defined($flag) && !$flag) {
|
||||
$pid = fork();
|
||||
if (!$pid) {
|
||||
exec("find / -type f >/dev/null 2>&1");
|
||||
@@ -3606,7 +3615,7 @@ my $alg = $zonekey->{'algorithm'};
|
||||
my $out = &backquote_logged(
|
||||
"cd ".quotemeta($dir)." && ".
|
||||
"$config{'keygen'} -a ".quotemeta($alg)." -b ".quotemeta($zonesize).
|
||||
" -n ZONE ".&get_rand_flag()." $dom 2>&1");
|
||||
" -n ZONE ".($flag || "")." ".quotemeta($dom)." 2>&1");
|
||||
kill('KILL', $pid) if ($pid);
|
||||
if ($?) {
|
||||
return "Failed to generate new zone key : $out";
|
||||
|
||||
@@ -44,7 +44,9 @@ if (&is_raw_format_records($file)) {
|
||||
my $temp = &transname();
|
||||
©_source_dest($file, $temp);
|
||||
my $out = &backquote_logged("named-compilezone -f raw -F text ".
|
||||
"-o $file $zone->{'name'} $temp 2>&1");
|
||||
"-o ".quotemeta($file)." ".
|
||||
quotemeta($zone->{'name'})." ".
|
||||
quotemeta($temp)." 2>&1");
|
||||
&error(&text('convert_ecompile', "<tt>".&html_escape($out)."</tt>"))
|
||||
if ($?);
|
||||
&unlink_file($temp);
|
||||
|
||||
@@ -127,7 +127,7 @@ if (&have_dnssec_tools_support()) {
|
||||
print "<br>\n<br>\n";
|
||||
|
||||
if ((($lsdnssec=dt_cmdpath('lsdnssec')) ne '')) {
|
||||
my $cmd = "$lsdnssec -z $dom $rrfile";
|
||||
my $cmd = "$lsdnssec -z ".quotemeta($dom)." $rrfile";
|
||||
my $out = &backquote_command("$cmd");
|
||||
print &ui_textarea("lsdnssec", $out, 12, 80, "soft", 0,
|
||||
"readonly style='width:90%'");
|
||||
|
||||
@@ -35,10 +35,13 @@ my $rootfile = $_[4] ? $file : &make_chroot($file);
|
||||
my $FILE;
|
||||
if (&is_raw_format_records($rootfile)) {
|
||||
# Convert from raw format first
|
||||
&has_command("named-compilezone") ||
|
||||
my $compilezone = &has_command("named-compilezone");
|
||||
$compilezone ||
|
||||
&error("Zone file $rootfile is in raw format, but the ".
|
||||
"named-compilezone command is not installed");
|
||||
open($FILE, "named-compilezone -f raw -F text -o - $origin $rootfile |");
|
||||
open($FILE, "-|", $compilezone,
|
||||
"-f", "raw", "-F", "text", "-o", "-",
|
||||
$origin, $rootfile);
|
||||
}
|
||||
else {
|
||||
# Can read text format records directly
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Add or update a server or group from the webmin servers module
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
@@ -94,12 +95,17 @@ foreach my $s (@add) {
|
||||
next;
|
||||
}
|
||||
if (!$in{'name_def'} && &check_ipaddress($in{'name'})) {
|
||||
print &text('add_eipaddr', $s->{'host'}),"<p>\n";
|
||||
print &text('add_eipaddr', $s->{'host'}),"<p>\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my @rzones = grep { $_->{'type'} ne 'view' }
|
||||
&remote_foreign_call($s, "bind8", "list_zone_names");
|
||||
my @zn = &remote_foreign_call($s, "bind8", "list_zone_names");
|
||||
if ($add_error_msg) {
|
||||
print "$add_error_msg<p>\n";
|
||||
next;
|
||||
}
|
||||
my @rzones = grep { $_->{'type'} ne 'view' } @zn;
|
||||
|
||||
print &text('add_ok', $s->{'host'}, scalar(@rzones)),"<p>\n";
|
||||
$s->{'sec'} = $in{'sec'};
|
||||
$s->{'nsname'} = $in{'name_def'} ? undef : $in{'name'};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
53
bsdfdisk/change_slice_label.cgi
Normal file
53
bsdfdisk/change_slice_label.cgi
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Change the label of a slice (GPT label or glabel)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
|
||||
# Validate input parameters to prevent command injection
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or &error("Invalid device name");
|
||||
$in{'device'} !~ /\.\./ or &error("Invalid device name");
|
||||
$in{'slice'} =~ /^\d+$/ or &error("Invalid slice number");
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
|
||||
my $base_device = $disk->{'device'};
|
||||
$base_device =~ s{^/dev/}{};
|
||||
my $disk_structure = get_disk_structure($base_device);
|
||||
my $current_label = get_device_label_name(
|
||||
disk => $disk,
|
||||
slice => $slice,
|
||||
disk_structure => $disk_structure
|
||||
);
|
||||
my $suggested_label = $slice->{'device'};
|
||||
$suggested_label =~ s{^/dev/}{};
|
||||
|
||||
&ui_print_header( $slice->{'desc'}, $text{'slice_label_title'}, "" );
|
||||
|
||||
print &ui_form_start( "save_slice_label.cgi", "post" );
|
||||
print &ui_hidden( "device", $in{'device'} );
|
||||
print &ui_hidden( "slice", $in{'slice'} );
|
||||
print &ui_table_start( $text{'slice_label_header'}, undef, 2 );
|
||||
print &ui_table_row( $text{'part_device'},
|
||||
"<tt>" . html_escape( $slice->{'device'} ) . "</tt>" );
|
||||
print &ui_table_row( $text{'slice_label_current'},
|
||||
$current_label ? "<tt>" . html_escape($current_label) . "</tt>" : "-" );
|
||||
print &ui_table_row( $text{'slice_label_new'},
|
||||
&ui_textbox( "label", $suggested_label, 20 ) );
|
||||
print &ui_table_end();
|
||||
print &ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&ui_print_footer( "edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
@@ -6,47 +6,63 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
&error_setup($text{'npart_err'});
|
||||
&error_setup( $text{'npart_err'} );
|
||||
|
||||
# Get the disk
|
||||
my @disks = &list_disks_partitions();
|
||||
|
||||
# Validate input parameters
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'slice'} =~ /^\d+$/ or &error( $text{'slice_egone'} );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
|
||||
# Validate inputs, starting with slice number
|
||||
my $part = { };
|
||||
$in{'letter'} =~ /^[a-d]$/i || &error($text{'npart_eletter'});
|
||||
$in{'letter'} = lc($in{'letter'});
|
||||
my ($clash) = grep { $_->{'letter'} eq $in{'letter'} } @{$slice->{'parts'}};
|
||||
$clash && &error(&text('npart_eclash', $in{'letter'}));
|
||||
my $part = {};
|
||||
$in{'letter'} =~ /^[a-h]$/i || &error( $text{'npart_eletter'} );
|
||||
$in{'letter'} = lc( $in{'letter'} );
|
||||
|
||||
# Partition 'c' is reserved in BSD disklabels (represents the whole slice)
|
||||
$in{'letter'} ne 'c' || &error( $text{'npart_ereserved'} );
|
||||
my ($clash) = grep { $_->{'letter'} eq $in{'letter'} } @{ $slice->{'parts'} };
|
||||
$clash && &error( &text( 'npart_eclash', $in{'letter'} ) );
|
||||
$part->{'letter'} = $in{'letter'};
|
||||
|
||||
# Start and end blocks
|
||||
$in{'start'} =~ /^\d+$/ || &error($text{'nslice_estart'});
|
||||
$in{'end'} =~ /^\d+$/ || &error($text{'nslice_eend'});
|
||||
$in{'start'} < $in{'end'} || &error($text{'npart_erange'});
|
||||
$in{'start'} =~ /^\d+$/ || &error( $text{'nslice_estart'} );
|
||||
$in{'end'} =~ /^\d+$/ || &error( $text{'nslice_eend'} );
|
||||
$in{'start'} < $in{'end'} || &error( $text{'npart_erange'} );
|
||||
$part->{'startblock'} = $in{'start'};
|
||||
$part->{'blocks'} = $in{'end'} - $in{'start'};
|
||||
$part->{'blocks'} = $in{'end'} - $in{'start'} + 1;
|
||||
|
||||
# Slice type
|
||||
$in{'type'} =~ /^[a-zA-Z0-9._-]+$/
|
||||
or &error( $text{'npart_etype'} || 'Invalid partition type' );
|
||||
$part->{'type'} = $in{'type'};
|
||||
|
||||
# Do the creation
|
||||
&ui_print_header($slice->{'desc'}, $text{'npart_title'}, "");
|
||||
&ui_print_header( $slice->{'desc'}, $text{'npart_title'}, "" );
|
||||
|
||||
print &text('npart_creating', $in{'letter'}, $slice->{'desc'}),"<p>\n";
|
||||
my $err = &save_partition($disk, $slice, $part);
|
||||
print &text( 'npart_creating', $in{'letter'},
|
||||
&html_escape( $slice->{'desc'} ) ), "<p>\n";
|
||||
|
||||
# Actually create the partition inside the slice (initialize BSD label if needed)
|
||||
my $err = &create_partition( $disk, $slice, $part );
|
||||
if ($err) {
|
||||
print &text('npart_failed', $err),"<p>\n";
|
||||
}
|
||||
print &text( 'npart_failed', &html_escape($err) ), "<p>\n";
|
||||
}
|
||||
else {
|
||||
print &text('npart_done'),"<p>\n";
|
||||
&webmin_log("create", "part", $part->{'device'}, $part);
|
||||
}
|
||||
print &text('npart_done'), "<p>\n";
|
||||
&webmin_log( "create", "part", $part->{'device'}, $part );
|
||||
}
|
||||
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&ui_print_footer( "edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
|
||||
@@ -1,65 +1,147 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Actually create a new slice
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
&error_setup($text{'nslice_err'});
|
||||
our ( %in, %text, $module_name );
|
||||
ReadParse();
|
||||
error_setup( $text{'nslice_err'} );
|
||||
|
||||
# Get the disk
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
# Get the disk using first() for an early exit on match
|
||||
my @disks = list_disks_partitions();
|
||||
|
||||
# Validate input parameters
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
my $disk;
|
||||
foreach my $d (@disks) {
|
||||
if ( $d->{'device'} eq $in{'device'} ) {
|
||||
$disk = $d;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# Validate device parameter to prevent path traversal and command injection
|
||||
$disk or error( $text{'disk_egone'} );
|
||||
|
||||
# Prefer GPART total blocks for bounds
|
||||
( my $base_dev = $in{'device'} ) =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base_dev);
|
||||
my $disk_blocks =
|
||||
( $ds && $ds->{'total_blocks'} )
|
||||
? $ds->{'total_blocks'}
|
||||
: ( $disk->{'blocks'} || 0 );
|
||||
|
||||
# Validate inputs, starting with slice number
|
||||
my $slice = { };
|
||||
$in{'number'} =~ /^\d+$/ || &error($text{'nslice_enumber'});
|
||||
my ($clash) = grep { $_->{'number'} == $in{'number'} } @{$disk->{'slices'}};
|
||||
$clash && &error(&text('nslice_eclash', $in{'number'}));
|
||||
my $slice = {};
|
||||
$in{'number'} =~ /^\d+$/ or error( $text{'nslice_enumber'} );
|
||||
|
||||
# Check for clash using first() with a loop exiting on first match
|
||||
my $clash;
|
||||
foreach my $s ( @{ $disk->{'slices'} } ) {
|
||||
if ( $s->{'number'} == $in{'number'} ) {
|
||||
$clash = $s;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$slice->{'number'} = $in{'number'};
|
||||
|
||||
# Start and end blocks
|
||||
$in{'start'} =~ /^\d+$/ || &error($text{'nslice_estart'});
|
||||
$in{'end'} =~ /^\d+$/ || &error($text{'nslice_eend'});
|
||||
$in{'start'} < $in{'end'} || &error($text{'nslice_erange'});
|
||||
$in{'start'} =~ /^\d+$/ or error( $text{'nslice_estart'} );
|
||||
$in{'end'} =~ /^\d+$/ or error( $text{'nslice_eend'} );
|
||||
( $in{'start'} < $in{'end'} ) or error( $text{'nslice_erange'} );
|
||||
|
||||
# total_blocks is the block *after* the last valid block, so end must be < total_blocks
|
||||
( $in{'end'} < $disk_blocks )
|
||||
or error( text( 'nslice_emax', $disk_blocks - 1 ) );
|
||||
|
||||
# Ensure the new slice does not overlap existing slices
|
||||
foreach my $s ( @{ $disk->{'slices'} } ) {
|
||||
my $s_start = $s->{'startblock'};
|
||||
my $s_end = $s->{'startblock'} + $s->{'blocks'} - 1;
|
||||
if ( !( $in{'end'} < $s_start || $in{'start'} > $s_end ) ) {
|
||||
error( "Requested slice range overlaps with existing slice #"
|
||||
. $s->{'number'} );
|
||||
}
|
||||
}
|
||||
|
||||
$slice->{'startblock'} = $in{'start'};
|
||||
$slice->{'blocks'} = $in{'end'} - $in{'start'};
|
||||
$slice->{'blocks'} = $in{'end'} - $in{'start'} + 1;
|
||||
|
||||
# Slice type
|
||||
$in{'type'} =~ /^[a-zA-Z0-9_-]+$/ or error( $text{'nslice_etype'} );
|
||||
length( $in{'type'} ) <= 20 or error( $text{'nslice_etype'} );
|
||||
$slice->{'type'} = $in{'type'};
|
||||
|
||||
# Do the creation
|
||||
&ui_print_header($disk->{'desc'}, $text{'nslice_title'}, "");
|
||||
|
||||
print &text('nslice_creating', $in{'number'}, $disk->{'desc'}),"<p>\n";
|
||||
my $err = &create_slice($disk, $slice);
|
||||
ui_print_header( $disk->{'desc'}, $text{'nslice_title'}, "" );
|
||||
print text( 'nslice_creating', $in{'number'}, &html_escape( $disk->{'desc'} ) ),
|
||||
"<p>\n";
|
||||
my $err = create_slice( $disk, $slice );
|
||||
if ($err) {
|
||||
print &text('nslice_failed', $err),"<p>\n";
|
||||
}
|
||||
print text( 'nslice_failed', &html_escape($err) ), "<p>\n";
|
||||
}
|
||||
else {
|
||||
print &text('nslice_done'),"<p>\n";
|
||||
}
|
||||
print text('nslice_done'), "<p>\n";
|
||||
|
||||
if (!$err && $in{'makepart'}) {
|
||||
# Also create a partition
|
||||
print &text('nslice_parting', $in{'number'}, $disk->{'desc'}),"<p>\n";
|
||||
my $err = &initialize_slice($disk, $slice);
|
||||
if ($err) {
|
||||
print &text('nslice_pfailed', $err),"<p>\n";
|
||||
}
|
||||
else {
|
||||
print &text('nslice_pdone'),"<p>\n";
|
||||
}
|
||||
}
|
||||
# Auto-label the new partition provider with its name if scheme is GPT or BSD
|
||||
my $base = $disk->{'device'};
|
||||
$base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
if ( $ds && $ds->{'scheme'} ) {
|
||||
|
||||
if (!$err) {
|
||||
&webmin_log("create", "slice", $slice->{'device'}, $slice);
|
||||
}
|
||||
# Determine provider and label text
|
||||
my $label_text = slice_name($slice); # e.g., da8s2 or da0p2
|
||||
if ( $ds->{'scheme'} =~ /GPT/i ) {
|
||||
my $idx = $slice->{'number'};
|
||||
if ($idx) {
|
||||
my $cmd2 =
|
||||
"gpart modify -i $idx -l "
|
||||
. quote_path($label_text) . " "
|
||||
. quote_path($base);
|
||||
my $out2 = backquote_command("$cmd2 2>&1");
|
||||
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}",
|
||||
$text{'disk_return'});
|
||||
# If it fails, ignore silently
|
||||
}
|
||||
}
|
||||
else {
|
||||
# On MBR, if BSD label exists we can set label once created; ignore for now
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !$err && $in{'makepart'} ) {
|
||||
|
||||
# Also create a partition (initialize slice label)
|
||||
my $part_err = initialize_slice( $disk, $slice );
|
||||
if ($part_err) {
|
||||
print text( 'nslice_pfailed', &html_escape($part_err) ), "<p>\n";
|
||||
}
|
||||
else {
|
||||
print text('nslice_pdone'), "<p>\n";
|
||||
}
|
||||
}
|
||||
if ( !$err ) {
|
||||
|
||||
# Auto-label GPT partitions with their device name (e.g., da8p2)
|
||||
my $base = $disk->{'device'};
|
||||
$base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
if ( $ds && $ds->{'scheme'} && $ds->{'scheme'} =~ /GPT/i ) {
|
||||
my $slice_devname = $slice->{'device'};
|
||||
$slice_devname =~ s{^/dev/}{}; # e.g., da8p2
|
||||
my $idx = $slice->{'number'};
|
||||
if ( $idx && $slice_devname ) {
|
||||
my $label_cmd =
|
||||
"gpart modify -i $idx -l "
|
||||
. quote_path($slice_devname) . " "
|
||||
. quote_path($base) . " 2>&1";
|
||||
my $label_out = backquote_command($label_cmd);
|
||||
|
||||
# Ignore errors - labeling is optional
|
||||
}
|
||||
}
|
||||
webmin_log( "create", "slice", $slice->{'device'}, $slice );
|
||||
}
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
ui_print_footer( "edit_disk.cgi?device=$url_device", $text{'disk_return'} );
|
||||
|
||||
@@ -6,47 +6,62 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
# Validate inputs
|
||||
($in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ && $in{'device'} !~ /\.\./)
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'slice'} =~ /^\d+$/ or &error( $text{'slice_egone'} );
|
||||
$in{'part'} =~ /^[a-z]$/ or &error( $text{'part_egone'} );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} } @{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my $in_slice_num = int($in{'slice'});
|
||||
my ($slice) = grep { int($_->{'number'}) == $in_slice_num } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} } @{ $slice->{'parts'} };
|
||||
$part || &error( $text{'part_egone'} );
|
||||
|
||||
&ui_print_header($part->{'desc'}, $text{'dpart_title'}, "");
|
||||
&ui_print_header( $part->{'desc'}, $text{'dpart_title'}, "" );
|
||||
|
||||
if ($in{'confirm'}) {
|
||||
# Delete it
|
||||
print &text('dpart_deleting', $part->{'desc'}),"<p>\n";
|
||||
my $err = &delete_partition($disk, $slice, $part);
|
||||
if ($err) {
|
||||
print &text('dpart_failed', $err),"<p>\n";
|
||||
}
|
||||
else {
|
||||
print $text{'dpart_done'},"<p>\n";
|
||||
&webmin_log("delete", "part", $part->{'device'}, $part);
|
||||
}
|
||||
}
|
||||
if ( $in{'confirm'} ) {
|
||||
|
||||
# Delete it
|
||||
print &text( 'dpart_deleting', &html_escape( $part->{'desc'} ) ), "<p>\n";
|
||||
my $err = &delete_partition( $disk, $slice, $part );
|
||||
if ($err) {
|
||||
print &text( 'dpart_failed', &html_escape($err) ), "<p>\n";
|
||||
}
|
||||
else {
|
||||
print $text{'dpart_done'}, "<p>\n";
|
||||
&webmin_log( "delete", "part", $part->{'device'}, $part );
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Ask first
|
||||
my @st = &fdisk::device_status($part->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
print &ui_confirmation_form(
|
||||
"delete_part.cgi",
|
||||
&text('dpart_rusure', "<tt>$part->{'device'}</tt>"),
|
||||
[ [ "device", $in{'device'} ],
|
||||
[ "slice", $in{'slice'} ],
|
||||
[ "part", $in{'part'} ] ],
|
||||
[ [ "confirm", $text{'dslice_confirm'} ] ],
|
||||
undef,
|
||||
$use ? &text('dpart_warn', $use) : undef);
|
||||
}
|
||||
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
# Ask first
|
||||
my @st = &fdisk::device_status( $part->{'device'} );
|
||||
my $use = &fdisk::device_status_link(@st); # returns safe HTML link(s); ensure upstream sanitization
|
||||
print &ui_confirmation_form(
|
||||
"delete_part.cgi",
|
||||
&text(
|
||||
'dpart_rusure',
|
||||
"<tt>" . &html_escape( $part->{'device'} ) . "</tt>"
|
||||
),
|
||||
[
|
||||
[ "device", $in{'device'} ],
|
||||
[ "slice", $in{'slice'} ],
|
||||
[ "part", $in{'part'} ]
|
||||
],
|
||||
# Use partition-specific confirmation text key if available
|
||||
[ [ "confirm", $text{'dpart_confirm'} || $text{'dslice_confirm'} ] ],
|
||||
undef,
|
||||
$use ? &text( 'dpart_warn', $use ) : undef
|
||||
);
|
||||
}
|
||||
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&ui_print_footer( "edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
|
||||
@@ -6,53 +6,62 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'slice'} =~ /^\d+$/ or &error( $text{'slice_egone'} );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
|
||||
&ui_print_header($slice->{'desc'}, $text{'dslice_title'}, "");
|
||||
&ui_print_header( $slice->{'desc'}, $text{'dslice_title'}, "" );
|
||||
|
||||
if ($in{'confirm'}) {
|
||||
# Delete it
|
||||
print &text('dslice_deleting', $slice->{'desc'}),"<p>\n";
|
||||
my $err = &delete_slice($disk, $slice);
|
||||
if ($err) {
|
||||
print &text('dslice_failed', $err),"<p>\n";
|
||||
}
|
||||
else {
|
||||
print $text{'dslice_done'},"<p>\n";
|
||||
&webmin_log("delete", "slice", $slice->{'device'}, $slice);
|
||||
}
|
||||
}
|
||||
if ( $in{'confirm'} ) {
|
||||
|
||||
# Delete it
|
||||
print &text( 'dslice_deleting', &html_escape( $slice->{'desc'} ) ), "<p>\n";
|
||||
my $err = &delete_slice( $disk, $slice );
|
||||
if ($err) {
|
||||
print &text( 'dslice_failed', &html_escape($err) ), "<p>\n";
|
||||
}
|
||||
else {
|
||||
print $text{'dslice_done'}, "<p>\n";
|
||||
&webmin_log( "delete", "slice", $slice->{'device'}, $slice );
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Ask first
|
||||
my @warn;
|
||||
my @st = &fdisk::device_status($slice->{'device'});
|
||||
if (@st) {
|
||||
push(@warn, &fdisk::device_status_link(@st));
|
||||
}
|
||||
foreach my $p (@{$slice->{'parts'}}) {
|
||||
my @st = &fdisk::device_status($p->{'device'});
|
||||
if (@st) {
|
||||
push(@warn, &fdisk::device_status_link(@st));
|
||||
}
|
||||
}
|
||||
print &ui_confirmation_form(
|
||||
"delete_slice.cgi",
|
||||
&text('dslice_rusure', "<tt>$slice->{'device'}</tt>"),
|
||||
[ [ "device", $in{'device'} ],
|
||||
[ "slice", $in{'slice'} ] ],
|
||||
[ [ "confirm", $text{'dslice_confirm'} ] ],
|
||||
undef,
|
||||
@warn ? &text('dslice_warn', join(" ", @warn)) : undef);
|
||||
}
|
||||
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}",
|
||||
$text{'disk_return'});
|
||||
# Ask first
|
||||
my @warn;
|
||||
my @st = &fdisk::device_status( $slice->{'device'} );
|
||||
if (@st) {
|
||||
push( @warn, &fdisk::device_status_link(@st) );
|
||||
}
|
||||
foreach my $p ( @{ $slice->{'parts'} } ) {
|
||||
my @st = &fdisk::device_status( $p->{'device'} );
|
||||
if (@st) {
|
||||
push( @warn, &fdisk::device_status_link(@st) );
|
||||
}
|
||||
}
|
||||
print &ui_confirmation_form(
|
||||
"delete_slice.cgi",
|
||||
&text(
|
||||
'dslice_rusure',
|
||||
"<tt>" . &html_escape( $slice->{'device'} ) . "</tt>"
|
||||
),
|
||||
[ [ "device", $in{'device'} ], [ "slice", $in{'slice'} ] ],
|
||||
[ [ "confirm", $text{'dslice_confirm'} ] ],
|
||||
undef,
|
||||
@warn
|
||||
? &text( 'dslice_warn', join( " ", @warn ) )
|
||||
: undef
|
||||
);
|
||||
}
|
||||
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
&ui_print_footer( "edit_disk.cgi?device=$url_device", $text{'disk_return'} );
|
||||
|
||||
@@ -1,100 +1,462 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show details of a disk, and slices on it
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
my $extwidth = 300;
|
||||
my $extwidth = 100;
|
||||
|
||||
# Get the disk
|
||||
my @disks = &list_disks_partitions();
|
||||
|
||||
# Validate input parameters
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or &error( $text{'disk_edevice'} );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
|
||||
&ui_print_header($disk->{'desc'}, $text{'disk_title'}, "");
|
||||
# Cache commonly used values
|
||||
my $device = $disk->{'device'};
|
||||
my $device_url = &urlize($device);
|
||||
my $desc = $disk->{'desc'};
|
||||
|
||||
# Show disk details
|
||||
my @info = ( );
|
||||
push(@info, &text('disk_dsize', &nice_size($disk->{'size'})));
|
||||
if ($disk->{'model'}) {
|
||||
push(@info, &text('disk_model', $disk->{'model'}));
|
||||
# Prefer total blocks from gpart header when available
|
||||
my $base_device = $disk->{'device'};
|
||||
$base_device =~ s{^/dev/}{};
|
||||
my $disk_structure = &get_disk_structure($base_device);
|
||||
my $disk_blocks =
|
||||
( $disk_structure && $disk_structure->{'total_blocks'} )
|
||||
? $disk_structure->{'total_blocks'}
|
||||
: ( $disk->{'blocks'} || 1000000 );
|
||||
|
||||
# Precompute a scale factor for extent image widths
|
||||
my $scale = $extwidth / ( $disk_blocks || 1 );
|
||||
&ui_print_header( $disk->{'desc'}, $text{'disk_title'}, "" );
|
||||
|
||||
# Debug toggle bar
|
||||
print
|
||||
"<div class='debug-toggle' style='margin-bottom: 15px; text-align: right;'>";
|
||||
if ( $in{'debug'} ) {
|
||||
print
|
||||
"<a href='edit_disk.cgi?device=$device_url' class='btn btn-default'><i class='fa fa-bug'></i> $text{'disk_hide_debug'}</a>";
|
||||
}
|
||||
else {
|
||||
print
|
||||
"<a href='edit_disk.cgi?device=$device_url&debug=1' class='btn btn-default'><i class='fa fa-bug'></i> $text{'disk_show_debug'}</a>";
|
||||
}
|
||||
print "</div>";
|
||||
|
||||
# Get detailed disk information from geom and disk structure from gpart show (cache disk_structure entries)
|
||||
my $geom_info = &get_detailed_disk_info($device);
|
||||
my $entries = $disk_structure
|
||||
&& $disk_structure->{'entries'} ? $disk_structure->{'entries'} : [];
|
||||
print &ui_table_start( $text{'disk_details'}, "width=100%", 2 );
|
||||
|
||||
# Prefer mediasize (bytes) for accurate size; fallback to stat-based size
|
||||
my $disk_bytes =
|
||||
( $disk_structure && $disk_structure->{'mediasize'} )
|
||||
? $disk_structure->{'mediasize'}
|
||||
: $disk->{'size'};
|
||||
print &ui_table_row( $text{'disk_dsize'}, &safe_nice_size($disk_bytes) );
|
||||
if ( $disk->{'model'} ) {
|
||||
print &ui_table_row( $text{'disk_model'},
|
||||
&html_escape( $disk->{'model'} ) );
|
||||
}
|
||||
print &ui_table_row( $text{'disk_device'},
|
||||
"<tt>" . &html_escape( $disk->{'device'} ) . "</tt>" );
|
||||
|
||||
# Get disk scheme
|
||||
print &ui_table_row( $text{'disk_scheme'},
|
||||
$disk_structure
|
||||
? &html_escape( $disk_structure->{'scheme'} )
|
||||
: $text{'disk_unknown'} );
|
||||
|
||||
# GEOM details
|
||||
if ($geom_info) {
|
||||
print &ui_table_hr();
|
||||
print &ui_table_row( $text{'disk_geom_header'},
|
||||
"<b>$text{'disk_geom_details'}</b>", 2 );
|
||||
if ( $geom_info->{'mediasize'} ) {
|
||||
print &ui_table_row( $text{'disk_mediasize'},
|
||||
$geom_info->{'mediasize'} );
|
||||
}
|
||||
if ( $geom_info->{'sectorsize'} ) {
|
||||
print &ui_table_row( $text{'disk_sectorsize'},
|
||||
$geom_info->{'sectorsize'} . " " . $text{'disk_bytes'} );
|
||||
}
|
||||
if ( $geom_info->{'stripesize'} ) {
|
||||
print &ui_table_row( $text{'disk_stripesize'},
|
||||
$geom_info->{'stripesize'} . " " . $text{'disk_bytes'} );
|
||||
}
|
||||
if ( $geom_info->{'stripeoffset'} ) {
|
||||
print &ui_table_row( $text{'disk_stripeoffset'},
|
||||
$geom_info->{'stripeoffset'} . " " . $text{'disk_bytes'} );
|
||||
}
|
||||
if ( $geom_info->{'mode'} ) {
|
||||
print &ui_table_row( $text{'disk_mode'},
|
||||
&html_escape( $geom_info->{'mode'} ) );
|
||||
}
|
||||
if ( $geom_info->{'rotationrate'} ) {
|
||||
if ( $geom_info->{'rotationrate'} eq "0" ) {
|
||||
print &ui_table_row( $text{'disk_rotationrate'},
|
||||
$text{'disk_ssd'} );
|
||||
}
|
||||
push(@info, &text('disk_cylinders', $disk->{'cylinders'}));
|
||||
push(@info, &text('disk_blocks', $disk->{'blocks'}));
|
||||
push(@info, &text('disk_device', "<tt>$disk->{'device'}</tt>"));
|
||||
print &ui_links_row(\@info),"<p>\n";
|
||||
else {
|
||||
print &ui_table_row( $text{'disk_rotationrate'},
|
||||
$geom_info->{'rotationrate'} . " " . $text{'disk_rpm'} );
|
||||
}
|
||||
}
|
||||
if ( $geom_info->{'ident'} ) {
|
||||
print &ui_table_row( $text{'disk_ident'},
|
||||
&html_escape( $geom_info->{'ident'} ) );
|
||||
}
|
||||
if ( $geom_info->{'lunid'} ) {
|
||||
print &ui_table_row( $text{'disk_lunid'},
|
||||
&html_escape( $geom_info->{'lunid'} ) );
|
||||
}
|
||||
if ( $geom_info->{'descr'} ) {
|
||||
print &ui_table_row( $text{'disk_descr'},
|
||||
&html_escape( $geom_info->{'descr'} ) );
|
||||
}
|
||||
}
|
||||
|
||||
# Advanced information (cylinders, blocks)
|
||||
print &ui_table_hr();
|
||||
print &ui_table_row( $text{'disk_advanced_header'},
|
||||
"<b>$text{'disk_advanced_details'}</b>", 2 );
|
||||
if ( $disk->{'cylinders'} ) {
|
||||
print &ui_table_row( $text{'disk_cylinders'}, $disk->{'cylinders'} );
|
||||
}
|
||||
print &ui_table_row( $text{'disk_blocks'}, $disk->{'blocks'} );
|
||||
print &ui_table_end();
|
||||
|
||||
# Debug: print raw outputs if debug mode is enabled
|
||||
if ( $in{'debug'} ) {
|
||||
print "<div class='debug-section'>";
|
||||
|
||||
# Debug: gpart show output
|
||||
my $cmd = "gpart show -l " . "e_path($base_device) . " 2>&1";
|
||||
my $out = &backquote_command($cmd);
|
||||
print "<div class='panel panel-default'>";
|
||||
print
|
||||
"<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_gpart'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Command: "
|
||||
. &html_escape($cmd)
|
||||
. "\nOutput:\n"
|
||||
. &html_escape($out)
|
||||
. "\n</pre>";
|
||||
print "</div></div>";
|
||||
|
||||
# Debug: disk structure
|
||||
print "<div class='panel panel-default'>";
|
||||
print
|
||||
"<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_structure'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Disk Structure:\n";
|
||||
foreach my $key ( sort keys %$disk_structure ) {
|
||||
if ( $key eq 'entries' ) {
|
||||
print "entries: [\n";
|
||||
foreach my $entry ( @{ $disk_structure->{'entries'} } ) {
|
||||
print " {\n";
|
||||
foreach my $k ( sort keys %$entry ) {
|
||||
print " $k: " . &html_escape( $entry->{$k} ) . "\n";
|
||||
}
|
||||
print " },\n";
|
||||
}
|
||||
print "]\n";
|
||||
}
|
||||
else {
|
||||
print "$key: " . &html_escape( $disk_structure->{$key} ) . "\n";
|
||||
}
|
||||
}
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
|
||||
# Debug: Raw GEOM output
|
||||
print "<div class='panel panel-default'>";
|
||||
print
|
||||
"<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_geom'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Raw GEOM output:\n";
|
||||
print &html_escape(
|
||||
&backquote_command(
|
||||
"geom disk list " . "e_path($device) . " 2>/dev/null"
|
||||
)
|
||||
);
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
|
||||
# Build partition details from disk_structure (no separate gpart list call)
|
||||
my %part_details = ();
|
||||
if ( $disk_structure && $disk_structure->{'partitions'} ) {
|
||||
%part_details = %{ $disk_structure->{'partitions'} };
|
||||
}
|
||||
|
||||
# Ensure we have names/labels for any entries missing from partitions map
|
||||
if ( $disk_structure && $disk_structure->{'entries'} ) {
|
||||
foreach my $entry ( @{ $disk_structure->{'entries'} } ) {
|
||||
next unless ( $entry->{'type'} eq 'partition' && $entry->{'index'} );
|
||||
my $part_num = $entry->{'index'};
|
||||
$part_details{$part_num} ||= {};
|
||||
$part_details{$part_num}->{'name'} ||=
|
||||
$base_device
|
||||
. ( ( $disk_structure->{'scheme'} eq 'GPT' )
|
||||
? "p$part_num"
|
||||
: "s$part_num" );
|
||||
if ( $entry->{'label'} && $entry->{'label'} ne '(null)' ) {
|
||||
$part_details{$part_num}->{'label'} ||= $entry->{'label'};
|
||||
}
|
||||
$part_details{$part_num}->{'type'} ||=
|
||||
$entry->{'part_type'} || 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
# Build ZFS devices cache
|
||||
my ( $zfs_pools, $zfs_devices ) = &build_zfs_devices_cache();
|
||||
|
||||
# Debug ZFS pools if debug mode is enabled
|
||||
if ( $in{'debug'} ) {
|
||||
print "<div class='debug-section'>";
|
||||
print "<div class='panel panel-default'>";
|
||||
print
|
||||
"<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_zfs'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>";
|
||||
my $cmd = "zpool status 2>&1";
|
||||
my $out = &backquote_command($cmd);
|
||||
print "Command: "
|
||||
. &html_escape($cmd)
|
||||
. "\nOutput:\n"
|
||||
. &html_escape($out) . "\n";
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
|
||||
# Debug: Print partition details mapping if debug enabled
|
||||
if ( $in{'debug'} ) {
|
||||
print "<div class='debug-section'>";
|
||||
print "<div class='panel panel-default'>";
|
||||
print
|
||||
"<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_part_details'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Partition Details Mapping:\n";
|
||||
foreach my $pnum ( sort { $a <=> $b } keys %part_details ) {
|
||||
print " $pnum: {\n";
|
||||
foreach my $k ( sort keys %{ $part_details{$pnum} } ) {
|
||||
print " $k: "
|
||||
. &html_escape( $part_details{$pnum}->{$k} ) . "\n";
|
||||
}
|
||||
print " },\n";
|
||||
}
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
|
||||
# Get sector size
|
||||
my $sectorsize =
|
||||
$disk_structure->{'sectorsize'} || &get_disk_sectorsize($device) || 512;
|
||||
my $sectorsize_text = $sectorsize ? "$sectorsize" : "512";
|
||||
|
||||
# Show partitions table
|
||||
my @links = ( "<a href='slice_form.cgi?device=".&urlize($disk->{'device'}).
|
||||
"&new=1'>".$text{'disk_add'}."</a>" );
|
||||
if (@{$disk->{'slices'}}) {
|
||||
print &ui_links_row(\@links);
|
||||
print &ui_columns_start([
|
||||
$text{'disk_no'},
|
||||
$text{'disk_type'},
|
||||
$text{'disk_extent'},
|
||||
$text{'disk_size'},
|
||||
$text{'disk_start'},
|
||||
$text{'disk_end'},
|
||||
$text{'disk_use'},
|
||||
]);
|
||||
foreach my $p (@{$disk->{'slices'}}) {
|
||||
# Create images for the extent
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src=images/gap.gif height=10 width=%d>",
|
||||
$extwidth*($p->{'startblock'} - 1) /
|
||||
$disk->{'blocks'};
|
||||
$ext .= sprintf "<img src=images/%s.gif height=10 width=%d>",
|
||||
$p->{'extended'} ? "ext" : "use",
|
||||
$extwidth*($p->{'blocks'}) /
|
||||
$disk->{'blocks'};
|
||||
$ext .= sprintf "<img src=images/gap.gif height=10 width=%d>",
|
||||
$extwidth*($disk->{'blocks'} - $p->{'startblock'} -
|
||||
$p->{'blocks'}) / $disk->{'blocks'};
|
||||
my @links =
|
||||
( "<a href='slice_form.cgi?device=$device_url&new=1'>"
|
||||
. $text{'disk_add'}
|
||||
. "</a>" );
|
||||
if (@$entries) {
|
||||
print &ui_links_row( \@links );
|
||||
print &ui_columns_start(
|
||||
[
|
||||
$text{'disk_no'}, # Row number
|
||||
$text{'disk_partno'}, # Part. No.
|
||||
$text{'disk_partname'}, # Part. Name
|
||||
$text{'disk_partlabel'}, # Part. Label
|
||||
$text{'disk_subpart'}, # Sub-part.
|
||||
$text{'disk_extent'}, # Extent
|
||||
$text{'disk_start'}, # Startblock
|
||||
$text{'disk_end'}, # Endblock
|
||||
$text{'disk_size'}, # Size
|
||||
$text{'disk_format'}, # Format type
|
||||
$text{'disk_use'}, # Used by
|
||||
$text{'disk_role'}, # Role Type
|
||||
]
|
||||
);
|
||||
my $row_number = 1;
|
||||
foreach my $entry (@$entries) {
|
||||
my @cols = ();
|
||||
push( @cols, $row_number++ );
|
||||
if ( $entry->{'type'} eq 'free' ) {
|
||||
my $start = $entry->{'start'};
|
||||
my $end = $entry->{'start'} + $entry->{'size'} - 1;
|
||||
my $create_url =
|
||||
"slice_form.cgi?device=$device_url&new=1&start=$start&end=$end";
|
||||
push( @cols,
|
||||
"<a href='$create_url' style='color: green;'>"
|
||||
. $text{'disk_free'}
|
||||
. "</a>" );
|
||||
push( @cols, "-" );
|
||||
push( @cols, "-" );
|
||||
push( @cols, "-" );
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>",
|
||||
$scale * ( $entry->{'start'} - 1 );
|
||||
$ext .=
|
||||
sprintf
|
||||
"<img src='images/gap.gif' height='10' width='%d' style='background-color: #8f8;'>",
|
||||
$scale * ( $entry->{'size'} );
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>",
|
||||
$scale * ( $disk_blocks - $entry->{'start'} - $entry->{'size'} );
|
||||
push( @cols, $ext );
|
||||
push( @cols, $start );
|
||||
push( @cols, $end );
|
||||
push( @cols, $entry->{'size_human'} );
|
||||
push( @cols, $text{'disk_free_space'} );
|
||||
push( @cols, $text{'disk_available'} );
|
||||
push( @cols, "-" );
|
||||
}
|
||||
else {
|
||||
my $part_num = $entry->{'index'};
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>",
|
||||
$scale * ( $entry->{'start'} - 1 );
|
||||
$ext .= sprintf "<img src='images/use.gif' height='10' width='%d'>",
|
||||
$scale * ( $entry->{'size'} );
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>",
|
||||
$scale * ( $disk_blocks - $entry->{'start'} - $entry->{'size'} );
|
||||
my $url = "edit_slice.cgi?device=$device_url&slice="
|
||||
. &urlize($part_num);
|
||||
push( @cols, "<a href='$url'>" . &html_escape($part_num) . "</a>" );
|
||||
|
||||
# Work out use
|
||||
my @st = &fdisk::device_status($p->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
my $n = scalar(@{$p->{'parts'}});
|
||||
my $part_info = $part_details{$part_num};
|
||||
my $part_name = $part_info ? $part_info->{'name'} : "-";
|
||||
push( @cols, &html_escape($part_name) );
|
||||
my $part_label =
|
||||
$part_info
|
||||
? $part_info->{'label'}
|
||||
: ( $entry->{'label'} eq "(null)" ? "-" : $entry->{'label'} );
|
||||
push( @cols, &html_escape($part_label) );
|
||||
|
||||
# Add row for the slice
|
||||
my $url = "edit_slice.cgi?device=".&urlize($disk->{'device'}).
|
||||
"&slice=".$p->{'number'};
|
||||
my $nlink = "<a href='$url'>$p->{'number'}</a>";
|
||||
$nlink = "<b>$nlink</b>" if ($p->{'active'});
|
||||
print &ui_columns_row([
|
||||
$nlink,
|
||||
"<a href='$url'>".&fdisk::tag_name($p->{'type'})."</a>",
|
||||
$ext,
|
||||
&nice_size($p->{'size'}),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
$use ? $use :
|
||||
$n ? &text('disk_scount', $n) : "",
|
||||
]);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
}
|
||||
# Find sub-partitions if available
|
||||
my ($slice) =
|
||||
grep { $_->{'number'} eq $part_num } @{ $disk->{'slices'} || [] };
|
||||
my $sub_part_info =
|
||||
( $slice && scalar( @{ $slice->{'parts'} || [] } ) > 0 )
|
||||
? join( ", ", map { $_->{'letter'} } @{ $slice->{'parts'} } )
|
||||
: "-";
|
||||
push( @cols, &html_escape($sub_part_info) );
|
||||
|
||||
push( @cols, $ext );
|
||||
push( @cols, $entry->{'start'} );
|
||||
push( @cols, $entry->{'start'} + $entry->{'size'} - 1 );
|
||||
push( @cols, $entry->{'size_human'} );
|
||||
|
||||
# Classify format/use/role via library helper
|
||||
my ( $format_type, $usage, $role ) = classify_partition_row(
|
||||
base_device => $base_device,
|
||||
scheme => ( $disk_structure->{'scheme'} || '' ),
|
||||
part_num => $part_num,
|
||||
part_name => $part_name,
|
||||
part_label => $part_label,
|
||||
entry_part_type => (
|
||||
$part_info ? $part_info->{'type'} : $entry->{'part_type'}
|
||||
),
|
||||
entry_rawtype =>
|
||||
( $part_info ? $part_info->{'rawtype'} : undef ),
|
||||
size_human => $entry->{'size_human'},
|
||||
size_blocks => $entry->{'size'},
|
||||
zfs_devices => $zfs_devices,
|
||||
);
|
||||
push( @cols, &html_escape( $format_type || '-' ) );
|
||||
push( @cols, &html_escape( $usage || $text{'part_nouse'} ) );
|
||||
push( @cols, &html_escape( $role || '-' ) );
|
||||
}
|
||||
print &ui_columns_row( \@cols );
|
||||
}
|
||||
print &ui_columns_end();
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'disk_none'}</b><p>\n";
|
||||
}
|
||||
print &ui_links_row(\@links);
|
||||
if ( @{ $disk->{'slices'} || [] } ) {
|
||||
print &ui_links_row( \@links );
|
||||
print &ui_columns_start(
|
||||
[
|
||||
$text{'disk_no'}, $text{'disk_type'},
|
||||
$text{'disk_extent'}, $text{'disk_start'},
|
||||
$text{'disk_end'}, $text{'disk_use'},
|
||||
]
|
||||
);
|
||||
foreach my $s ( @{ $disk->{'slices'} } ) {
|
||||
my @cols = ();
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>",
|
||||
$scale * ( $s->{'startblock'} - 1 );
|
||||
$ext .= sprintf "<img src='images/%s.gif' height='10' width='%d'>",
|
||||
( $s->{'extended'} ? "ext" : "use" ),
|
||||
$scale * ( $s->{'blocks'} );
|
||||
$ext .= sprintf "<img src='images/gap.gif' height='10' width='%d'>",
|
||||
$scale * ( $disk_blocks - $s->{'startblock'} - $s->{'blocks'} );
|
||||
my $url = "edit_slice.cgi?device=$device_url&slice="
|
||||
. &urlize( $s->{'number'} );
|
||||
push( @cols,
|
||||
"<a href='$url'>" . &html_escape( $s->{'number'} ) . "</a>" );
|
||||
push( @cols,
|
||||
&get_type_description( $s->{'type'} ) || $s->{'type'} );
|
||||
push( @cols, $ext );
|
||||
push( @cols, $s->{'startblock'} );
|
||||
push( @cols, $s->{'startblock'} + $s->{'blocks'} - 1 );
|
||||
my @st = &fdisk::device_status( $s->{'device'} );
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
push( @cols, &html_escape( $use || $text{'part_nouse'} ) );
|
||||
print &ui_columns_row( \@cols );
|
||||
}
|
||||
print &ui_columns_end();
|
||||
}
|
||||
else {
|
||||
print "<p>$text{'disk_none'}</p>\n";
|
||||
}
|
||||
}
|
||||
print &ui_links_row( \@links );
|
||||
|
||||
print &ui_hr();
|
||||
print &ui_buttons_start();
|
||||
# Show SMART status link if available
|
||||
if ( &has_command("smartctl") ) {
|
||||
print &ui_hr();
|
||||
print &ui_buttons_start();
|
||||
print &ui_buttons_row( "smart.cgi", $text{'disk_smart'},
|
||||
$text{'disk_smartdesc'}, &ui_hidden( "device", $device ) );
|
||||
print &ui_buttons_end();
|
||||
}
|
||||
|
||||
if (&foreign_installed("smart-status")) {
|
||||
print &ui_buttons_row(
|
||||
"../smart-status/index.cgi",
|
||||
$text{'disk_smart'},
|
||||
$text{'disk_smartdesc'},
|
||||
&ui_hidden("drive", $disk->{'device'}.":"));
|
||||
}
|
||||
|
||||
print &ui_buttons_end();
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
# Debug: ZFS cache detail
|
||||
if ( $in{'debug'} ) {
|
||||
print "<div class='debug-section'>";
|
||||
print "<div class='panel panel-default'>";
|
||||
print
|
||||
"<div class='panel-heading'><h3 class='panel-title'>$text{'disk_debug_zfs_cache'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Pools: " . join( ", ", keys %$zfs_pools ) . "\n\nDevices:\n";
|
||||
foreach my $device_id ( sort keys %$zfs_devices ) {
|
||||
next if $device_id =~ /^_debug_/;
|
||||
my $device_info = $zfs_devices->{$device_id};
|
||||
print
|
||||
"$device_id => Pool: $device_info->{'pool'}, Type: $device_info->{'vdev_type'}, Mirrored: "
|
||||
. ( $device_info->{'is_mirrored'} ? "Yes" : "No" )
|
||||
. ", RAIDZ: "
|
||||
. ( $device_info->{'is_raidz'}
|
||||
? "Yes (Level: $device_info->{'raidz_level'})"
|
||||
: "No" )
|
||||
. ", Single: "
|
||||
. ( $device_info->{'is_single'} ? "Yes" : "No" )
|
||||
. ", Striped: "
|
||||
. ( $device_info->{'is_striped'} ? "Yes" : "No" ) . "\n";
|
||||
}
|
||||
print "</pre>";
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
&ui_print_footer( "", $text{'disk_return'} );
|
||||
|
||||
@@ -1,87 +1,181 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show details of a partition, with buttons to create a filesystem
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} } @{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
# Load required libraries
|
||||
require "./bsdfdisk-lib.pl";
|
||||
our ( %in, %text, $module_name );
|
||||
ReadParse();
|
||||
|
||||
&ui_print_header($part->{'desc'}, $text{'part_title'}, "");
|
||||
# Cache input parameters to avoid repeated hash lookups
|
||||
my $device = $in{'device'};
|
||||
my $slice_num = $in{'slice'};
|
||||
my $part_letter = $in{'part'};
|
||||
my $url_device = &urlize($device);
|
||||
my $url_slice = &urlize($slice_num);
|
||||
my $url_part = &urlize($part_letter);
|
||||
|
||||
# Get the disk and slice using first() to stop at the first matching element
|
||||
my @disks = list_disks_partitions();
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or error( $text{'disk_edevice'} );
|
||||
$in{'device'} !~ /\.\./ or error( $text{'disk_edevice'} );
|
||||
$in{'slice'} =~ /^\d+$/ or error( $text{'slice_egone'} );
|
||||
$in{'part'} =~ /^[a-z]$/ or error( $text{'part_egone'} );
|
||||
my $disk;
|
||||
foreach my $d (@disks) {
|
||||
if ( $d->{'device'} eq $device ) {
|
||||
$disk = $d;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$disk or error( $text{'disk_egone'} );
|
||||
my $slice;
|
||||
foreach my $s ( @{ $disk->{'slices'} } ) {
|
||||
if ( $s->{'number'} eq $slice_num ) {
|
||||
$slice = $s;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$slice or error( $text{'slice_egone'} );
|
||||
my $part;
|
||||
foreach my $p ( @{ $slice->{'parts'} } ) {
|
||||
if ( $p->{'letter'} eq $part_letter ) { $part = $p; last; }
|
||||
}
|
||||
$part or error( $text{'part_egone'} );
|
||||
ui_print_header( $part->{'desc'}, $text{'part_title'}, "" );
|
||||
|
||||
# Check if this is a boot partition
|
||||
my $is_boot = is_boot_partition($part);
|
||||
if ($is_boot) {
|
||||
print ui_alert_box( $text{'part_bootdesc'}, 'info' );
|
||||
}
|
||||
|
||||
# Show current details
|
||||
my @st = &fdisk::device_status($part->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
my $canedit = !@st || !$st[2];
|
||||
my $hiddens = &ui_hidden("device", $in{'device'})."\n".
|
||||
&ui_hidden("slice", $in{'slice'})."\n".
|
||||
&ui_hidden("part", $in{'part'})."\n";
|
||||
my $zfs_info = get_all_zfs_info();
|
||||
my @st = fdisk::device_status( $part->{'device'} );
|
||||
|
||||
# calculate $use from either ZFS info or from a status link
|
||||
my $device_path = $part->{'device'};
|
||||
my $use = $zfs_info->{$device_path} || fdisk::device_status_link(@st);
|
||||
my $canedit = ( !@st && !$zfs_info->{$device_path} && !$is_boot );
|
||||
|
||||
# Prepare hidden fields once
|
||||
my $hiddens =
|
||||
ui_hidden( "device", $device ) . "\n"
|
||||
. ui_hidden( "slice", $slice_num ) . "\n"
|
||||
. ui_hidden( "part", $part_letter ) . "\n";
|
||||
if ($canedit) {
|
||||
print &ui_form_start("save_part.cgi", "post");
|
||||
print $hiddens;
|
||||
}
|
||||
print &ui_table_start($text{'part_header'}, undef, 2);
|
||||
|
||||
print &ui_table_row($text{'part_device'},
|
||||
"<tt>$part->{'device'}</tt>");
|
||||
|
||||
print &ui_table_row($text{'part_size'},
|
||||
&nice_size($part->{'size'}));
|
||||
|
||||
print &ui_table_row($text{'part_start'},
|
||||
$part->{'startblock'});
|
||||
|
||||
print &ui_table_row($text{'part_end'},
|
||||
$part->{'startblock'} + $part->{'blocks'} - 1);
|
||||
print ui_form_start( "save_part.cgi", "post" ), $hiddens;
|
||||
}
|
||||
print ui_table_start( $text{'part_header'}, undef, 2 );
|
||||
print ui_table_row( $text{'part_device'},
|
||||
"<tt>" . html_escape( $part->{'device'} ) . "</tt>" );
|
||||
my $part_bytes = bytes_from_blocks( $part->{'device'}, $part->{'blocks'} );
|
||||
print ui_table_row( $text{'part_size'},
|
||||
$part_bytes ? safe_nice_size($part_bytes) : '-' );
|
||||
print ui_table_row( $text{'part_start'}, $part->{'startblock'} );
|
||||
print ui_table_row( $text{'part_end'},
|
||||
$part->{'startblock'} + $part->{'blocks'} - 1 );
|
||||
my $disk_geom = get_detailed_disk_info( $disk->{'device'} );
|
||||
my $stripesize =
|
||||
( $disk_geom && $disk_geom->{'stripesize'} )
|
||||
? $disk_geom->{'stripesize'}
|
||||
: '-';
|
||||
print ui_table_row( $text{'disk_stripesize'}, $stripesize );
|
||||
|
||||
if ($canedit) {
|
||||
print &ui_table_row($text{'part_type'},
|
||||
&ui_select("type", $part->{'type'},
|
||||
[ &list_partition_types() ], 1, 0, 1));
|
||||
}
|
||||
|
||||
# BSD disklabel partitions only support FreeBSD types
|
||||
print ui_table_row(
|
||||
$text{'part_type'},
|
||||
ui_select(
|
||||
"type", $part->{'type'}, [ list_partition_types('BSD') ],
|
||||
1, 0, 1
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
print &ui_table_row($text{'part_type'},
|
||||
$part->{'type'});
|
||||
}
|
||||
print ui_table_row( $text{'part_type'}, get_format_type($part) );
|
||||
}
|
||||
my $use_text = (
|
||||
( !@st && !$zfs_info->{ $part->{'device'} } ) ? $text{'part_nouse'}
|
||||
: (
|
||||
( $st[2] || $zfs_info->{ $part->{'device'} } )
|
||||
? text( 'part_inuse', $use )
|
||||
: text( 'part_foruse', $use )
|
||||
)
|
||||
);
|
||||
print ui_table_row( $text{'part_use'}, $use_text );
|
||||
|
||||
print &ui_table_row($text{'part_use'},
|
||||
!@st ? $text{'part_nouse'} :
|
||||
$st[2] ? &text('part_inuse', $use) :
|
||||
&text('part_foruse', $use));
|
||||
|
||||
print &ui_table_end();
|
||||
# Add a row for the partition role
|
||||
print ui_table_row( $text{'part_role'}, get_partition_role($part) );
|
||||
print ui_table_end();
|
||||
if ($canedit) {
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
}
|
||||
print ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
}
|
||||
|
||||
# Show newfs and mount buttons
|
||||
# Existing partitions on this slice
|
||||
if ( @{ $slice->{'parts'} || [] } ) {
|
||||
my $zfs = get_all_zfs_info();
|
||||
print ui_hr();
|
||||
print ui_columns_start(
|
||||
[
|
||||
$text{'slice_letter'}, $text{'slice_type'}, $text{'slice_start'},
|
||||
$text{'slice_end'}, $text{'slice_size'}, $text{'slice_use'},
|
||||
$text{'slice_role'}
|
||||
],
|
||||
$text{'epart_existing'}
|
||||
);
|
||||
foreach my $p ( sort { $a->{'startblock'} <=> $b->{'startblock'} }
|
||||
@{ $slice->{'parts'} } )
|
||||
{
|
||||
my $ptype = get_type_description( $p->{'type'} ) || $p->{'type'};
|
||||
my @stp = fdisk::device_status( $p->{'device'} );
|
||||
my $usep =
|
||||
$zfs->{ $p->{'device'} }
|
||||
|| fdisk::device_status_link(@stp)
|
||||
|| $text{'part_nouse'};
|
||||
my $rolep = get_partition_role($p);
|
||||
my $pb2 = bytes_from_blocks( $p->{'device'}, $p->{'blocks'} );
|
||||
print ui_columns_row(
|
||||
[
|
||||
uc( $p->{'letter'} ),
|
||||
&html_escape($ptype),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
( $pb2 ? safe_nice_size($pb2) : '-' ),
|
||||
$usep,
|
||||
&html_escape($rolep)
|
||||
]
|
||||
);
|
||||
}
|
||||
print ui_columns_end();
|
||||
}
|
||||
|
||||
# Show newfs and mount buttons if editing is allowed
|
||||
if ($canedit) {
|
||||
print &ui_hr();
|
||||
|
||||
print &ui_buttons_start();
|
||||
|
||||
&show_filesystem_buttons($hiddens, \@st, $part);
|
||||
|
||||
print &ui_buttons_row(
|
||||
"delete_part.cgi", $text{'part_delete'},
|
||||
$text{'part_deletedesc'}, $hiddens);
|
||||
|
||||
print &ui_buttons_end();
|
||||
}
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
my $mount_return =
|
||||
"edit_part.cgi?device=$url_device&slice=$url_slice&part=$url_part";
|
||||
show_filesystem_buttons( $hiddens, \@st, $part, $mount_return );
|
||||
print ui_buttons_row( "delete_part.cgi", $text{'part_delete'},
|
||||
$text{'part_deletedesc'}, $hiddens );
|
||||
print ui_buttons_end();
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'part_cannotedit'}</b><p>\n";
|
||||
}
|
||||
print $is_boot
|
||||
? "<b>$text{'part_bootcannotedit'}</b><p>\n"
|
||||
: "<b>$text{'part_cannotedit'}</b><p>\n";
|
||||
}
|
||||
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
# SMART button (physical device)
|
||||
if ( &has_command("smartctl") ) {
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
print ui_buttons_row( "smart.cgi", $text{'disk_smart'},
|
||||
$text{'disk_smartdesc'}, ui_hidden( "device", $disk->{'device'} ) );
|
||||
print ui_buttons_end();
|
||||
}
|
||||
ui_print_footer( "edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
|
||||
@@ -1,146 +1,285 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show details of a slice, and partitions on it
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
&ReadParse();
|
||||
our ( %in, %text, $module_name );
|
||||
ReadParse();
|
||||
my $extwidth = 300;
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my @disks = list_disks_partitions();
|
||||
|
||||
&ui_print_header($slice->{'desc'}, $text{'slice_title'}, "");
|
||||
# Validate input parameters to prevent command injection
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or error( $text{'disk_edevice'} );
|
||||
$in{'device'} !~ /\.\./ or error( $text{'disk_edevice'} );
|
||||
$in{'slice'} =~ /^\d+$/ or error( $text{'slice_egone'} );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks
|
||||
or error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} }
|
||||
or error( $text{'slice_egone'} );
|
||||
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
ui_print_header( $slice->{'desc'}, $text{'slice_title'}, "" );
|
||||
|
||||
# Show slice details
|
||||
my @st = &fdisk::device_status($slice->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
my $canedit = !@st || !$st[2];
|
||||
my $hiddens = &ui_hidden("device", $in{'device'})."\n".
|
||||
&ui_hidden("slice", $in{'slice'})."\n";
|
||||
print &ui_form_start("save_slice.cgi");
|
||||
my $zfs_info = get_all_zfs_info();
|
||||
my ( $zfs_pools, $zfs_devices ) = build_zfs_devices_cache();
|
||||
|
||||
# Cache slice device status
|
||||
my @slice_status = fdisk::device_status( $slice->{'device'} );
|
||||
my $slice_use =
|
||||
$zfs_info->{ $slice->{'device'} }
|
||||
? $zfs_info->{ $slice->{'device'} }
|
||||
: fdisk::device_status_link(@slice_status);
|
||||
my $canedit = ( !@slice_status || !$slice_status[2] );
|
||||
|
||||
# Prepare hidden fields
|
||||
my $hiddens = ui_hidden( "device", $in{'device'} ) . "\n"
|
||||
. ui_hidden( "slice", $in{'slice'} ) . "\n";
|
||||
|
||||
# Derive disk scheme for classifier
|
||||
my $base_device = $disk->{'device'};
|
||||
$base_device =~ s{^/dev/}{};
|
||||
my $disk_structure = get_disk_structure($base_device);
|
||||
|
||||
# Device label (GPT label or glabel)
|
||||
my $slice_label = get_device_label_name(
|
||||
disk => $disk,
|
||||
slice => $slice,
|
||||
disk_structure => $disk_structure
|
||||
);
|
||||
|
||||
# Check if this is a boot slice
|
||||
my $is_boot = is_boot_partition($slice);
|
||||
print ui_alert_box( $text{'slice_bootdesc'}, 'info' ) if $is_boot;
|
||||
my $confirm_msg = $text{'confirm_overwrite'}
|
||||
|| 'You will destroy/overwrite existing data structures. Continue?';
|
||||
my $confirm_js = $confirm_msg;
|
||||
$confirm_js =~ s/\\/\\\\/g;
|
||||
$confirm_js =~ s/'/\\'/g;
|
||||
$confirm_js =~ s/\r?\n/\\n/g;
|
||||
print ui_form_start( "save_slice.cgi", "post", undef,
|
||||
"onsubmit=\"return confirm('$confirm_js')\"" );
|
||||
print $hiddens;
|
||||
print &ui_table_start($text{'slice_header'}, undef, 2);
|
||||
print ui_table_start( $text{'slice_header'}, undef, 2 );
|
||||
print ui_table_row( $text{'part_device'},
|
||||
"<tt>" . html_escape( $slice->{'device'} ) . "</tt>" );
|
||||
print ui_table_row( $text{'slice_label'},
|
||||
$slice_label ? "<tt>" . html_escape($slice_label) . "</tt>" : "-" );
|
||||
my $slice_bytes = bytes_from_blocks( $slice->{'device'}, $slice->{'blocks'} );
|
||||
print ui_table_row( $text{'slice_ssize'},
|
||||
$slice_bytes ? safe_nice_size($slice_bytes) : '-' );
|
||||
print ui_table_row( $text{'slice_sstart'}, $slice->{'startblock'} );
|
||||
print ui_table_row( $text{'slice_send'},
|
||||
$slice->{'startblock'} + $slice->{'blocks'} - 1 );
|
||||
|
||||
print &ui_table_row($text{'part_device'},
|
||||
"<tt>$slice->{'device'}</tt>");
|
||||
# Slice type selector (GPT vs legacy)
|
||||
if ( is_using_gpart() ) {
|
||||
my $scheme =
|
||||
( $disk_structure && $disk_structure->{'scheme'} )
|
||||
? $disk_structure->{'scheme'}
|
||||
: 'GPT';
|
||||
my @opts = list_partition_types($scheme);
|
||||
|
||||
print &ui_table_row($text{'slice_ssize'},
|
||||
&nice_size($slice->{'size'}));
|
||||
|
||||
print &ui_table_row($text{'slice_sstart'},
|
||||
$slice->{'startblock'});
|
||||
|
||||
print &ui_table_row($text{'slice_send'},
|
||||
$slice->{'startblock'} + $slice->{'blocks'} - 1);
|
||||
|
||||
print &ui_table_row($text{'slice_stype'},
|
||||
&ui_select("type", $slice->{'type'},
|
||||
[ sort { $a->[1] cmp $b->[1] }
|
||||
map { [ $_, &fdisk::tag_name($_) ] }
|
||||
&fdisk::list_tags() ]));
|
||||
|
||||
print &ui_table_row($text{'slice_sactive'},
|
||||
$slice->{'active'} ? $text{'yes'} :
|
||||
&ui_yesno_radio("active", $slice->{'active'}));
|
||||
|
||||
print &ui_table_row($text{'slice_suse'},
|
||||
!@st ? $text{'part_nouse'} :
|
||||
$st[2] ? &text('part_inuse', $use) :
|
||||
&text('part_foruse', $use));
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
|
||||
print &ui_hr();
|
||||
|
||||
# Show partitions table
|
||||
my @links = ( "<a href='part_form.cgi?device=".&urlize($disk->{'device'}).
|
||||
"&slice=$in{'slice'}'>".$text{'slice_add'}."</a>" );
|
||||
if (@{$slice->{'parts'}}) {
|
||||
print &ui_links_row(\@links);
|
||||
print &ui_columns_start([
|
||||
$text{'slice_letter'},
|
||||
$text{'slice_type'},
|
||||
$text{'slice_extent'},
|
||||
$text{'slice_size'},
|
||||
$text{'slice_start'},
|
||||
$text{'slice_end'},
|
||||
$text{'slice_use'},
|
||||
]);
|
||||
foreach my $p (@{$slice->{'parts'}}) {
|
||||
# Create images for the extent
|
||||
my $ext = "";
|
||||
$ext .= sprintf "<img src=images/gap.gif height=10 width=%d>",
|
||||
$extwidth*($p->{'startblock'} - 1) /
|
||||
$slice->{'blocks'};
|
||||
$ext .= sprintf "<img src=images/%s.gif height=10 width=%d>",
|
||||
$p->{'extended'} ? "ext" : "use",
|
||||
$extwidth*($p->{'blocks'}) /
|
||||
$slice->{'blocks'};
|
||||
$ext .= sprintf "<img src=images/gap.gif height=10 width=%d>",
|
||||
$extwidth*($slice->{'blocks'} - $p->{'startblock'} -
|
||||
$p->{'blocks'}) / $slice->{'blocks'};
|
||||
|
||||
# Work out use
|
||||
my @st = &fdisk::device_status($p->{'device'});
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
|
||||
# Add row for the partition
|
||||
my $url = "edit_part.cgi?device=".&urlize($disk->{'device'}).
|
||||
"&slice=".$slice->{'number'}."&part=".$p->{'letter'};
|
||||
print &ui_columns_row([
|
||||
"<a href='$url'>".uc($p->{'letter'})."</a>",
|
||||
"<a href='$url'>$p->{'type'}</a>",
|
||||
$ext,
|
||||
&nice_size($p->{'size'}),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
$use,
|
||||
]);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
print &ui_links_row(\@links);
|
||||
}
|
||||
# Default sensibly per scheme
|
||||
my $default_type = ( $scheme =~ /GPT/i ) ? 'freebsd-zfs' : 'freebsd';
|
||||
print ui_table_row( $text{'slice_stype'},
|
||||
ui_select( "type", $slice->{'type'} || $default_type, \@opts ) );
|
||||
}
|
||||
else {
|
||||
# No partitions yet
|
||||
if (@st) {
|
||||
# And directly in use, so none can be created
|
||||
print "<b>$text{'slice_none2'}</b><p>\n";
|
||||
}
|
||||
else {
|
||||
# Show link to add first partition
|
||||
print "<b>$text{'slice_none'}</b><p>\n";
|
||||
print &ui_links_row(\@links);
|
||||
}
|
||||
}
|
||||
# Pre-cache tag options for the slice type select (legacy fdisk)
|
||||
my @tags = fdisk::list_tags();
|
||||
my @tag_options = map { [ $_, fdisk::tag_name($_) ] } @tags;
|
||||
@tag_options = sort { $a->[1] cmp $b->[1] } @tag_options;
|
||||
print ui_table_row( $text{'slice_stype'},
|
||||
ui_select( "type", $slice->{'type'}, \@tag_options ) );
|
||||
}
|
||||
|
||||
if ($canedit) {
|
||||
print &ui_hr();
|
||||
print &ui_buttons_start();
|
||||
# Active slice - only applicable for legacy MBR. For GPT/UEFI and for EFI/freebsd-boot types, the active flag is irrelevant.
|
||||
my $is_gpt =
|
||||
is_using_gpart()
|
||||
&& ( $disk_structure
|
||||
&& $disk_structure->{'scheme'}
|
||||
&& $disk_structure->{'scheme'} =~ /GPT/i );
|
||||
if ( !$is_gpt && ( $slice->{'type'} !~ /^(?:efi|freebsd-boot)$/i ) ) {
|
||||
my $active_default = $slice->{'active'} ? 1 : 0;
|
||||
print ui_table_row( $text{'slice_sactive'},
|
||||
ui_yesno_radio( "active", $active_default ) );
|
||||
}
|
||||
else {
|
||||
# Do not offer the control; display 'No' since active is not used here
|
||||
print ui_table_row( $text{'slice_sactive'}, $text{'no'} );
|
||||
}
|
||||
print ui_table_row(
|
||||
$text{'slice_suse'},
|
||||
( !$slice_use || $slice_use eq $text{'part_nouse'} ) ? $text{'part_nouse'}
|
||||
: ( $slice_status[2] ? text( 'part_inuse', $slice_use )
|
||||
: text( 'part_foruse', $slice_use ) )
|
||||
);
|
||||
|
||||
if (!@{$slice->{'parts'}}) {
|
||||
&show_filesystem_buttons($hiddens, \@st, $slice);
|
||||
}
|
||||
# Add a row for the slice role
|
||||
print ui_table_row( $text{'slice_role'}, get_partition_role($slice) );
|
||||
print ui_table_end();
|
||||
print ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
print ui_hr();
|
||||
|
||||
# Button to delete slice
|
||||
print &ui_buttons_row(
|
||||
'delete_slice.cgi',
|
||||
$text{'slice_delete'},
|
||||
$text{'slice_deletedesc'},
|
||||
&ui_hidden("device", $in{'device'})."\n".
|
||||
&ui_hidden("slice", $in{'slice'}));
|
||||
# Show partitions table (only for MBR slices that support BSD disklabel)
|
||||
my $can_have_parts = 0;
|
||||
if ( !is_using_gpart() ) {
|
||||
|
||||
print &ui_buttons_end();
|
||||
}
|
||||
# Legacy MBR with BSD disklabel
|
||||
$can_have_parts = 1;
|
||||
}
|
||||
elsif ($disk_structure
|
||||
&& $disk_structure->{'scheme'}
|
||||
&& $disk_structure->{'scheme'} !~ /GPT/i )
|
||||
{
|
||||
# MBR-style slice
|
||||
$can_have_parts = 1;
|
||||
}
|
||||
my @links =
|
||||
$can_have_parts
|
||||
? ( "<a href='part_form.cgi?device="
|
||||
. $url_device
|
||||
. "&slice=$url_slice'>"
|
||||
. $text{'slice_add'}
|
||||
. "</a>" )
|
||||
: ();
|
||||
if ( @{ $slice->{'parts'} } ) {
|
||||
print ui_links_row( \@links ) if @links;
|
||||
print ui_columns_start(
|
||||
[
|
||||
$text{'slice_letter'}, $text{'slice_type'},
|
||||
$text{'slice_extent'}, $text{'slice_size'},
|
||||
$text{'slice_start'}, $text{'slice_end'},
|
||||
$text{'disk_stripesize'}, $text{'slice_use'},
|
||||
$text{'slice_role'},
|
||||
]
|
||||
);
|
||||
|
||||
# Pre-calculate scaling factor for the partition extent images
|
||||
my $scale = $extwidth / $slice->{'blocks'};
|
||||
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}",
|
||||
$text{'disk_return'});
|
||||
foreach my $p ( @{ $slice->{'parts'} } ) {
|
||||
|
||||
# Create images representing the partition extent
|
||||
my $gap_before = sprintf( "<img src=images/gap.gif height=10 width=%d>",
|
||||
int( $scale * ( $p->{'startblock'} - 1 ) ) );
|
||||
my $img_type = $p->{'extended'} ? "ext" : "use";
|
||||
my $partition_img =
|
||||
sprintf( "<img src=images/%s.gif height=10 width=%d>",
|
||||
$img_type, int( $scale * $p->{'blocks'} ) );
|
||||
my $gap_after = sprintf(
|
||||
"<img src=images/gap.gif height=10 width=%d>",
|
||||
int(
|
||||
$scale * (
|
||||
$slice->{'blocks'} - $p->{'startblock'} - $p->{'blocks'}
|
||||
)
|
||||
)
|
||||
);
|
||||
my $ext = $gap_before . $partition_img . $gap_after;
|
||||
|
||||
# Cache partition device status information
|
||||
my @part_status = fdisk::device_status( $p->{'device'} );
|
||||
my $part_use = $zfs_info->{ $p->{'device'} }
|
||||
|| fdisk::device_status_link(@part_status);
|
||||
|
||||
# Prefer GEOM details for stripesize
|
||||
my $ginfo = get_detailed_disk_info( $p->{'device'} );
|
||||
my $stripesize =
|
||||
( $ginfo && $ginfo->{'stripesize'} ) ? $ginfo->{'stripesize'} : '-';
|
||||
|
||||
# Classify format/use/role via library helper
|
||||
( my $pn = $p->{'device'} ) =~ s{^/dev/}{};
|
||||
my ( $fmt, $use_txt, $role_txt ) = classify_partition_row(
|
||||
base_device => $base_device,
|
||||
scheme => ( $disk_structure->{'scheme'} || '' ),
|
||||
part_name => $pn,
|
||||
entry_part_type => $p->{'type'},
|
||||
zfs_devices => $zfs_devices,
|
||||
);
|
||||
$use_txt ||= $part_use;
|
||||
$role_txt ||= get_partition_role($p);
|
||||
|
||||
# Build edit URL
|
||||
my $url =
|
||||
"edit_part.cgi?device="
|
||||
. $url_device
|
||||
. "&slice="
|
||||
. $url_slice
|
||||
. "&part="
|
||||
. &urlize( $p->{'letter'} );
|
||||
my $psz_b = bytes_from_blocks( $p->{'device'}, $p->{'blocks'} );
|
||||
print ui_columns_row(
|
||||
[
|
||||
"<a href='$url'>" . uc( $p->{'letter'} ) . "</a>",
|
||||
"<a href='$url'>"
|
||||
. html_escape( $fmt || get_format_type($p) ) . "</a>",
|
||||
$ext,
|
||||
( $psz_b ? safe_nice_size($psz_b) : '-' ),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
$stripesize,
|
||||
$use_txt,
|
||||
html_escape($role_txt),
|
||||
]
|
||||
);
|
||||
}
|
||||
print ui_columns_end();
|
||||
print ui_links_row( \@links ) if @links;
|
||||
}
|
||||
else {
|
||||
# GPT partitions do not have sub-partitions
|
||||
if ( !$can_have_parts ) {
|
||||
|
||||
# No message needed for GPT; partitions are top-level
|
||||
}
|
||||
|
||||
# If slice is in use by a filesystem OR it is a boot slice, do not allow creating partitions
|
||||
elsif ( @slice_status || $zfs_info->{ $slice->{'device'} } || $is_boot ) {
|
||||
print "<b>$text{'slice_none2'}</b><p>\n";
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'slice_none'}</b><p>\n";
|
||||
print ui_links_row( \@links ) if @links;
|
||||
}
|
||||
}
|
||||
if ( $canedit && !$is_boot ) { # Do not allow editing boot slices
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
if ( !@{ $slice->{'parts'} } ) {
|
||||
my $mount_return =
|
||||
"edit_slice.cgi?device=$url_device&slice=$url_slice";
|
||||
show_filesystem_buttons( $hiddens, \@slice_status, $slice,
|
||||
$mount_return );
|
||||
}
|
||||
print ui_buttons_row(
|
||||
'change_slice_label.cgi',
|
||||
$text{'slice_chglabel'},
|
||||
$text{'slice_chglabeldesc'},
|
||||
ui_hidden( "device", $in{'device'} ) . "\n"
|
||||
. ui_hidden( "slice", $in{'slice'} )
|
||||
);
|
||||
print ui_buttons_row(
|
||||
'delete_slice.cgi',
|
||||
$text{'slice_delete'},
|
||||
$text{'slice_deletedesc'},
|
||||
ui_hidden( "device", $in{'device'} ) . "\n"
|
||||
. ui_hidden( "slice", $in{'slice'} )
|
||||
);
|
||||
print ui_buttons_end();
|
||||
}
|
||||
|
||||
# SMART button (physical device)
|
||||
if ( &has_command("smartctl") ) {
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
print ui_buttons_row( "smart.cgi", $text{'disk_smart'},
|
||||
$text{'disk_smartdesc'}, ui_hidden( "device", $disk->{'device'} ) );
|
||||
print ui_buttons_end();
|
||||
}
|
||||
|
||||
ui_print_footer( "edit_disk.cgi?device=$url_device", $text{'disk_return'} );
|
||||
|
||||
@@ -6,57 +6,107 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
&error_setup($text{'fsck_err'});
|
||||
&error_setup( $text{'fsck_err'} );
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'slice'} =~ /^\d+$/ or &error( $text{'slice_egone'} );
|
||||
$in{'part'} =~ /^[a-z]$/ or &error( $text{'part_egone'} ) if $in{'part'};
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my ($object, $part);
|
||||
if ($in{'part'} ne '') {
|
||||
($part) = grep { $_->{'letter'} eq $in{'part'} }
|
||||
@{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
$object = $part;
|
||||
}
|
||||
else {
|
||||
$object = $slice;
|
||||
}
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
my ( $object, $part );
|
||||
|
||||
&ui_print_unbuffered_header($object->{'desc'}, $text{'fsck_title'}, "");
|
||||
|
||||
# Do the creation
|
||||
print &text('fsck_checking', "<tt>$object->{'device'}</tt>"),"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = &get_check_filesystem_command($disk, $slice, $part);
|
||||
&additional_log('exec', undef, $cmd);
|
||||
my $fh = "CMD";
|
||||
&open_execute_command($fh, $cmd, 2);
|
||||
while(<$fh>) {
|
||||
print &html_escape($_);
|
||||
}
|
||||
close($fh);
|
||||
print "</pre>";
|
||||
if ($?) {
|
||||
print $text{'fsck_failed'},"<p>\n";
|
||||
}
|
||||
if ( $in{'part'} ne '' ) {
|
||||
($part) = grep { $_->{'letter'} eq $in{'part'} } @{ $slice->{'parts'} };
|
||||
$part || &error( $text{'part_egone'} );
|
||||
$object = $part;
|
||||
}
|
||||
else {
|
||||
print $text{'fsck_done'},"<p>\n";
|
||||
}
|
||||
&webmin_log("fsck", $in{'part'} ne '' ? "part" : "object",
|
||||
$object->{'device'}, $object);
|
||||
$object = $slice;
|
||||
}
|
||||
|
||||
if ($in{'part'} ne '') {
|
||||
&ui_print_footer("edit_part.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}&part=$in{'part'}",
|
||||
$text{'part_return'});
|
||||
}
|
||||
# Safety checks: do not run fsck on boot partitions or in-use devices
|
||||
if ( is_boot_partition($object) ) {
|
||||
&error( $in{'part'} ne '' ? $text{'part_eboot'} : $text{'slice_eboot'} );
|
||||
}
|
||||
my @st_obj = &fdisk::device_status( $object->{'device'} );
|
||||
my $use_obj = &fdisk::device_status_link(@st_obj);
|
||||
if ( @st_obj && $st_obj[2] ) {
|
||||
&error( &text( 'part_esave', &html_escape($use_obj) ) );
|
||||
}
|
||||
|
||||
&ui_print_unbuffered_header( $object->{'desc'}, $text{'fsck_title'}, "" );
|
||||
|
||||
# If device is ZFS, do not run fsck; show zpool status instead
|
||||
my $zmap = get_all_zfs_info();
|
||||
if ( $zmap->{ $object->{'device'} } ) {
|
||||
my $pool = $zmap->{ $object->{'device'} };
|
||||
$pool =~ s/^.*?\b([A-Za-z0-9_\-]+)\b.*$/$1/;
|
||||
print &text(
|
||||
'fsck_checking', "<tt>" . &html_escape( $object->{'device'} ) . "</tt>"
|
||||
),
|
||||
"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = "zpool status 2>&1";
|
||||
&additional_log( 'exec', undef, $cmd );
|
||||
print &html_escape( &backquote_command($cmd) );
|
||||
print "</pre>";
|
||||
print $text{'fsck_done'}, "<p>\n";
|
||||
}
|
||||
else {
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
}
|
||||
# Do the creation
|
||||
print &text(
|
||||
'fsck_checking', "<tt>" . &html_escape( $object->{'device'} ) . "</tt>"
|
||||
),
|
||||
"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = &get_check_filesystem_command( $disk, $slice, $part );
|
||||
&additional_log( 'exec', undef, $cmd );
|
||||
my $out = &backquote_command( $cmd . " 2>&1" );
|
||||
foreach my $line ( split( /\n/, $out ) ) {
|
||||
$line =~ s/[^\x09\x0A\x0D\x20-\x7E]//g;
|
||||
print &html_escape($line) . "\n";
|
||||
}
|
||||
print "</pre>";
|
||||
my $rc = $? >> 8;
|
||||
if ( $rc == 0 ) {
|
||||
print $text{'fsck_done'}, "<p>\n";
|
||||
}
|
||||
elsif ( $rc == 1 ) {
|
||||
print $text{'fsck_done'}, "<p>\n";
|
||||
print $text{'fsck_fixed'}, "<p>\n" if ( $text{'fsck_fixed'} );
|
||||
}
|
||||
elsif ( $rc == 2 ) {
|
||||
print $text{'fsck_done'}, "<p>\n";
|
||||
print $text{'fsck_reboot'}, "<p>\n" if ( $text{'fsck_reboot'} );
|
||||
}
|
||||
else {
|
||||
print $text{'fsck_failed'}, "<p>\n";
|
||||
}
|
||||
}
|
||||
&webmin_log( "fsck", $in{'part'} ne '' ? "part" : "object",
|
||||
$object->{'device'}, $object );
|
||||
|
||||
if ( $in{'part'} ne '' ) {
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
my $url_part = &urlize( $in{'part'} );
|
||||
&ui_print_footer(
|
||||
"edit_part.cgi?device=$url_device&slice=$url_slice&part=$url_part",
|
||||
$text{'part_return'}
|
||||
);
|
||||
}
|
||||
else {
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&ui_print_footer(
|
||||
"edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
}
|
||||
|
||||
BIN
bsdfdisk/images/free.gif
Normal file
BIN
bsdfdisk/images/free.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 B |
@@ -1,41 +1,57 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show a list of disks
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, %config, $module_name);
|
||||
|
||||
&ui_print_header(undef, $text{'index_title'}, "", "intro", 1, 1, 0,
|
||||
&help_search_link("fdisk", "man"));
|
||||
|
||||
my $err = &check_fdisk();
|
||||
# Check prerequisites first
|
||||
my $err = check_fdisk();
|
||||
if ($err) {
|
||||
&ui_print_endpage(&text('index_problem', $err));
|
||||
}
|
||||
ui_print_header(undef, $text{'index_title'}, "", "intro", 1, 1, 0);
|
||||
print "<b>$text{'index_problem'}</b><br>\n$err\n";
|
||||
ui_print_footer("/", $text{'index_return'});
|
||||
exit;
|
||||
}
|
||||
|
||||
# Print header with help link
|
||||
ui_print_header(undef, $text{'index_title'}, "", "intro", 1, 1, 0,
|
||||
help_search_link("fdisk", "man"));
|
||||
|
||||
# List and sort disks by device name
|
||||
my @disks = list_disks_partitions();
|
||||
@disks = sort { ($a->{'device'}//'') cmp ($b->{'device'}//'') } @disks;
|
||||
|
||||
my @disks = &list_disks_partitions();
|
||||
@disks = sort { $a->{'device'} cmp $b->{'device'} } @disks;
|
||||
if (@disks) {
|
||||
print &ui_columns_start([ $text{'index_dname'},
|
||||
$text{'index_dsize'},
|
||||
$text{'index_dmodel'},
|
||||
$text{'index_dparts'} ]);
|
||||
foreach my $d (@disks) {
|
||||
print &ui_columns_row([
|
||||
"<a href='edit_disk.cgi?device=".&urlize($d->{'device'}).
|
||||
"'>".&partition_description($d->{'device'})."</a>",
|
||||
&nice_size($d->{'size'}),
|
||||
$d->{'model'},
|
||||
scalar(@{$d->{'slices'}}),
|
||||
]);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'index_none'}</b> <p>\n";
|
||||
}
|
||||
print ui_columns_start([
|
||||
$text{'index_dname'},
|
||||
$text{'index_dsize'},
|
||||
$text{'index_dmodel'},
|
||||
$text{'index_dparts'}
|
||||
]);
|
||||
|
||||
&ui_print_footer("/", $text{'index'});
|
||||
foreach my $d (@disks) {
|
||||
my $device = $d->{'device'} // '';
|
||||
my $disk_name = $device; $disk_name =~ s{^/dev/}{};
|
||||
# Prefer mediasize from gpart list (bytes); fallback to diskinfo size
|
||||
my $base = $device; $base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
my $bytes = $ds && $ds->{'mediasize'} ? $ds->{'mediasize'} : $d->{'size'};
|
||||
my $size_display = defined $bytes ? safe_nice_size($bytes) : '-';
|
||||
my $model = $d->{'model'} // '-';
|
||||
my $url_device = urlize($device);
|
||||
my $slices_cnt = scalar(@{ $d->{'slices'} || [] });
|
||||
|
||||
print ui_columns_row([
|
||||
"<a href='edit_disk.cgi?device=$url_device'>$disk_name</a>",
|
||||
$size_display,
|
||||
$model, # Now correctly populated from bsdfdisk-lib.pl
|
||||
$slices_cnt,
|
||||
]);
|
||||
}
|
||||
print ui_columns_end();
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'index_none'}</b><p>\n";
|
||||
}
|
||||
|
||||
ui_print_footer("/", $text{'index_return'});
|
||||
247
bsdfdisk/lang/en
247
bsdfdisk/lang/en
@@ -1,42 +1,92 @@
|
||||
index_title=Partitions on Local Disks
|
||||
index_ecmd=The required command $1 is missing
|
||||
index_problem=This module cannot be used : $1
|
||||
index_none=No disks were found on this system!
|
||||
index_ecmd=Missing required command $1
|
||||
index_problem=Cannot use this module : $1
|
||||
index_none=No disks found on this system!
|
||||
index_dname=Disk name
|
||||
index_dsize=Total size
|
||||
index_dmodel=Make and model
|
||||
index_dparts=Slices
|
||||
index_return=list of disks
|
||||
index_format=Format
|
||||
index_efdisk=You must have $1 or $2 installed to use this module
|
||||
disk_edevice=Invalid device parameter
|
||||
nslice_etype=Invalid slice type
|
||||
|
||||
# Disk overview
|
||||
disk_title=Edit Disk
|
||||
disk_egone=Disk no longer exists!
|
||||
disk_no=Slice
|
||||
disk_type=Type
|
||||
disk_extent=Extent
|
||||
disk_start=Start block
|
||||
disk_end=End block
|
||||
disk_use=Used by
|
||||
disk_scount=$1 partitions
|
||||
disk_parts=Partitions
|
||||
disk_free=Free space
|
||||
disk_vm=Virtual memory
|
||||
disk_iscsi=iSCSI shared device $1
|
||||
disk_none=This disk has no slices yet.
|
||||
disk_size=Size
|
||||
disk_dsize=<b>Disk size:</b> $1
|
||||
disk_model=<b>Make and model:</b> $1
|
||||
disk_cylinders=<b>Cylinders:</b> $1
|
||||
disk_blocks=<b>Blocks:</b> $1
|
||||
disk_device=<b>Device file:</b> $1
|
||||
disk_dsize=Disk size
|
||||
disk_model=Make and model
|
||||
disk_cylinders=Cylinders
|
||||
disk_blocks=Blocks
|
||||
disk_device=Device file
|
||||
disk_return=disk details and list of slices
|
||||
disk_add=Create a new slice.
|
||||
disk_smart=Show SMART Status
|
||||
disk_smartdesc=Show the current status of this drive as detected by SMART, and check it for disk errors.
|
||||
disk_no=No.
|
||||
disk_partno=Part. No.
|
||||
disk_partname=Part. Name
|
||||
disk_partlabel=Part. Label
|
||||
disk_subpart=Sub-part.
|
||||
disk_size=Size
|
||||
disk_free_space=Free Space
|
||||
disk_available=Available
|
||||
disk_format=Format
|
||||
disk_use=Used by
|
||||
disk_role=Role Type
|
||||
|
||||
select_device=$1 device $2
|
||||
select_slice=$1 device $2 slice $3
|
||||
select_part=$1 device $2 slice $3 partition $4
|
||||
# Debug and scheme
|
||||
disk_show_debug=Show raw debug information
|
||||
disk_hide_debug=Hide debug information
|
||||
disk_scheme=Partition Scheme
|
||||
disk_unknown=Unknown
|
||||
disk_sectorsize=Sector Size
|
||||
disk_bytes=bytes
|
||||
|
||||
# GEOM information
|
||||
disk_geom_header=GEOM Information
|
||||
disk_geom_details=GEOM Disk Details
|
||||
disk_mediasize=Media Size
|
||||
disk_stripesize=Stripe Size
|
||||
disk_stripeoffset=Stripe Offset
|
||||
disk_mode=Mode
|
||||
disk_rotationrate=Rotation Rate
|
||||
disk_ident=Identifier
|
||||
disk_lunid=LUN ID
|
||||
disk_descr=Description
|
||||
disk_rpm=RPM
|
||||
disk_ssd=SSD (Solid State Drive)
|
||||
|
||||
# Debug panels
|
||||
disk_debug_gpart=GPART Output
|
||||
disk_debug_structure=Parsed Disk Structure
|
||||
disk_debug_geom=GEOM Disk List
|
||||
disk_debug_zfs=ZFS Status
|
||||
disk_debug_zfs_cache=ZFS Devices Cache
|
||||
|
||||
# Roles and usage
|
||||
disk_inzfs=In ZFS pool
|
||||
disk_zfs_log=ZFS Log Device
|
||||
disk_zfs_cache=ZFS Cache Device
|
||||
disk_zfs_spare=ZFS Spare Device
|
||||
disk_zfs_mirror=ZFS Mirror
|
||||
disk_zfs_stripe=ZFS Stripe
|
||||
disk_zfs_single=ZFS Single Device
|
||||
disk_zfs_data=ZFS Data Device
|
||||
disk_boot=Boot Partition
|
||||
disk_boot_role=System Boot
|
||||
disk_swap=Swap
|
||||
disk_swap_role=Virtual Memory
|
||||
|
||||
# Slice pages
|
||||
slice_title=Edit Slice
|
||||
slice_egone=Selected slice does not exist!
|
||||
slice_ssize=Slice size
|
||||
@@ -56,55 +106,83 @@ slice_use=Used by
|
||||
slice_none=This slice has no partitions yet.
|
||||
slice_none2=This slice has no partitions, and none can be created as it is in use as a filesystem.
|
||||
slice_delete=Delete Slice
|
||||
slice_deletedesc=Delete this slice and all partitions and filesystems within it. Any data on those filesystem will be almost certainly unrecoverable.
|
||||
slice_deletedesc=Delete this slice and all partitions and filesystems within it. Any data on that filesystem will be almost certainly unrecoverable.
|
||||
slice_return=slice details and list of partitions
|
||||
slice_err=Failed to modify slice
|
||||
slice_header=Slice details
|
||||
slice_suse=Directly used by
|
||||
slice_label=Device label
|
||||
slice_adddesc=Create a new partition within this slice.
|
||||
slice_cannotedit=This slice cannot be modified as it is currently in use.
|
||||
slice_eboot=Cannot delete this slice as it contains boot partitions
|
||||
slice_bootdesc=This slice contains bootloader code or kernel files needed to start the system
|
||||
slice_role=Role
|
||||
slice_chglabel=Change device label
|
||||
slice_chglabeldesc=Set or update the label for this slice.
|
||||
slice_label_title=Change Slice Label
|
||||
slice_label_header=Change device label
|
||||
slice_label_current=Current label
|
||||
slice_label_new=New label
|
||||
slice_label_err=Failed to change slice label
|
||||
slice_label_empty=Label cannot be empty
|
||||
slice_label_noglabel=glabel command not available to set labels on this disk scheme
|
||||
|
||||
# Delete slice dialogs
|
||||
dslice_title=Delete Slice
|
||||
dslice_rusure=Are you sure you want to delete the slice $1? Any partitions and filesystems within it will also be deleted.
|
||||
dslice_rusure=Are you sure you want to delete the slice $1 ? Any partitions and filesystems within it will also be deleted.
|
||||
dslice_warn=Warning - this slice is currently used by : $1
|
||||
dslice_confirm=Delete Now
|
||||
dslice_deleting=Deleting slice $1 ..
|
||||
dslice_failed=.. deletion failed : $1
|
||||
dslice_done=.. done
|
||||
|
||||
# Create slice
|
||||
nslice_title=Create Slice
|
||||
nslice_header=New slice details
|
||||
nslice_number=Slice number
|
||||
nslice_autonext=Will auto-select next index
|
||||
nslice_diskblocks=Disk size in blocks
|
||||
nslice_start=Starting block
|
||||
nslice_end=Ending block
|
||||
nslice_type=New slice type
|
||||
nslice_makepart=Create default partition?
|
||||
nslice_type=Slice type
|
||||
nslice_err=Failed to create slice
|
||||
nslice_enumber=Missing or non-numeric slice number
|
||||
nslice_enumber=Slice number must be a number
|
||||
nslice_eclash=A slice with number $1 already exists
|
||||
nslice_estart=Starting block must be a number
|
||||
nslice_eend=Ending block must be a number
|
||||
nslice_erange=Starting block must be lower than the ending block
|
||||
nslice_emax=Ending block cannot be larger than the disk size of $1 blocks
|
||||
nslice_erange=Starting block must be before ending block
|
||||
nslice_emax=Ending block cannot be larger than disk size of $1 blocks
|
||||
nslice_creating=Creating slice $1 on $2 ..
|
||||
nslice_failed=.. slice creation failed : $1
|
||||
nslice_failed=.. creation failed : $1
|
||||
nslice_done=.. slice added
|
||||
nslice_parting=Create default partitions in slice $1 on $2 ..
|
||||
nslice_pfailed=.. partition creation failed : $1
|
||||
nslice_pdone=.. partition added
|
||||
nslice_existing_header=Existing slices on this disk
|
||||
nslice_existing_parts=Existing partitions on this disk
|
||||
nslice_existing_slices_label=Existing slices
|
||||
nslice_existing_parts_label=Existing partitions
|
||||
nslice_enospace=No space on device left to create a slice!
|
||||
epart_existing=Existing partitions on this slice
|
||||
|
||||
# Create partition
|
||||
npart_title=Create Partition
|
||||
npart_header=New partition details
|
||||
npart_letter=Partition letter
|
||||
npart_diskblocks=Slice size in blocks
|
||||
npart_slicerel=(slice-relative)
|
||||
npart_creserved=partition 'c' is reserved
|
||||
npart_type=Partition type
|
||||
npart_err=Failed to create partition
|
||||
npart_eletter=Partition number must be a letter from A to D
|
||||
npart_eletter=Partition letter must be a-h (excluding c)
|
||||
npart_ereserved=Partition 'c' is reserved for the whole slice in BSD disklabels
|
||||
npart_eclash=A partition with letter $1 already exists
|
||||
npart_emax=Ending block cannot be larger than the slice size of $1 blocks
|
||||
npart_emax=Ending block cannot be larger than slice size of $1 blocks
|
||||
npart_erange=Start block must be less than end block
|
||||
npart_creating=Creating partition $1 on $2 ..
|
||||
npart_failed=.. partition creation failed : $1
|
||||
npart_done=.. partition added
|
||||
|
||||
# Edit partition
|
||||
part_title=Edit Partition
|
||||
part_egone=Partition no longer exists!
|
||||
part_header=Partition details
|
||||
@@ -118,9 +196,9 @@ part_nouse=Nothing
|
||||
part_inuse=In use by $1
|
||||
part_foruse=For use by $1
|
||||
part_newfs=Create Filesystem
|
||||
part_newfsdesc=Click this button to create a new UFS filesystem on this device. Any data that was previously on the partition will be erased.
|
||||
part_newfsdesc=Click this button to create a new filesystem on this device. Will destroy/overwrite previous filesystem if it's not a pool!
|
||||
part_fsck=Check Filesystem
|
||||
part_fsckdesc=Click this button to check the UFS filesystem on this device, and repair any errors found.
|
||||
part_fsckdesc=Click this button to check the filesystem on this device, and repair any errors found.
|
||||
part_delete=Delete Partition
|
||||
part_deletedesc=Click this button to remove this partition from the slice. Any data on the partition will be lost forever.
|
||||
part_return=partition details
|
||||
@@ -128,18 +206,29 @@ part_err=Failed to save partition
|
||||
part_esave=Currently in use by $1
|
||||
part_newmount=Mount Partition On:
|
||||
part_newmount2=Mount Partition
|
||||
part_mountmsg=Mount this device on new directory on your system, so that it can be used to store files. A filesystem must have been already created on the device.
|
||||
part_mountmsg2=Mount this device as virtual memory on your system, to increase the amount of memory available.
|
||||
part_mountmsg=Mount this device on a new directory on your system, so that it can be used to store files. A filesystem must already be created on the device.
|
||||
part_mountmsg2=Mount this device as virtual memory on your system to increase the amount of available memory.
|
||||
part_cannotedit=This partition cannot be modified as it is currently in use.
|
||||
part_boot=Boot Partition
|
||||
part_eboot=Cannot delete boot partitions as this may render the system unbootable
|
||||
part_bootdesc=This partition contains bootloader code or kernel files needed to start the system
|
||||
part_zfslog=ZFS Log Device
|
||||
part_zfsdata=ZFS Data Device
|
||||
part_mounted=Mounted on $1
|
||||
part_unused=Not in use
|
||||
part_bootcannotedit=This partition cannot be modified as it is a boot partition. Changing it could render the system unbootable.
|
||||
part_role=Role
|
||||
|
||||
# Delete partition dialogs
|
||||
dpart_title=Delete Partition
|
||||
dpart_rusure=Are you sure you want to delete the partition $1? Any filesystems within it will also be deleted.
|
||||
dpart_rusure=Are you sure you want to delete the partition $1 ? Any filesystems in it will also be deleted.
|
||||
dpart_warn=Warning - this partition is currently used by $1
|
||||
dpart_confirm=Delete Now
|
||||
dpart_deleting=Deleting partition $1 ..
|
||||
dpart_failed=.. deletion failed : $1
|
||||
dpart_done=.. done
|
||||
|
||||
# New filesystem
|
||||
newfs_title=Create Filesystem
|
||||
newfs_header=New filesystem details
|
||||
newfs_free=Space to reserve for root
|
||||
@@ -147,27 +236,87 @@ newfs_deffree=Default (8%)
|
||||
newfs_trim=Enable TRIM mode for SSDs
|
||||
newfs_label=Filesystem label
|
||||
newfs_none=None
|
||||
newfs_create=Create Now
|
||||
newfs_err=Failed to create filesystem
|
||||
newfs_efree=Space to reserve for root must be a percentage
|
||||
newfs_elabel=Missing or invalid label
|
||||
newfs_efree=Missing or invalid percentage of free space
|
||||
newfs_elabel=Missing or invalid filesystem label
|
||||
newfs_creating=Creating filesystem on $1 ..
|
||||
newfs_failed=.. creation failed!
|
||||
newfs_done=.. created successfully
|
||||
newfs_failed=.. creation failed : $1
|
||||
newfs_done=.. filesystem created
|
||||
create=Create
|
||||
newfs_zfs_title=Create ZFS Filesystem
|
||||
newfs_zfs_header=New ZFS filesystem details
|
||||
newfs_zfs_parent=Parent file system:
|
||||
newfs_zfs_existing=Existing filesystems:
|
||||
newfs_zfs_fs=file system
|
||||
newfs_zfs_used=used
|
||||
newfs_zfs_avail=avail
|
||||
newfs_zfs_refer=refer
|
||||
newfs_zfs_mount=mountpoint
|
||||
newfs_zfs_name=Name:
|
||||
newfs_zfs_mountpoint=Mount point:
|
||||
newfs_zfs_mount_blank=blank for default
|
||||
newfs_zfs_opts=File system options
|
||||
newfs_zfs_tab_fs=Create Filesystem
|
||||
newfs_zfs_tab_vol=Create Volume
|
||||
newfs_zfs_aclflags=ACL inherit flags:
|
||||
newfs_zfs_aclflags_desc=Add :fd to base NFSv4 ACL entries
|
||||
newfs_zfs_acltype_desc=Controls whether ACLs are enabled and if so what type of ACL to use.
|
||||
newfs_zfs_aclinherit_desc=Controls how ACL entries are inherited when files and directories are created.
|
||||
newfs_zfs_aclmode_desc=Controls how an ACL is modified during a chmod operation.
|
||||
newfs_zfs_err=Failed to create ZFS filesystem
|
||||
newfs_zfs_nozfs=zfs command not available
|
||||
newfs_zfs_notinpool=Device is not part of a ZFS pool
|
||||
newfs_zfs_badname=Invalid dataset name
|
||||
newfs_zfs_badopt=Invalid value for option $1
|
||||
newfs_zfs_creating=Creating ZFS filesystem on $1 ..
|
||||
newfs_zfs_failed=.. creation failed : $1
|
||||
newfs_zfs_done=.. filesystem created
|
||||
newfs_zvol_title=Create ZFS Volume
|
||||
newfs_zvol_header=New ZFS volume details
|
||||
newfs_zvol_name=Name:
|
||||
newfs_zvol_size=Size:
|
||||
newfs_zvol_opts=Volume options
|
||||
newfs_zvol_existing=Existing volumes:
|
||||
newfs_zvol_vol=volume
|
||||
newfs_zvol_volsize=volsize
|
||||
newfs_zvol_sparse=Sparse:
|
||||
newfs_zvol_sparse_desc=Create as sparse volume (thin provisioned)
|
||||
newfs_zvol_refreservation=Refreservation:
|
||||
newfs_zvol_err=Failed to create ZFS volume
|
||||
newfs_zvol_badname=Invalid volume name
|
||||
newfs_zvol_badsize=Invalid volume size
|
||||
newfs_zvol_badopt=Invalid value for option $1
|
||||
newfs_zvol_badrefres=Invalid refreservation size
|
||||
newfs_zvol_creating=Creating ZFS volume on $1 ..
|
||||
newfs_zvol_failed=.. creation failed : $1
|
||||
newfs_zvol_done=.. volume created
|
||||
|
||||
# FS check
|
||||
fsck_title=Check Filesystem
|
||||
fsck_header=Filesystem check options
|
||||
fsck_repair=Repair mode
|
||||
fsck_fix=Only fix safe errors
|
||||
fsck_fix2=Try to fix all errors
|
||||
fsck_err=Failed to check filesystem
|
||||
fsck_checking=Checking filesystem on $1 ..
|
||||
fsck_exec=Executing command $1 ..
|
||||
fsck_failed=.. check failed!
|
||||
fsck_done=.. check completed with no errors found
|
||||
fsck_done=.. check complete
|
||||
fsck_fixed=.. file system was modified and fixed.
|
||||
fsck_reboot=.. file system modified; a reboot may be required.
|
||||
fsck_checking=Checking filesystem on $1 ..
|
||||
confirm_overwrite=You will destroy/overwrite existing data structures. Continue?
|
||||
|
||||
log_create_slice=Created slice $1
|
||||
log_delete_slice=Deleted slice $1
|
||||
log_modify_slice=Modified slice $1
|
||||
log_create_part=Created partition $1
|
||||
log_delete_part=Deleted partition $1
|
||||
log_modify_part=Modified partition $1
|
||||
log_newfs_part=Created filesystem on partition $1
|
||||
log_fsck_part=Checked filesystem on partition $1
|
||||
# Logging
|
||||
action_create_slice=Created slice $1
|
||||
action_delete_slice=Deleted slice $1
|
||||
action_modify_slice=Modified slice $1
|
||||
action_create_part=Created partition $1
|
||||
action_delete_part=Deleted partition $1
|
||||
action_modify_part=Modified partition $1
|
||||
action_create_fs=Created filesystem on $1
|
||||
action_check_fs=Checked filesystem on $1
|
||||
|
||||
__norefs=1
|
||||
# Generic
|
||||
save=Save
|
||||
yes=Yes
|
||||
no=No
|
||||
|
||||
@@ -6,67 +6,123 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
&error_setup($text{'newfs_err'});
|
||||
&error_setup( $text{'newfs_err'} );
|
||||
|
||||
# Get the disk and slice
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'slice'} =~ /^\d+$/ or &error( $text{'slice_egone'} );
|
||||
$in{'part'} =~ /^[a-z]$/ or &error( $text{'part_egone'} ) if $in{'part'};
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my ($object, $part);
|
||||
if ($in{'part'} ne '') {
|
||||
($part) = grep { $_->{'letter'} eq $in{'part'} }
|
||||
@{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
$object = $part;
|
||||
}
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
my ( $object, $part );
|
||||
|
||||
if ( $in{'part'} ne '' ) {
|
||||
($part) = grep { $_->{'letter'} eq $in{'part'} } @{ $slice->{'parts'} };
|
||||
$part || &error( $text{'part_egone'} );
|
||||
$object = $part;
|
||||
}
|
||||
else {
|
||||
$object = $slice;
|
||||
}
|
||||
$object = $slice;
|
||||
}
|
||||
|
||||
# Safety checks: do not run newfs on boot partitions or in-use devices
|
||||
if ( is_boot_partition($object) ) {
|
||||
&error( $in{'part'} ne '' ? $text{'part_eboot'} : $text{'slice_eboot'} );
|
||||
}
|
||||
my @st_obj = &fdisk::device_status( $object->{'device'} );
|
||||
my $use_obj = &fdisk::device_status_link(@st_obj);
|
||||
if ( @st_obj && $st_obj[2] ) {
|
||||
&error( &text( 'part_esave', &html_escape($use_obj) ) );
|
||||
}
|
||||
|
||||
# Validate inputs
|
||||
my $newfs = { };
|
||||
$in{'free_def'} || $in{'free'} =~ /^\d+$/ && $in{'free'} <= 100 ||
|
||||
&error($text{'newfs_efree'});
|
||||
my $newfs = {};
|
||||
$in{'free_def'}
|
||||
|| ( $in{'free'} =~ /^\d+$/ && $in{'free'} >= 0 && $in{'free'} <= 100 )
|
||||
|| &error( $text{'newfs_efree'} );
|
||||
$newfs->{'free'} = $in{'free_def'} ? undef : $in{'free'};
|
||||
$newfs->{'trim'} = $in{'trim'};
|
||||
$in{'label_def'} || $in{'label'} =~ /^\S+$/ ||
|
||||
&error($text{'newfs_elabel'});
|
||||
$in{'label_def'}
|
||||
|| length( $in{'label'} ) > 0
|
||||
|| &error( $text{'newfs_elabel'} );
|
||||
$newfs->{'label'} = $in{'label_def'} ? undef : $in{'label'};
|
||||
|
||||
&ui_print_unbuffered_header($object->{'desc'}, $text{'newfs_title'}, "");
|
||||
&ui_print_unbuffered_header( $object->{'desc'}, $text{'newfs_title'}, "" );
|
||||
|
||||
# Do the creation
|
||||
print &text('newfs_creating', "<tt>$object->{'device'}</tt>"),"<br>\n";
|
||||
print &text(
|
||||
'newfs_creating', "<tt>" . &html_escape( $object->{'device'} ) . "</tt>"
|
||||
),
|
||||
"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = &get_create_filesystem_command($disk, $slice, $part, $newfs);
|
||||
&additional_log('exec', undef, $cmd);
|
||||
my $fh = "CMD";
|
||||
&open_execute_command($fh, $cmd, 2);
|
||||
while(<$fh>) {
|
||||
print &html_escape($_);
|
||||
}
|
||||
close($fh);
|
||||
my $cmd = &get_create_filesystem_command( $disk, $slice, $part, $newfs );
|
||||
&additional_log( 'exec', undef, $cmd );
|
||||
my $out = &backquote_command( $cmd . " 2>&1" );
|
||||
foreach my $line ( split( /\n/, $out ) ) {
|
||||
$line =~ s/[^\x09\x0A\x0D\x20-\x7E]//g;
|
||||
print &html_escape($line) . "\n";
|
||||
}
|
||||
print "</pre>";
|
||||
if ($?) {
|
||||
print $text{'newfs_failed'},"<p>\n";
|
||||
}
|
||||
my $rc = $? >> 8;
|
||||
if ($rc) {
|
||||
print $text{'newfs_failed'}, "<p>\n";
|
||||
}
|
||||
else {
|
||||
print $text{'newfs_done'},"<p>\n";
|
||||
&webmin_log("newfs", $in{'part'} ne '' ? "part" : "object",
|
||||
$object->{'device'}, $object);
|
||||
}
|
||||
print $text{'newfs_done'}, "<p>\n";
|
||||
&webmin_log( "newfs", $in{'part'} ne '' ? "part" : "object",
|
||||
$object->{'device'}, $object );
|
||||
|
||||
if ($in{'part'} ne '') {
|
||||
&ui_print_footer("edit_part.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}&part=$in{'part'}",
|
||||
$text{'part_return'});
|
||||
}
|
||||
# Verify filesystem signature if possible
|
||||
if ( has_command('fstyp') ) {
|
||||
my $fstyp_out = &backquote_command(
|
||||
"fstyp " . quote_path( $object->{'device'} ) . " 2>&1" );
|
||||
$fstyp_out =~ s/[\r\n]+$//;
|
||||
if ($fstyp_out) {
|
||||
print "<pre>\n";
|
||||
print &html_escape($fstyp_out) . "\n";
|
||||
print "</pre>\n";
|
||||
}
|
||||
else {
|
||||
print
|
||||
"<b>Warning:</b> fstyp did not detect a filesystem on this device.<p>\n";
|
||||
}
|
||||
}
|
||||
|
||||
# If a label was provided, set the partition label (GPT slice or BSD sub-partition)
|
||||
if ( !$in{'label_def'} && defined $in{'label'} && length $in{'label'} ) {
|
||||
my $errlbl = set_partition_label(
|
||||
disk => $disk,
|
||||
slice => $slice,
|
||||
part => ( $in{'part'} ne '' ? $part : undef ),
|
||||
label => $in{'label'}
|
||||
);
|
||||
if ($errlbl) {
|
||||
print "Warning: failed to set partition label: \n"
|
||||
. &html_escape($errlbl) . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $in{'part'} ne '' ) {
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
my $url_part = &urlize( $in{'part'} );
|
||||
&ui_print_footer(
|
||||
"edit_part.cgi?device=$url_device&slice=$url_slice&part=$url_part",
|
||||
$text{'part_return'}
|
||||
);
|
||||
}
|
||||
else {
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
}
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&ui_print_footer(
|
||||
"edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
}
|
||||
|
||||
@@ -6,56 +6,492 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
|
||||
# Get the disk and slice
|
||||
# Validate input parameters to prevent command injection
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or &error("Invalid device name");
|
||||
$in{'device'} !~ /\.\./ or &error("Invalid device name");
|
||||
$in{'slice'} =~ /^\d+$/ or &error("Invalid slice number") if $in{'slice'};
|
||||
$in{'part'} =~ /^[a-z]$/ or &error("Invalid partition letter") if $in{'part'};
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
my $object;
|
||||
if ($in{'part'} ne '') {
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} }
|
||||
@{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
$object = $part;
|
||||
}
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
my $url_part = &urlize( $in{'part'} );
|
||||
|
||||
if ( $in{'part'} ne '' ) {
|
||||
$in{'part'} =~ /^[a-z]$/ or &error("Invalid partition letter");
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} } @{ $slice->{'parts'} };
|
||||
$part || &error( $text{'part_egone'} );
|
||||
$object = $part;
|
||||
}
|
||||
else {
|
||||
$object = $slice;
|
||||
$object = $slice;
|
||||
}
|
||||
|
||||
# If this device is part of a ZFS pool, offer dataset creation instead
|
||||
my $zdev = get_zfs_device_info($object);
|
||||
if ($zdev) {
|
||||
&ui_print_header( $object->{'desc'}, $text{'newfs_title'}, "" );
|
||||
|
||||
# Parent pool summary (best effort)
|
||||
my $parent = $zdev->{'pool'};
|
||||
my $parent_q = quote_path($parent);
|
||||
my $zlist = &backquote_command(
|
||||
"zfs list -H -o name,used,avail,refer,mountpoint $parent_q 2>/dev/null"
|
||||
);
|
||||
if ($zlist) {
|
||||
chomp($zlist);
|
||||
my @cols = split( /\t/, $zlist );
|
||||
if ( @cols >= 5 ) {
|
||||
print "<b>$text{'newfs_zfs_parent'}</b>\n";
|
||||
print ui_columns_start(
|
||||
[
|
||||
$text{'newfs_zfs_fs'}, $text{'newfs_zfs_used'},
|
||||
$text{'newfs_zfs_avail'}, $text{'newfs_zfs_refer'},
|
||||
$text{'newfs_zfs_mount'}
|
||||
]
|
||||
);
|
||||
print ui_columns_row( [ map { html_escape($_) } @cols[ 0 .. 4 ] ] );
|
||||
print ui_columns_end();
|
||||
print "<br/>\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Existing filesystems under this pool
|
||||
my $zlist_fs = &backquote_command(
|
||||
"zfs list -H -r -t filesystem -o name,used,avail,refer,mountpoint $parent_q 2>/dev/null"
|
||||
);
|
||||
if ($zlist_fs) {
|
||||
my @lines = split( /\n/, $zlist_fs );
|
||||
if ( @lines && $lines[0] =~ /^\Q$parent\E(\t|$)/ ) {
|
||||
shift @lines;
|
||||
}
|
||||
if (@lines) {
|
||||
print "<b>$text{'newfs_zfs_existing'}</b>\n";
|
||||
print ui_columns_start(
|
||||
[
|
||||
$text{'newfs_zfs_fs'}, $text{'newfs_zfs_used'},
|
||||
$text{'newfs_zfs_avail'}, $text{'newfs_zfs_refer'},
|
||||
$text{'newfs_zfs_mount'}
|
||||
]
|
||||
);
|
||||
foreach my $ln (@lines) {
|
||||
my @cols = split( /\t/, $ln );
|
||||
next unless @cols >= 5;
|
||||
print ui_columns_row(
|
||||
[ map { html_escape($_) } @cols[ 0 .. 4 ] ] );
|
||||
}
|
||||
print ui_columns_end();
|
||||
print "<br/>\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Existing volumes under this pool
|
||||
my $zlist_vol = &backquote_command(
|
||||
"zfs list -H -r -t volume -o name,used,avail,refer,volsize $parent_q 2>/dev/null"
|
||||
);
|
||||
if ($zlist_vol) {
|
||||
my @lines = split( /\n/, $zlist_vol );
|
||||
if ( @lines && $lines[0] =~ /^\Q$parent\E(\t|$)/ ) {
|
||||
shift @lines;
|
||||
}
|
||||
if (@lines) {
|
||||
print "<b>$text{'newfs_zvol_existing'}</b>\n";
|
||||
print ui_columns_start(
|
||||
[
|
||||
$text{'newfs_zvol_vol'}, $text{'newfs_zfs_used'},
|
||||
$text{'newfs_zfs_avail'}, $text{'newfs_zfs_refer'},
|
||||
$text{'newfs_zvol_volsize'}
|
||||
]
|
||||
);
|
||||
foreach my $ln (@lines) {
|
||||
my @cols = split( /\t/, $ln );
|
||||
next unless @cols >= 5;
|
||||
print ui_columns_row(
|
||||
[ map { html_escape($_) } @cols[ 0 .. 4 ] ] );
|
||||
}
|
||||
print ui_columns_end();
|
||||
print "<br/>\n";
|
||||
}
|
||||
}
|
||||
|
||||
my %fs_descriptions = (
|
||||
'recordsize' => {
|
||||
'128K' => '128K (General/Default)',
|
||||
'1M' => '1M (Media/Large files)',
|
||||
'4M' => '4M',
|
||||
'16K' => '16K (Database)',
|
||||
'4K' => '4K (VM)'
|
||||
},
|
||||
'compression' => {
|
||||
'lz4' => 'lz4 (Recommended)',
|
||||
'off' => 'off (None)',
|
||||
'gzip' => 'gzip (High compression)'
|
||||
},
|
||||
'atime' => {
|
||||
'off' => 'off (Performance)',
|
||||
'on' => 'on (Record access time)'
|
||||
},
|
||||
'sync' => {
|
||||
'disabled' => 'disabled (Performance)',
|
||||
'standard' => 'standard (Safety)',
|
||||
'always' => 'always (Maximum Safety)'
|
||||
},
|
||||
'acltype' => {
|
||||
'nfsv4' => 'nfsv4 (ZFS Default)',
|
||||
'posixacl' => 'posixacl (Linux Default)'
|
||||
},
|
||||
'aclinherit' => {
|
||||
'passthrough' => 'passthrough (SMB Recommended)',
|
||||
'restricted' => 'restricted (ZFS Default)'
|
||||
},
|
||||
'aclmode' => {
|
||||
'passthrough' => 'passthrough (SMB Recommended)',
|
||||
'discard' => 'discard (ZFS Default)'
|
||||
}
|
||||
);
|
||||
my @fs_order = (
|
||||
'recordsize', 'compression', 'atime', 'sync',
|
||||
'exec', 'canmount', 'acltype', 'aclinherit',
|
||||
'aclmode', 'xattr'
|
||||
);
|
||||
my %fs_defaults = (
|
||||
'recordsize' => '128K',
|
||||
'compression' => 'lz4',
|
||||
'atime' => 'off',
|
||||
'sync' => 'default',
|
||||
'acltype' => 'nfsv4',
|
||||
'aclinherit' => 'passthrough',
|
||||
'aclmode' => 'passthrough',
|
||||
'canmount' => 'on',
|
||||
'exec' => 'on',
|
||||
'xattr' => 'sa',
|
||||
);
|
||||
my %fs_opts = (
|
||||
'recordsize' =>
|
||||
'512, 1K, 2K, 4K, 8K, 16K, 32K, 64K, 128K, 256K, 512K, 1M',
|
||||
'compression' => 'on, off, lz4, gzip',
|
||||
'atime' => 'on, off',
|
||||
'sync' => 'standard, always, disabled',
|
||||
'exec' => 'on, off',
|
||||
'canmount' => 'on, off, noauto',
|
||||
'acltype' => 'nfsv4, posixacl',
|
||||
'aclinherit' =>
|
||||
'discard, noallow, restricted, passthrough, passthrough-x',
|
||||
'aclmode' => 'discard, groupmask, passthrough',
|
||||
'xattr' => 'on, off, sa',
|
||||
);
|
||||
my %acl_tooltips = (
|
||||
'acltype' => $text{'newfs_zfs_acltype_desc'},
|
||||
'aclinherit' => $text{'newfs_zfs_aclinherit_desc'},
|
||||
'aclmode' => $text{'newfs_zfs_aclmode_desc'},
|
||||
);
|
||||
|
||||
my $base_url =
|
||||
"newfs_form.cgi?device="
|
||||
. $url_device
|
||||
. "&slice=$url_slice";
|
||||
$base_url .= "&part=$url_part" if ( $in{'part'} ne '' );
|
||||
my @tabs = (
|
||||
[ "zfs", $text{'newfs_zfs_tab_fs'}, $base_url . "&mode=zfs" ],
|
||||
[ "zvol", $text{'newfs_zfs_tab_vol'}, $base_url . "&mode=zvol" ],
|
||||
);
|
||||
print &ui_tabs_start( \@tabs, "mode", $in{'mode'} || $tabs[0]->[0], 1 );
|
||||
print &ui_tabs_start_tab( "mode", "zfs" );
|
||||
|
||||
print &ui_form_start( "zfs_create.cgi", "post", undef,
|
||||
"onsubmit='return validateFsForm(this)'" );
|
||||
print &ui_hidden( "device", $in{'device'} );
|
||||
print &ui_hidden( "slice", $in{'slice'} );
|
||||
print &ui_hidden( "part", $in{'part'} );
|
||||
print &ui_hidden( "parent", $parent );
|
||||
|
||||
print &ui_table_start( $text{'newfs_zfs_header'}, 'width=100%', 2 );
|
||||
print &ui_table_row( $text{'newfs_zfs_name'},
|
||||
html_escape($parent) . "/" . &ui_textbox( "zfs", undef, 24 ) );
|
||||
print &ui_table_row( $text{'newfs_zfs_mountpoint'},
|
||||
&ui_filebox( 'mountpoint', '', 25, undef, undef, 1 ) . " ("
|
||||
. $text{'newfs_zfs_mount_blank'}
|
||||
. ")" );
|
||||
print &ui_table_end();
|
||||
|
||||
print &ui_table_start( $text{'newfs_zfs_opts'}, "width=100%", undef );
|
||||
foreach my $key (@fs_order) {
|
||||
my @options;
|
||||
push( @options, [ 'default', 'default' ] );
|
||||
foreach my $opt ( split( ", ", $fs_opts{$key} ) ) {
|
||||
my $label = $fs_descriptions{$key}{$opt} || $opt;
|
||||
push( @options, [ $opt, $label ] );
|
||||
}
|
||||
my $default_val = $fs_defaults{$key} || 'default';
|
||||
my $help =
|
||||
$acl_tooltips{$key}
|
||||
? "<br><small><i>$acl_tooltips{$key}</i></small>"
|
||||
: "";
|
||||
my $selected = defined( $in{$key} ) ? $in{$key} : $default_val;
|
||||
print ui_table_row( $key . ': ',
|
||||
ui_select( $key, $selected, \@options, 1, 0, 1 ) . $help );
|
||||
}
|
||||
my $add_inherit_default =
|
||||
defined( $in{'add_inherit'} ) ? $in{'add_inherit'} : 1;
|
||||
print ui_table_row(
|
||||
$text{'newfs_zfs_aclflags'},
|
||||
ui_checkbox(
|
||||
'add_inherit', 1,
|
||||
$text{'newfs_zfs_aclflags_desc'}, $add_inherit_default
|
||||
)
|
||||
);
|
||||
print &ui_table_end();
|
||||
print &ui_form_end( [ [ undef, $text{'create'} ] ] );
|
||||
|
||||
print &ui_tabs_end_tab( "mode", "zfs" );
|
||||
|
||||
# ZVOL tab
|
||||
print &ui_tabs_start_tab( "mode", "zvol" );
|
||||
my %zvol_defaults = (
|
||||
'volblocksize' => '16K',
|
||||
'compression' => 'lz4',
|
||||
'sync' => 'default',
|
||||
'logbias' => 'latency',
|
||||
'primarycache' => 'all',
|
||||
'secondarycache' => 'all',
|
||||
);
|
||||
my %zvol_opts = (
|
||||
'volblocksize' => '512, 1K, 2K, 4K, 8K, 16K, 32K, 64K, 128K',
|
||||
'compression' => 'on, off, lz4, gzip',
|
||||
'sync' => 'standard, always, disabled',
|
||||
'logbias' => 'latency, throughput',
|
||||
'primarycache' => 'all, metadata, none',
|
||||
'secondarycache' => 'all, metadata, none',
|
||||
);
|
||||
my %zvol_desc = (
|
||||
'volblocksize' => {
|
||||
'512' => '512B',
|
||||
'1K' => '1K',
|
||||
'2K' => '2K',
|
||||
'4K' => '4K (Swap)',
|
||||
'8K' => '8K (Databases)',
|
||||
'16K' => '16K (VM/Default)',
|
||||
'64K' => '64K (Backups)',
|
||||
},
|
||||
'logbias' => {
|
||||
'latency' => 'latency (databases, NFS sync)',
|
||||
'throughput' => 'throughput (VM, media/backups)',
|
||||
},
|
||||
'primarycache' => {
|
||||
'all' => 'all (filesystems)',
|
||||
'metadata' => 'metadata (VM, iSCSI)',
|
||||
'none' => 'none (Swap)',
|
||||
},
|
||||
'secondarycache' => {
|
||||
'all' => 'all (general filesystems)',
|
||||
'metadata' => 'metadata (VM, databases)',
|
||||
'none' => 'none (media)',
|
||||
},
|
||||
);
|
||||
|
||||
print &ui_form_start( "zvol_create.cgi", "post", undef,
|
||||
"onsubmit='return validateZvolForm(this)'" );
|
||||
print &ui_hidden( "device", $in{'device'} );
|
||||
print &ui_hidden( "slice", $in{'slice'} );
|
||||
print &ui_hidden( "part", $in{'part'} );
|
||||
print &ui_hidden( "parent", $parent );
|
||||
print &ui_table_start( $text{'newfs_zvol_header'}, 'width=100%', 2 );
|
||||
print &ui_table_row( $text{'newfs_zvol_name'},
|
||||
html_escape($parent) . "/" . &ui_textbox( "zvol", undef, 24 ) );
|
||||
print &ui_table_row(
|
||||
$text{'newfs_zvol_size'},
|
||||
&ui_textbox(
|
||||
'size', undef, 20, undef, undef, "oninput='updateRefres()'"
|
||||
)
|
||||
);
|
||||
print &ui_table_end();
|
||||
|
||||
print &ui_table_start( $text{'newfs_zvol_opts'}, "width=100%", undef );
|
||||
foreach my $key (
|
||||
qw(volblocksize compression sync logbias primarycache secondarycache))
|
||||
{
|
||||
my @options;
|
||||
push( @options, [ 'default', 'default' ] );
|
||||
foreach my $opt ( split( ", ", $zvol_opts{$key} ) ) {
|
||||
my $label = $zvol_desc{$key}{$opt} || $opt;
|
||||
push( @options, [ $opt, $label ] );
|
||||
}
|
||||
my $default_val = $zvol_defaults{$key} || 'default';
|
||||
my $selected = defined( $in{$key} ) ? $in{$key} : $default_val;
|
||||
print ui_table_row( $key . ': ',
|
||||
ui_select( $key, $selected, \@options, 1, 0, 1 ) );
|
||||
}
|
||||
my $sparse_default = defined( $in{'sparse'} ) ? $in{'sparse'} : 1;
|
||||
print ui_table_row( $text{'newfs_zvol_sparse'},
|
||||
ui_yesno_radio( 'sparse', $sparse_default )
|
||||
. " <small>"
|
||||
. $text{'newfs_zvol_sparse_desc'}
|
||||
. "</small>" );
|
||||
print ui_table_row( $text{'newfs_zvol_refreservation'},
|
||||
ui_textbox( 'refreservation', 'none', 20 )
|
||||
. "<span id='refres_label'></span>" );
|
||||
print &ui_table_end();
|
||||
print &ui_form_end( [ [ undef, $text{'create'} ] ] );
|
||||
print &ui_tabs_end_tab( "mode", "zvol" );
|
||||
|
||||
print &ui_tabs_end(1);
|
||||
|
||||
print <<'EOF';
|
||||
<script type="text/javascript">
|
||||
function validateFsForm(form) {
|
||||
var name = form.zfs.value;
|
||||
var nameRegex = /^[a-zA-Z0-9_\-.:]+$/;
|
||||
if (!name || !nameRegex.test(name)) {
|
||||
alert("Invalid Name. Please use alphanumeric characters, -, _, ., or :");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function validateZvolForm(form) {
|
||||
var name = form.zvol.value;
|
||||
var nameRegex = /^[a-zA-Z0-9_\-.:]+$/;
|
||||
if (!name || !nameRegex.test(name)) {
|
||||
alert("Invalid Name. Please use alphanumeric characters, -, _, ., or :");
|
||||
return false;
|
||||
}
|
||||
if (name.indexOf("/") !== -1 || name.indexOf("@") !== -1 || name.indexOf("#") !== -1) {
|
||||
alert("Invalid Name. Do not include '/', '@' or '#'.");
|
||||
return false;
|
||||
}
|
||||
if (name.charAt(0) === "-" || name.charAt(0) === ".") {
|
||||
alert("Invalid Name. It cannot start with '-' or '.'.");
|
||||
return false;
|
||||
}
|
||||
if (name.indexOf("..") !== -1) {
|
||||
alert("Invalid Name. It cannot contain '..'.");
|
||||
return false;
|
||||
}
|
||||
var size = form.size.value;
|
||||
var regex = /^\d+(\.\d+)?[KMGTP]?$/i;
|
||||
if (!size || !regex.test(size) || parseFloat(size) <= 0) {
|
||||
alert("Invalid size format. Please use format like 10G, 500M, etc.");
|
||||
return false;
|
||||
}
|
||||
var ref = form.refreservation.value;
|
||||
if (ref && ref.toLowerCase() !== "none") {
|
||||
if (!regex.test(ref) || parseFloat(ref) <= 0) {
|
||||
alert("Invalid refreservation format. Please use format like 10G, 500M, etc.");
|
||||
return false;
|
||||
}
|
||||
// Compare sizes when possible
|
||||
function toBytes(v) {
|
||||
var m = v.match(/^(\d+(?:\.\d+)?)([KMGTP]?)$/i);
|
||||
if (!m) return null;
|
||||
var n = parseFloat(m[1]);
|
||||
var u = (m[2] || "").toUpperCase();
|
||||
var mult = 1;
|
||||
if (u === "K") mult = 1024;
|
||||
else if (u === "M") mult = 1024*1024;
|
||||
else if (u === "G") mult = 1024*1024*1024;
|
||||
else if (u === "T") mult = 1024*1024*1024*1024;
|
||||
else if (u === "P") mult = 1024*1024*1024*1024*1024;
|
||||
return n * mult;
|
||||
}
|
||||
var sizeB = toBytes(size);
|
||||
var refB = toBytes(ref);
|
||||
if (sizeB && refB && refB > sizeB) {
|
||||
alert("Refreservation cannot be larger than the volume size.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function updateRefres() {
|
||||
var sparseList = document.getElementsByName("sparse");
|
||||
var refres = document.getElementsByName("refreservation")[0];
|
||||
var size = document.getElementsByName("size")[0];
|
||||
var label = document.getElementById("refres_label");
|
||||
if (!sparseList || sparseList.length === 0 || !refres || !size || !label) return;
|
||||
var sparseOn = false;
|
||||
for (var i = 0; i < sparseList.length; i++) {
|
||||
if (sparseList[i].checked && sparseList[i].value === "1") {
|
||||
sparseOn = true;
|
||||
}
|
||||
}
|
||||
if (sparseOn) {
|
||||
refres.disabled = false;
|
||||
var sVal = size.value ? size.value : "Size";
|
||||
label.innerHTML = " (" + sVal + " max, default: none)";
|
||||
} else {
|
||||
refres.disabled = true;
|
||||
label.innerHTML = " (" + "Volume is thick provisioned)";
|
||||
}
|
||||
}
|
||||
function bindSparseRadios() {
|
||||
var sparseList = document.getElementsByName("sparse");
|
||||
if (!sparseList) return;
|
||||
for (var i = 0; i < sparseList.length; i++) {
|
||||
sparseList[i].onclick = updateRefres;
|
||||
}
|
||||
}
|
||||
window.onload = function() { updateRefres(); bindSparseRadios(); };
|
||||
</script>
|
||||
EOF
|
||||
|
||||
&ui_print_header($object->{'desc'}, $text{'newfs_title'}, "");
|
||||
if ( $in{'part'} ne '' ) {
|
||||
&ui_print_footer(
|
||||
"edit_part.cgi?device=$url_device&slice=$url_slice&part=$url_part",
|
||||
$text{'part_return'}
|
||||
);
|
||||
}
|
||||
else {
|
||||
&ui_print_footer(
|
||||
"edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
print &ui_form_start("newfs.cgi", "post");
|
||||
print &ui_hidden("device", $in{'device'});
|
||||
print &ui_hidden("slice", $in{'slice'});
|
||||
print &ui_hidden("part", $in{'part'});
|
||||
print &ui_table_start($text{'newfs_header'}, undef, 2);
|
||||
# Default: UFS newfs form
|
||||
&ui_print_header( $object->{'desc'}, $text{'newfs_title'}, "" );
|
||||
|
||||
print &ui_table_row($text{'part_device'},
|
||||
"<tt>$object->{'device'}</tt>");
|
||||
my $confirm_msg = $text{'confirm_overwrite'}
|
||||
|| 'You will destroy/overwrite existing data structures. Continue?';
|
||||
my $confirm_js = $confirm_msg;
|
||||
$confirm_js =~ s/\\/\\\\/g;
|
||||
$confirm_js =~ s/'/\\'/g;
|
||||
$confirm_js =~ s/\r?\n/\\n/g;
|
||||
print &ui_form_start( "newfs.cgi", "post", undef,
|
||||
"onsubmit=\"return confirm('$confirm_js')\"" );
|
||||
print &ui_hidden( "device", $in{'device'} );
|
||||
print &ui_hidden( "slice", $in{'slice'} );
|
||||
print &ui_hidden( "part", $in{'part'} );
|
||||
print &ui_table_start( $text{'newfs_header'}, undef, 2 );
|
||||
|
||||
print &ui_table_row($text{'newfs_free'},
|
||||
&ui_opt_textbox("free", undef, 4, $text{'newfs_deffree'})."%");
|
||||
print &ui_table_row( $text{'part_device'}, "<tt>$object->{'device'}</tt>" );
|
||||
|
||||
print &ui_table_row($text{'newfs_trim'},
|
||||
&ui_yesno_radio("trim", 0));
|
||||
print &ui_table_row( $text{'newfs_free'},
|
||||
&ui_opt_textbox( "free", undef, 4, $text{'newfs_deffree'} ) . "%" );
|
||||
|
||||
print &ui_table_row($text{'newfs_label'},
|
||||
&ui_opt_textbox("label", undef, 20, $text{'newfs_none'}));
|
||||
print &ui_table_row( $text{'newfs_trim'}, &ui_yesno_radio( "trim", 0 ) );
|
||||
|
||||
print &ui_table_row( $text{'newfs_label'},
|
||||
&ui_opt_textbox( "label", undef, 20, $text{'newfs_none'} ) );
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'newfs_create'} ] ]);
|
||||
print &ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
|
||||
if ($in{'part'} ne '') {
|
||||
&ui_print_footer("edit_part.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}&part=$in{'part'}",
|
||||
$text{'part_return'});
|
||||
}
|
||||
if ( $in{'part'} ne '' ) {
|
||||
&ui_print_footer(
|
||||
"edit_part.cgi?device=$url_device&slice=$url_slice&part=$url_part",
|
||||
$text{'part_return'}
|
||||
);
|
||||
}
|
||||
else {
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&".
|
||||
"slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
}
|
||||
&ui_print_footer(
|
||||
"edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
}
|
||||
|
||||
@@ -6,54 +6,109 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'slice'} =~ /^\d+$/ or &error( $text{'slice_egone'} );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
|
||||
&ui_print_header($slice->{'desc'}, $text{'npart_title'}, "");
|
||||
&ui_print_header( $slice->{'desc'}, $text{'npart_title'}, "" );
|
||||
|
||||
print &ui_form_start("create_part.cgi", "post");
|
||||
print &ui_hidden("device", $in{'device'});
|
||||
print &ui_hidden("slice", $in{'slice'});
|
||||
print &ui_table_start($text{'npart_header'}, undef, 2);
|
||||
print &ui_form_start( "create_part.cgi", "post" );
|
||||
print &ui_hidden( "device", $in{'device'} );
|
||||
print &ui_hidden( "slice", $in{'slice'} );
|
||||
print &ui_table_start( $text{'npart_header'}, undef, 2 );
|
||||
|
||||
# Partition number (first free)
|
||||
my %used = map { $_->{'letter'}, $_ } @{$slice->{'parts'}};
|
||||
# Partition number (first free, skipping 'c' which is reserved for the whole slice)
|
||||
my %used = map { $_->{'letter'}, $_ } @{ $slice->{'parts'} };
|
||||
$used{'c'} = 1; # Reserve 'c' for the whole slice (BSD convention)
|
||||
my $l = 'a';
|
||||
while($used{$l}) {
|
||||
$l++;
|
||||
}
|
||||
print &ui_table_row($text{'npart_letter'},
|
||||
&ui_textbox("letter", $l, 4));
|
||||
while ( $used{$l} ) {
|
||||
$l++;
|
||||
}
|
||||
print &ui_table_row( $text{'npart_letter'},
|
||||
&ui_textbox( "letter", $l, 4 ) . " <i>("
|
||||
. $text{'npart_creserved'}
|
||||
. ")</i>" );
|
||||
|
||||
# Slice size in blocks
|
||||
print &ui_table_row($text{'npart_diskblocks'},
|
||||
$slice->{'blocks'});
|
||||
print &ui_table_row( $text{'npart_diskblocks'}, $slice->{'blocks'} );
|
||||
|
||||
# Start and end blocks (defaults to last part)
|
||||
my ($start, $end) = (0, $slice->{'blocks'});
|
||||
foreach my $p (sort { $a->{'startblock'} cmp $b->{'startblock'} }
|
||||
@{$slice->{'parts'}}) {
|
||||
$start = $p->{'startblock'} + $p->{'blocks'} + 1;
|
||||
}
|
||||
print &ui_table_row($text{'nslice_start'},
|
||||
&ui_textbox("start", $start, 10));
|
||||
print &ui_table_row($text{'nslice_end'},
|
||||
&ui_textbox("end", $end, 10));
|
||||
# Start and end blocks for BSD partitions are SLICE-RELATIVE (not disk-absolute)
|
||||
# Start at 0 (or after last partition), end at slice size - 1
|
||||
my ( $start, $end ) = ( 0, $slice->{'blocks'} - 1 );
|
||||
foreach my $p ( sort { $a->{'startblock'} <=> $b->{'startblock'} }
|
||||
@{ $slice->{'parts'} } )
|
||||
{
|
||||
# Partitions are already stored as slice-relative
|
||||
$start = $p->{'startblock'} + $p->{'blocks'};
|
||||
}
|
||||
if ( defined $in{'start'} && $in{'start'} =~ /^\d+$/ ) {
|
||||
$start = $in{'start'};
|
||||
}
|
||||
if ( defined $in{'end'} && $in{'end'} =~ /^\d+$/ ) { $end = $in{'end'}; }
|
||||
print &ui_table_row( $text{'nslice_start'} . " " . $text{'npart_slicerel'},
|
||||
&ui_textbox( "start", $start, 10 ) );
|
||||
print &ui_table_row( $text{'nslice_end'} . " " . $text{'npart_slicerel'},
|
||||
&ui_textbox( "end", $end, 10 ) );
|
||||
|
||||
# Partition type
|
||||
print &ui_table_row($text{'npart_type'},
|
||||
&ui_select("type", '4.2BSD',
|
||||
[ &list_partition_types() ]));
|
||||
# For BSD-on-MBR inner label partitions, offer FreeBSD partition types
|
||||
my $scheme = 'BSD';
|
||||
my $default_ptype = 'freebsd-ufs';
|
||||
print &ui_table_row( $text{'npart_type'},
|
||||
&ui_select( "type", $default_ptype, [ list_partition_types($scheme) ] ) );
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'create'} ] ]);
|
||||
print &ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
|
||||
&ui_print_footer("edit_slice.cgi?device=$in{'device'}&slice=$in{'slice'}",
|
||||
$text{'slice_return'});
|
||||
# Existing partitions summary
|
||||
if ( @{ $slice->{'parts'} || [] } ) {
|
||||
my $zfs = get_all_zfs_info();
|
||||
print &ui_hr();
|
||||
print &ui_columns_start(
|
||||
[
|
||||
$text{'slice_letter'}, $text{'slice_type'}, $text{'slice_start'},
|
||||
$text{'slice_end'}, $text{'slice_size'}, $text{'slice_use'},
|
||||
$text{'slice_role'}
|
||||
],
|
||||
$text{'epart_existing'}
|
||||
);
|
||||
foreach my $p ( sort { $a->{'startblock'} <=> $b->{'startblock'} }
|
||||
@{ $slice->{'parts'} } )
|
||||
{
|
||||
my $ptype = get_type_description( $p->{'type'} ) || $p->{'type'};
|
||||
my @stp = fdisk::device_status( $p->{'device'} );
|
||||
my $usep =
|
||||
$zfs->{ $p->{'device'} }
|
||||
|| fdisk::device_status_link(@stp)
|
||||
|| $text{'part_nouse'};
|
||||
my $rolep = get_partition_role($p);
|
||||
my $pb = bytes_from_blocks( $p->{'device'}, $p->{'blocks'} );
|
||||
print &ui_columns_row(
|
||||
[
|
||||
uc( $p->{'letter'} ),
|
||||
&html_escape($ptype),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
( $pb ? safe_nice_size($pb) : '-' ),
|
||||
&html_escape($usep),
|
||||
&html_escape($rolep)
|
||||
]
|
||||
);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
}
|
||||
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&ui_print_footer( "edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
|
||||
@@ -6,30 +6,39 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
&error_setup($text{'part_err'});
|
||||
&error_setup( $text{'part_err'} );
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'slice'} =~ /^\d+$/ or &error( $text{'slice_egone'} );
|
||||
$in{'part'} =~ /^[a-z]$/ or &error( $text{'part_egone'} );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} } @{$slice->{'parts'}};
|
||||
$part || &error($text{'part_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
my ($part) = grep { $_->{'letter'} eq $in{'part'} } @{ $slice->{'parts'} };
|
||||
$part || &error( $text{'part_egone'} );
|
||||
|
||||
# Check if in use
|
||||
my @st = &fdisk::device_status($part->{'device'});
|
||||
my @st = &fdisk::device_status( $part->{'device'} );
|
||||
my $use = &fdisk::device_status_link(@st);
|
||||
if (@st && $st[2]) {
|
||||
&error(&text('part_esave', $use));
|
||||
}
|
||||
if ( @st && $st[2] ) {
|
||||
&error( &text( 'part_esave', &html_escape($use) ) );
|
||||
}
|
||||
|
||||
# Make the change
|
||||
$in{'type'} =~ /^[a-zA-Z0-9._-]+$/
|
||||
or &error( $text{'part_etype'} || 'Invalid partition type' );
|
||||
$part->{'type'} = $in{'type'};
|
||||
my $err = &save_partition($disk, $slice, $part);
|
||||
&error($err) if ($err);
|
||||
my $err = &save_partition( $disk, $slice, $part );
|
||||
&error( &html_escape($err) ) if ($err);
|
||||
|
||||
&webmin_log("modify", "part", $part->{'device'}, $part);
|
||||
&redirect("edit_slice.cgi?device=$in{'device'}&slice=$in{'slice'}");
|
||||
&webmin_log( "modify", "part", $part->{'device'}, $part );
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&redirect("edit_slice.cgi?device=$url_device&slice=$url_slice");
|
||||
|
||||
@@ -6,25 +6,53 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
&error_setup($text{'slice_err'});
|
||||
&error_setup( $text{'slice_err'} );
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'slice'} =~ /^\d+$/ or &error( $text{'slice_egone'} );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{$disk->{'slices'}};
|
||||
$slice || &error($text{'slice_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
|
||||
# Apply changes
|
||||
my $oldslice = { %$slice };
|
||||
$slice->{'type'} = $in{'type'};
|
||||
if (!$slice->{'active'}) {
|
||||
$slice->{'active'} = $in{'active'};
|
||||
}
|
||||
my $err = &modify_slice($disk, $oldslice, $slice);
|
||||
&error($err) if ($err);
|
||||
my $oldslice = {%$slice};
|
||||
$in{'type'} =~ /^[a-zA-Z0-9._-]+$/
|
||||
or &error( $text{'nslice_etype'} || 'Invalid slice type' );
|
||||
$slice->{'type'} = $in{'type'};
|
||||
$slice->{'active'} = $in{'active'} if ( defined $in{'active'} );
|
||||
|
||||
&webmin_log("modify", "slice", $slice->{'device'}, $slice);
|
||||
&redirect("edit_disk.cgi?device=$in{'device'}");
|
||||
# Apply active flag for MBR disks via gpart set/unset when it changed
|
||||
my $base = $disk->{'device'};
|
||||
$base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
if ( is_using_gpart() && $ds && $ds->{'scheme'} && $ds->{'scheme'} !~ /GPT/i ) {
|
||||
my $idx = _safe_uint( slice_number($slice) );
|
||||
&error( $text{'slice_egone'} ) unless defined $idx;
|
||||
if ( defined $oldslice->{'active'}
|
||||
&& defined $slice->{'active'}
|
||||
&& $oldslice->{'active'} != $slice->{'active'} )
|
||||
{
|
||||
my $cmd =
|
||||
$slice->{'active'}
|
||||
? "gpart set -a active -i $idx " . quote_path($base)
|
||||
: "gpart unset -a active -i $idx " . quote_path($base);
|
||||
my $out = backquote_command("$cmd 2>&1");
|
||||
if ( $? != 0 ) {
|
||||
&error( "Failed to change active flag: " . &html_escape($out) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $err = &modify_slice( $disk, $oldslice, $slice );
|
||||
&error( &html_escape($err) ) if ($err);
|
||||
|
||||
&webmin_log( "modify", "slice", $slice->{'device'}, $slice );
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
&redirect("edit_disk.cgi?device=$url_device");
|
||||
|
||||
49
bsdfdisk/save_slice_label.cgi
Normal file
49
bsdfdisk/save_slice_label.cgi
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Save slice label (GPT label or glabel)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
&error_setup( $text{'slice_label_err'} );
|
||||
|
||||
# Validate input parameters to prevent command injection
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or &error("Invalid device name");
|
||||
$in{'device'} !~ /\.\./ or &error("Invalid device name");
|
||||
$in{'slice'} =~ /^\d+$/ or &error("Invalid slice number");
|
||||
|
||||
my $label = defined $in{'label'} ? $in{'label'} : '';
|
||||
$label =~ s/^\s+|\s+$//g;
|
||||
$label ne '' || &error( $text{'slice_label_empty'} );
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
|
||||
# Ensure we can set labels on this scheme
|
||||
my $base_device = $disk->{'device'};
|
||||
$base_device =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base_device);
|
||||
if ( !$ds || !$ds->{'scheme'} || $ds->{'scheme'} !~ /GPT/i ) {
|
||||
if ( !has_command('glabel') ) {
|
||||
&error( $text{'slice_label_noglabel'} );
|
||||
}
|
||||
}
|
||||
|
||||
my $err = set_partition_label(
|
||||
disk => $disk,
|
||||
slice => $slice,
|
||||
label => $label,
|
||||
);
|
||||
&error( &html_escape($err) ) if ($err);
|
||||
|
||||
&webmin_log( "label", "slice", $slice->{'device'}, { 'label' => $label } );
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&redirect("edit_slice.cgi?device=$url_device&slice=$url_slice");
|
||||
@@ -6,57 +6,177 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
|
||||
# Get the disk
|
||||
my @disks = &list_disks_partitions();
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error($text{'disk_egone'});
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
|
||||
&ui_print_header($disk->{'desc'}, $text{'nslice_title'}, "");
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
|
||||
print &ui_form_start("create_slice.cgi", "post");
|
||||
print &ui_hidden("device", $in{'device'});
|
||||
print &ui_table_start($text{'nslice_header'}, undef, 2);
|
||||
&ui_print_header( $disk->{'desc'}, $text{'nslice_title'}, "" );
|
||||
|
||||
# Determine scheme for read-only behavior and note
|
||||
my $base_device = $disk->{'device'};
|
||||
$base_device =~ s{^/dev/}{};
|
||||
my $disk_structure = get_disk_structure($base_device);
|
||||
my $is_gpt =
|
||||
( is_using_gpart()
|
||||
&& $disk_structure
|
||||
&& ( $disk_structure->{'scheme'} || '' ) =~ /GPT/i );
|
||||
|
||||
# Check if there is any free space on the device
|
||||
my $has_free_space = 0;
|
||||
if ( $disk_structure && $disk_structure->{'entries'} ) {
|
||||
foreach my $entry ( @{ $disk_structure->{'entries'} } ) {
|
||||
if ( $entry->{'type'} eq 'free' && $entry->{'size'} > 0 ) {
|
||||
$has_free_space = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If no free space, show error and return
|
||||
if ( !$has_free_space ) {
|
||||
print "<p><b>$text{'nslice_enospace'}</b></p>\n";
|
||||
&ui_print_footer( "edit_disk.cgi?device=$url_device", $text{'disk_return'} );
|
||||
exit;
|
||||
}
|
||||
|
||||
print &ui_form_start( "create_slice.cgi", "post" );
|
||||
print &ui_hidden( "device", $in{'device'} );
|
||||
print &ui_table_start( $text{'nslice_header'}, undef, 2 );
|
||||
|
||||
# Slice number (first free)
|
||||
my %used = map { $_->{'number'}, $_ } @{$disk->{'slices'}};
|
||||
my $n = 1;
|
||||
while($used{$n}) {
|
||||
$n++;
|
||||
}
|
||||
print &ui_table_row($text{'nslice_number'},
|
||||
&ui_textbox("number", $n, 6));
|
||||
my %used = map { $_->{'number'}, $_ } @{ $disk->{'slices'} };
|
||||
my $n = 1;
|
||||
while ( $used{$n} ) {
|
||||
$n++;
|
||||
}
|
||||
my $num_field =
|
||||
$is_gpt
|
||||
? "<input type='text' name='number' value='"
|
||||
. &html_escape($n)
|
||||
. "' size='6' readonly> <span style='color:#666;font-style:italic'>"
|
||||
. $text{'nslice_autonext'}
|
||||
. "</span>"
|
||||
: &ui_textbox( "number", $n, 6 );
|
||||
print &ui_table_row( $text{'nslice_number'}, $num_field );
|
||||
|
||||
# Disk size in blocks
|
||||
print &ui_table_row($text{'nslice_diskblocks'},
|
||||
$disk->{'blocks'});
|
||||
# Disk size in blocks (prefer GPART total blocks)
|
||||
my $disk_blocks =
|
||||
( $disk_structure && $disk_structure->{'total_blocks'} )
|
||||
? $disk_structure->{'total_blocks'}
|
||||
: ( $disk->{'blocks'} || 0 );
|
||||
print &ui_table_row( $text{'nslice_diskblocks'}, $disk_blocks );
|
||||
|
||||
# Start and end blocks (defaults to last slice+1)
|
||||
my ($start, $end) = (63, $disk->{'blocks'});
|
||||
foreach my $s (sort { $a->{'startblock'} cmp $b->{'startblock'} }
|
||||
@{$disk->{'slices'}}) {
|
||||
$start = $s->{'startblock'} + $s->{'blocks'} + 1;
|
||||
}
|
||||
print &ui_table_row($text{'nslice_start'},
|
||||
&ui_textbox("start", $start, 10));
|
||||
print &ui_table_row($text{'nslice_end'},
|
||||
&ui_textbox("end", $end, 10));
|
||||
# Start and end blocks (defaults to last slice+1). Allow prefill from query.
|
||||
my ( $start, $end ) = ( 2048, $disk_blocks > 0 ? $disk_blocks - 1 : 0 );
|
||||
foreach my $s ( sort { $a->{'startblock'} <=> $b->{'startblock'} }
|
||||
@{ $disk->{'slices'} } )
|
||||
{
|
||||
$start = $s->{'startblock'} + $s->{'blocks'}; # leave 1 block (512B) gap
|
||||
}
|
||||
if ( defined $in{'start'} && $in{'start'} =~ /^\d+$/ ) {
|
||||
$start = $in{'start'};
|
||||
}
|
||||
if ( defined $in{'end'} && $in{'end'} =~ /^\d+$/ ) { $end = $in{'end'}; }
|
||||
print &ui_table_row( $text{'nslice_start'},
|
||||
&ui_textbox( "start", $start, 10 ) );
|
||||
print &ui_table_row( $text{'nslice_end'}, &ui_textbox( "end", $end, 10 ) );
|
||||
|
||||
# Slice type
|
||||
print &ui_table_row($text{'nslice_type'},
|
||||
&ui_select("type", 'a5',
|
||||
[ sort { $a->[1] cmp $b->[1] }
|
||||
map { [ $_, &fdisk::tag_name($_) ] }
|
||||
&fdisk::list_tags() ]));
|
||||
if ( is_using_gpart() ) {
|
||||
my $scheme =
|
||||
( $disk_structure && $disk_structure->{'scheme'} )
|
||||
? $disk_structure->{'scheme'}
|
||||
: 'GPT';
|
||||
my $default_stype = ( $scheme =~ /GPT/i ) ? 'freebsd-zfs' : 'freebsd';
|
||||
print &ui_table_row( $text{'nslice_type'},
|
||||
&ui_select( "type", $default_stype, [ list_partition_types($scheme) ] )
|
||||
);
|
||||
}
|
||||
else {
|
||||
print &ui_table_row(
|
||||
$text{'nslice_type'},
|
||||
&ui_select(
|
||||
"type", 'a5',
|
||||
[
|
||||
sort { $a->[1] cmp $b->[1] }
|
||||
map { [ $_, &fdisk::tag_name($_) ] } &fdisk::list_tags()
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
# Also create partition?
|
||||
print &ui_table_row($text{'nslice_makepart'},
|
||||
&ui_yesno_radio("makepart", 1));
|
||||
# Also create partition? (only for MBR slices with BSD disklabel support)
|
||||
if ( !$is_gpt ) {
|
||||
print &ui_table_row( $text{'slice_add'}, &ui_yesno_radio( "makepart", 1 ) );
|
||||
}
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'create'} ] ]);
|
||||
print &ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}",
|
||||
$text{'disk_return'});
|
||||
# Existing slices summary
|
||||
print &ui_hr();
|
||||
print &ui_subheading( $text{'nslice_existing_slices_label'} );
|
||||
print &ui_columns_start(
|
||||
[
|
||||
$text{'disk_no'}, $text{'disk_type'}, $text{'disk_start'},
|
||||
$text{'disk_end'}, $text{'disk_size'}
|
||||
],
|
||||
$text{'nslice_existing_header'}
|
||||
);
|
||||
foreach
|
||||
my $s ( sort { $a->{'number'} <=> $b->{'number'} } @{ $disk->{'slices'} } )
|
||||
{
|
||||
my $stype = get_type_description( $s->{'type'} ) || $s->{'type'};
|
||||
my $szb = bytes_from_blocks( $s->{'device'}, $s->{'blocks'} );
|
||||
my $sz = defined $szb ? safe_nice_size($szb) : '-';
|
||||
print &ui_columns_row(
|
||||
[
|
||||
$s->{'number'}, &html_escape($stype),
|
||||
$s->{'startblock'}, $s->{'startblock'} + $s->{'blocks'} - 1,
|
||||
$sz,
|
||||
]
|
||||
);
|
||||
}
|
||||
print &ui_columns_end();
|
||||
|
||||
# Existing partitions summary
|
||||
my @parts_rows;
|
||||
foreach
|
||||
my $s ( sort { $a->{'number'} <=> $b->{'number'} } @{ $disk->{'slices'} } )
|
||||
{
|
||||
next unless @{ $s->{'parts'} || [] };
|
||||
foreach my $p ( @{ $s->{'parts'} } ) {
|
||||
my $ptype = get_type_description( $p->{'type'} ) || $p->{'type'};
|
||||
my $pb = bytes_from_blocks( $p->{'device'}, $p->{'blocks'} );
|
||||
my $psz = defined $pb ? safe_nice_size($pb) : '-';
|
||||
push @parts_rows,
|
||||
[
|
||||
$s->{'number'}, uc( $p->{'letter'} ),
|
||||
&html_escape($ptype), $p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1, $psz
|
||||
];
|
||||
}
|
||||
}
|
||||
if (@parts_rows) {
|
||||
print &ui_subheading( $text{'nslice_existing_parts_label'} );
|
||||
print &ui_columns_start(
|
||||
[
|
||||
'Slice', $text{'slice_letter'}, $text{'slice_type'},
|
||||
$text{'slice_start'}, $text{'slice_end'}, $text{'slice_size'}
|
||||
],
|
||||
$text{'nslice_existing_parts'}
|
||||
);
|
||||
foreach my $row (@parts_rows) { print &ui_columns_row($row); }
|
||||
print &ui_columns_end();
|
||||
}
|
||||
|
||||
&ui_print_footer( "edit_disk.cgi?device=$url_device", $text{'disk_return'} );
|
||||
|
||||
110
bsdfdisk/smart.cgi
Executable file
110
bsdfdisk/smart.cgi
Executable file
@@ -0,0 +1,110 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show SMART status for a given device
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
|
||||
# Validate device param
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/
|
||||
or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
$in{'device'} !~ /\.\./ or &error( $text{'disk_edevice'} || 'Invalid device' );
|
||||
|
||||
# Check smartctl availability
|
||||
&has_command('smartctl')
|
||||
or &error( $text{'index_ecmd'}
|
||||
? &text( 'index_ecmd', 'smartctl' )
|
||||
: 'smartctl not available' );
|
||||
|
||||
my $device = $in{'device'};
|
||||
my $dev_html = &html_escape($device);
|
||||
|
||||
&ui_print_header( $dev_html, $text{'disk_smart'} || 'SMART Status', "" );
|
||||
|
||||
print "<div class='panel panel-default'>\n";
|
||||
print "<div class='panel-heading'><h3 class='panel-title'>SMART status for "
|
||||
. "<tt>$dev_html</tt></h3></div>\n";
|
||||
print "<div class='panel-body'>\n";
|
||||
|
||||
sub _smartctl_needs_type {
|
||||
my ($out) = @_;
|
||||
return ( $out =~ /Please specify device type with the -d option/i
|
||||
|| $out =~ /Unknown USB bridge/i );
|
||||
}
|
||||
|
||||
sub _smartctl_no_smart {
|
||||
my ($out) = @_;
|
||||
return ( $out =~ /Read Device Identity failed/i
|
||||
|| $out =~ /unsupported scsi opcode/i
|
||||
|| $out =~ /Device does not support SMART/i
|
||||
|| $out =~ /SMART support is:\s*Unavailable/i
|
||||
|| $out =~ /mandatory SMART command failed/i );
|
||||
}
|
||||
|
||||
my $cmd = "smartctl -a " . "e_path($device) . " 2>&1";
|
||||
my $out = &backquote_command($cmd);
|
||||
my $used_cmd = $cmd;
|
||||
my $note;
|
||||
|
||||
# If smartctl requests a device type or indicates SMART unsupported,
|
||||
# try common USB bridge options.
|
||||
if ( _smartctl_needs_type($out) || _smartctl_no_smart($out) ) {
|
||||
my @types = (
|
||||
'sat,auto', 'sat', 'scsi', 'auto',
|
||||
'usbjmicron', 'usbprolific', 'usbcypress', 'usbsunplus',
|
||||
);
|
||||
my $best_out = $out;
|
||||
my $best_cmd = $cmd;
|
||||
foreach my $t (@types) {
|
||||
my $try_cmd = "smartctl -a -d $t " . "e_path($device) . " 2>&1";
|
||||
my $try_out = &backquote_command($try_cmd);
|
||||
|
||||
# If it still needs a type, keep trying
|
||||
if ( _smartctl_needs_type($try_out) ) {
|
||||
$best_out = $try_out;
|
||||
$best_cmd = $try_cmd;
|
||||
next;
|
||||
}
|
||||
|
||||
# If SMART commands fail, try permissive once
|
||||
if ( _smartctl_no_smart($try_out) ) {
|
||||
my $perm_cmd = "smartctl -a -T permissive -d $t "
|
||||
. "e_path($device) . " 2>&1";
|
||||
my $perm_out = &backquote_command($perm_cmd);
|
||||
if ( !_smartctl_needs_type($perm_out)
|
||||
&& !_smartctl_no_smart($perm_out) )
|
||||
{
|
||||
$out = $perm_out;
|
||||
$used_cmd = $perm_cmd;
|
||||
last;
|
||||
}
|
||||
$best_out = $perm_out;
|
||||
$best_cmd = $perm_cmd;
|
||||
next;
|
||||
}
|
||||
|
||||
# Success
|
||||
$out = $try_out;
|
||||
$used_cmd = $try_cmd;
|
||||
last;
|
||||
}
|
||||
if ( $used_cmd eq $cmd ) {
|
||||
$out = $best_out;
|
||||
$used_cmd = $best_cmd;
|
||||
if ( _smartctl_no_smart($out) ) {
|
||||
$note = "SMART may not be supported by this USB device or bridge.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($note) {
|
||||
print "<div class='alert alert-warning'>$note</div>\n";
|
||||
}
|
||||
print "<pre>" . &html_escape("Command: $used_cmd\n\n$out") . "</pre>\n";
|
||||
print "</div></div>\n";
|
||||
|
||||
&ui_print_footer( "edit_disk.cgi?device=" . &urlize($device),
|
||||
$text{'disk_return'} );
|
||||
121
bsdfdisk/zfs_create.cgi
Normal file
121
bsdfdisk/zfs_create.cgi
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Create a ZFS filesystem (dataset) within the pool owning this device
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
&error_setup( $text{'newfs_zfs_err'} );
|
||||
|
||||
# Validate input parameters to prevent command injection
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or &error("Invalid device name");
|
||||
$in{'device'} !~ /\.\./ or &error("Invalid device name");
|
||||
$in{'slice'} =~ /^\d+$/ or &error("Invalid slice number");
|
||||
$in{'part'} =~ /^[a-z]$/ or &error("Invalid partition letter") if $in{'part'};
|
||||
|
||||
&has_command('zfs') or &error( $text{'newfs_zfs_nozfs'} );
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
my ( $object, $part );
|
||||
if ( $in{'part'} ne '' ) {
|
||||
($part) = grep { $_->{'letter'} eq $in{'part'} } @{ $slice->{'parts'} };
|
||||
$part || &error( $text{'part_egone'} );
|
||||
$object = $part;
|
||||
}
|
||||
else {
|
||||
$object = $slice;
|
||||
}
|
||||
|
||||
# Determine ZFS pool for this device
|
||||
my $zdev = get_zfs_device_info($object);
|
||||
$zdev || &error( $text{'newfs_zfs_notinpool'} );
|
||||
my $parent = $zdev->{'pool'};
|
||||
|
||||
# Validate dataset name
|
||||
my $name = $in{'zfs'};
|
||||
$name =~ s/^\s+|\s+$//g if defined $name;
|
||||
$name && $name =~ /^[a-zA-Z0-9_\-.:]+$/ or &error( $text{'newfs_zfs_badname'} );
|
||||
my $dataset = $parent . "/" . $name;
|
||||
|
||||
# Allowed property values
|
||||
my %allowed = (
|
||||
'recordsize' =>
|
||||
{ map { $_ => 1 } qw(512 1K 2K 4K 8K 16K 32K 64K 128K 256K 512K 1M) },
|
||||
'compression' => { map { $_ => 1 } qw(on off lz4 gzip) },
|
||||
'atime' => { map { $_ => 1 } qw(on off) },
|
||||
'sync' => { map { $_ => 1 } qw(standard always disabled) },
|
||||
'exec' => { map { $_ => 1 } qw(on off) },
|
||||
'canmount' => { map { $_ => 1 } qw(on off noauto) },
|
||||
'acltype' => { map { $_ => 1 } qw(nfsv4 posixacl) },
|
||||
'aclinherit' => {
|
||||
map { $_ => 1 }
|
||||
qw(discard noallow restricted passthrough passthrough-x)
|
||||
},
|
||||
'aclmode' => { map { $_ => 1 } qw(discard groupmask passthrough) },
|
||||
'xattr' => { map { $_ => 1 } qw(on off sa) },
|
||||
);
|
||||
|
||||
my @props =
|
||||
qw(recordsize compression atime sync exec canmount acltype aclinherit aclmode xattr);
|
||||
my @opts;
|
||||
foreach my $p (@props) {
|
||||
next if ( !defined $in{$p} || $in{$p} eq '' || $in{$p} eq 'default' );
|
||||
$allowed{$p} && $allowed{$p}->{ $in{$p} }
|
||||
or &error( &text( 'newfs_zfs_badopt', $p ) );
|
||||
push( @opts, "-o $p=" . "e_path( $in{$p} ) );
|
||||
}
|
||||
if ( defined $in{'mountpoint'} && $in{'mountpoint'} ne '' ) {
|
||||
push( @opts, "-o mountpoint=" . "e_path( $in{'mountpoint'} ) );
|
||||
}
|
||||
|
||||
&ui_print_unbuffered_header( $object->{'desc'}, $text{'newfs_zfs_title'}, "" );
|
||||
|
||||
print &text( 'newfs_zfs_creating', "<tt>" . &html_escape($dataset) . "</tt>" ),
|
||||
"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd = "zfs create " . join( " ", @opts ) . " " . "e_path($dataset);
|
||||
&additional_log( 'exec', undef, $cmd );
|
||||
my $fh;
|
||||
&open_execute_command( $fh, $cmd, 2 );
|
||||
if ($fh) {
|
||||
while (<$fh>) { print &html_escape($_); }
|
||||
close($fh);
|
||||
}
|
||||
print "</pre>";
|
||||
if ($?) {
|
||||
print $text{'newfs_zfs_failed'}, "<p>\n";
|
||||
}
|
||||
else {
|
||||
# Optional ACL inherit flags
|
||||
if ( $in{'add_inherit'} ) {
|
||||
my $cmd2 = acl_inherit_flags_cmd($dataset);
|
||||
if ($cmd2) { system($cmd2); }
|
||||
}
|
||||
print $text{'newfs_zfs_done'}, "<p>\n";
|
||||
&webmin_log( "zfs-create", "dataset", $dataset, { parent => $parent } );
|
||||
}
|
||||
|
||||
if ( $in{'part'} ne '' ) {
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
my $url_part = &urlize( $in{'part'} );
|
||||
&ui_print_footer(
|
||||
"edit_part.cgi?device=$url_device&slice=$url_slice&part=$url_part",
|
||||
$text{'part_return'}
|
||||
);
|
||||
}
|
||||
else {
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&ui_print_footer(
|
||||
"edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
}
|
||||
128
bsdfdisk/zvol_create.cgi
Normal file
128
bsdfdisk/zvol_create.cgi
Normal file
@@ -0,0 +1,128 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Create a ZFS volume (zvol) within the pool owning this device
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our ( %in, %text, $module_name );
|
||||
&ReadParse();
|
||||
&error_setup( $text{'newfs_zvol_err'} );
|
||||
|
||||
# Validate input parameters to prevent command injection
|
||||
$in{'device'} =~ /^[a-zA-Z0-9_\/.-]+$/ or &error("Invalid device name");
|
||||
$in{'device'} !~ /\.\./ or &error("Invalid device name");
|
||||
$in{'slice'} =~ /^\d+$/ or &error("Invalid slice number");
|
||||
$in{'part'} =~ /^[a-z]$/ or &error("Invalid partition letter") if $in{'part'};
|
||||
|
||||
&has_command('zfs') or &error( $text{'newfs_zfs_nozfs'} );
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = &list_disks_partitions();
|
||||
my ($disk) = grep { $_->{'device'} eq $in{'device'} } @disks;
|
||||
$disk || &error( $text{'disk_egone'} );
|
||||
my ($slice) = grep { $_->{'number'} eq $in{'slice'} } @{ $disk->{'slices'} };
|
||||
$slice || &error( $text{'slice_egone'} );
|
||||
my ( $object, $part );
|
||||
if ( $in{'part'} ne '' ) {
|
||||
($part) = grep { $_->{'letter'} eq $in{'part'} } @{ $slice->{'parts'} };
|
||||
$part || &error( $text{'part_egone'} );
|
||||
$object = $part;
|
||||
}
|
||||
else {
|
||||
$object = $slice;
|
||||
}
|
||||
|
||||
# Determine ZFS pool for this device
|
||||
my $zdev = get_zfs_device_info($object);
|
||||
$zdev || &error( $text{'newfs_zfs_notinpool'} );
|
||||
my $parent = $zdev->{'pool'};
|
||||
|
||||
# Validate zvol name and size
|
||||
my $name = $in{'zvol'};
|
||||
$name =~ s/^\s+|\s+$//g if defined $name;
|
||||
$name && $name =~ /^[a-zA-Z0-9_\-.:]+$/
|
||||
or &error( $text{'newfs_zvol_badname'} );
|
||||
my $size = $in{'size'};
|
||||
$size =~ s/^\s+|\s+$//g if defined $size;
|
||||
$size && $size =~ /^\d+(\.\d+)?[KMGTP]?$/i
|
||||
or &error( $text{'newfs_zvol_badsize'} );
|
||||
my $dataset = $parent . "/" . $name;
|
||||
|
||||
# Allowed property values
|
||||
my %allowed = (
|
||||
'volblocksize' => { map { $_ => 1 } qw(512 1K 2K 4K 8K 16K 32K 64K 128K) },
|
||||
'compression' => { map { $_ => 1 } qw(on off lz4 gzip) },
|
||||
'sync' => { map { $_ => 1 } qw(standard always disabled) },
|
||||
'logbias' => { map { $_ => 1 } qw(latency throughput) },
|
||||
'primarycache' => { map { $_ => 1 } qw(all metadata none) },
|
||||
'secondarycache' => { map { $_ => 1 } qw(all metadata none) },
|
||||
);
|
||||
|
||||
my @props =
|
||||
qw(volblocksize compression sync logbias primarycache secondarycache);
|
||||
my @opts;
|
||||
foreach my $p (@props) {
|
||||
next if ( !defined $in{$p} || $in{$p} eq '' || $in{$p} eq 'default' );
|
||||
$allowed{$p} && $allowed{$p}->{ $in{$p} }
|
||||
or &error( &text( 'newfs_zvol_badopt', $p ) );
|
||||
push( @opts, "-o $p=" . "e_path( $in{$p} ) );
|
||||
}
|
||||
|
||||
my $sparse = $in{'sparse'};
|
||||
my $refres = $in{'refreservation'};
|
||||
if ( defined $refres ) {
|
||||
$refres =~ s/^\s+|\s+$//g;
|
||||
if ( $refres ne '' && lc($refres) ne 'none' ) {
|
||||
$refres =~ /^\d+(\.\d+)?[KMGTP]?$/i
|
||||
or &error( $text{'newfs_zvol_badrefres'} );
|
||||
push( @opts, "-o refreservation=" . "e_path($refres) );
|
||||
}
|
||||
}
|
||||
|
||||
&ui_print_unbuffered_header( $object->{'desc'}, $text{'newfs_zvol_title'}, "" );
|
||||
|
||||
print &text( 'newfs_zvol_creating', "<tt>" . &html_escape($dataset) . "</tt>" ),
|
||||
"<br>\n";
|
||||
print "<pre>\n";
|
||||
my $cmd =
|
||||
"zfs create "
|
||||
. ( $sparse ? "-s " : "" ) . "-V "
|
||||
. "e_path($size) . " "
|
||||
. join( " ", @opts ) . " "
|
||||
. "e_path($dataset);
|
||||
&additional_log( 'exec', undef, $cmd );
|
||||
my $fh;
|
||||
&open_execute_command( $fh, $cmd, 2 );
|
||||
|
||||
if ($fh) {
|
||||
while (<$fh>) { print &html_escape($_); }
|
||||
close($fh);
|
||||
}
|
||||
print "</pre>";
|
||||
if ($?) {
|
||||
print $text{'newfs_zvol_failed'}, "<p>\n";
|
||||
}
|
||||
else {
|
||||
print $text{'newfs_zvol_done'}, "<p>\n";
|
||||
&webmin_log( "zfs-create", "zvol", $dataset,
|
||||
{ parent => $parent, volsize => $size } );
|
||||
}
|
||||
|
||||
if ( $in{'part'} ne '' ) {
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
my $url_part = &urlize( $in{'part'} );
|
||||
&ui_print_footer(
|
||||
"edit_part.cgi?device=$url_device&slice=$url_slice&part=$url_part",
|
||||
$text{'part_return'}
|
||||
);
|
||||
}
|
||||
else {
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
&ui_print_footer(
|
||||
"edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
}
|
||||
@@ -10,6 +10,24 @@ use WebminCore;
|
||||
$parallel_max = 20;
|
||||
%access = &get_module_acl();
|
||||
|
||||
# validate_remote_download_target()
|
||||
# Validates URL-derived download target fields in %in
|
||||
sub validate_remote_download_target
|
||||
{
|
||||
$in{'host'} =~ /^\S+$/ || &error("Invalid download host");
|
||||
my @ips = &to_ipaddress($in{'host'});
|
||||
push(@ips, &to_ip6address($in{'host'}));
|
||||
@ips || &error("Invalid download host");
|
||||
if ($in{'ftpfile'}) {
|
||||
$in{'ftpfile'} =~ /^\/\S+$/ || &error("Invalid FTP path");
|
||||
}
|
||||
else {
|
||||
$in{'port'} =~ /^\d+$/ && $in{'port'} >= 1 && $in{'port'} <= 65535 ||
|
||||
&error("Invalid HTTP port");
|
||||
$in{'page'} =~ /^\/\S*$/ || &error("Invalid HTTP path");
|
||||
}
|
||||
}
|
||||
|
||||
# list_software_hosts()
|
||||
# Returns a list of all hosts whose software is being managed by this module
|
||||
sub list_software_hosts
|
||||
|
||||
@@ -24,6 +24,9 @@ else {
|
||||
}
|
||||
|
||||
$in{'source'} == 3 || -r $in{'file'} || &error($text{'do_edeleted'});
|
||||
if ($in{'source'} == 2 && $in{'down'}) {
|
||||
&validate_remote_download_target();
|
||||
}
|
||||
&ui_print_header(undef, $text{'do_title'}, "");
|
||||
|
||||
# Setup error handler for down hosts
|
||||
@@ -75,8 +78,9 @@ foreach $h (@hosts) {
|
||||
elsif ($in{'source'} == 0) {
|
||||
# Is the file the same on remote (like if we have NFS)
|
||||
local @st = stat($in{'file'});
|
||||
local $qfile = "e_literal_escape($in{'file'});
|
||||
local $rst = &remote_eval($s->{'host'}, "software",
|
||||
"[ stat('$in{'file'}') ]");
|
||||
"[ stat('$qfile') ]");
|
||||
local @rst = @$rst;
|
||||
if (@st && @rst && $st[7] == $rst[7] &&
|
||||
$st[9] == $rst[9]) {
|
||||
@@ -170,8 +174,11 @@ foreach $h (@hosts) {
|
||||
}
|
||||
}
|
||||
}
|
||||
&remote_eval($s->{'host'}, "software", "unlink('$rfile')")
|
||||
if ($need_unlink);
|
||||
if ($need_unlink) {
|
||||
local $qfile = "e_literal_escape($rfile);
|
||||
&remote_eval($s->{'host'}, "software",
|
||||
"unlink('$qfile')");
|
||||
}
|
||||
|
||||
print $wh &serialise_variable(\@rv);
|
||||
close($wh);
|
||||
|
||||
@@ -12,6 +12,9 @@ foreach $p (@packages) {
|
||||
push(@names, $n); push(@descs, $d);
|
||||
}
|
||||
-r $in{'file'} || &error($text{'do_edeleted'});
|
||||
if ($in{'source'} == 2 && $in{'down'}) {
|
||||
&validate_remote_download_target();
|
||||
}
|
||||
&ui_print_header(undef, $text{'do_title'}, "");
|
||||
print "<b>",&text('do_header', join(" ", @names)),"</b><p>\n";
|
||||
|
||||
@@ -78,7 +81,10 @@ foreach $h (@hosts) {
|
||||
}
|
||||
}
|
||||
}
|
||||
&remote_eval($s->{'host'}, "software", "unlink('$rfile')") if ($need_unlink);
|
||||
if ($need_unlink) {
|
||||
local $qfile = "e_literal_escape($rfile);
|
||||
&remote_eval($s->{'host'}, "software", "unlink('$qfile')");
|
||||
}
|
||||
}
|
||||
unlink($in{'file'}) if ($in{'need_unlink'});
|
||||
print "<p><b>$text{'do_done'}</b><p>\n";
|
||||
|
||||
@@ -332,7 +332,8 @@ foreach $h (@hosts) {
|
||||
\$ENV{'config_dir'} = \$config{'usermin_dir'};
|
||||
\$ENV{'webmin_upgrade'} = 1;
|
||||
\$ENV{'autothird'} = 1;
|
||||
\$out = `(cd $extract/usermin-$version && $setup) </dev/null 2>&1 | tee /tmp/.webmin/usermin-setup.out`;
|
||||
\$tmp = &tempname_dir();
|
||||
\$out = `(cd $extract/usermin-$version && $setup) </dev/null 2>&1 | tee \$tmp/usermin-setup.out`;
|
||||
(\$out, \$?)");
|
||||
if ($out !~ /success/i) {
|
||||
print $wh &serialise_variable(
|
||||
|
||||
@@ -423,7 +423,8 @@ foreach $h (@hosts) {
|
||||
\$ENV{'config_dir'} = \$config_directory;
|
||||
\$ENV{'webmin_upgrade'} = 1;
|
||||
\$ENV{'autothird'} = 1;
|
||||
\$out = `(cd $extract/webmin-$version && $setup) </dev/null 2>&1 | tee /tmp/.webmin/webmin-setup.out`;
|
||||
\$tmp = &tempname_dir();
|
||||
\$out = `(cd $extract/webmin-$version && $setup) </dev/null 2>&1 | tee \$tmp/webmin-setup.out`;
|
||||
(\$out, \$?)");
|
||||
if ($ex || $out !~ /success|^0$/i) {
|
||||
print $wh &serialise_variable(
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
require './cpan-lib.pl';
|
||||
&ReadParse();
|
||||
$tmp_base = $gconfig{'tempdir'} || "/tmp/.webmin";
|
||||
$tmp_base = $gconfig{'tempdir'} || &default_webmin_temp_dir();
|
||||
foreach $f (split(/\0/, $in{'file'})) {
|
||||
$f =~ /^\Q$tmp_base\E\// || &error($text{'delete_efile'});
|
||||
unlink($f);
|
||||
|
||||
@@ -43,7 +43,9 @@ if (!$fcron) {
|
||||
local $user;
|
||||
opendir(DIR, $config{'cron_dir'});
|
||||
while($user = readdir(DIR)) {
|
||||
system("cp $config{'cron_dir'}/$user $cron_temp_file");
|
||||
next if ($user =~ /^\./);
|
||||
system("cp ".quotemeta("$config{'cron_dir'}/$user").
|
||||
" ".quotemeta($cron_temp_file));
|
||||
©_crontab($user);
|
||||
}
|
||||
closedir(DIR);
|
||||
|
||||
@@ -309,7 +309,8 @@ elsif ($fcron) {
|
||||
undef, $cron_temp_file, undef);
|
||||
}
|
||||
else {
|
||||
system("cp ".&translate_filename("$config{'cron_dir'}/$_[0]->{'user'}").
|
||||
system("cp ".quotemeta(
|
||||
&translate_filename("$config{'cron_dir'}/$_[0]->{'user'}")).
|
||||
" ".quotemeta($cron_temp_file)." 2>/dev/null");
|
||||
}
|
||||
}
|
||||
@@ -513,7 +514,7 @@ if (&read_file_contents($cron_temp_file) =~ /\S/) {
|
||||
$ENV{"VISUAL"} = $ENV{"EDITOR"} =
|
||||
"$module_root_directory/cron_editor.pl";
|
||||
$ENV{"CRON_EDITOR_COPY"} = $cron_temp_file;
|
||||
system("chown $_[0] $cron_temp_file");
|
||||
system("chown ".quotemeta($_[0])." ".quotemeta($cron_temp_file));
|
||||
local $oldpwd = &get_current_dir();
|
||||
chdir("/");
|
||||
if ($single_user) {
|
||||
@@ -600,7 +601,7 @@ sub user_sub
|
||||
{
|
||||
local($tmp);
|
||||
$tmp = $_[0];
|
||||
$tmp =~ s/USER/$_[1]/g;
|
||||
$tmp =~ s/USER/quotemeta($_[1])/ge;
|
||||
return $tmp;
|
||||
}
|
||||
|
||||
@@ -1557,7 +1558,7 @@ if ($err) {
|
||||
|
||||
=head2 cleanup_temp_files
|
||||
|
||||
Called from cron to delete old files in the Webmin /tmp directory, and also
|
||||
Called from cron to delete old files in the Webmin temp directory, and also
|
||||
old lock links directories.
|
||||
|
||||
=cut
|
||||
@@ -1569,7 +1570,7 @@ if (!$gconfig{'tempdelete_days'}) {
|
||||
return;
|
||||
}
|
||||
|
||||
# Cleanup files in /tmp/.webmin
|
||||
# Cleanup files in the default Webmin temp directory
|
||||
if ($gconfig{'tempdir'} && !$gconfig{'tempdirdelete'}) {
|
||||
print STDERR "Temp file clearing is not done for the custom directory $gconfig{'tempdir'}\n";
|
||||
}
|
||||
@@ -1577,7 +1578,7 @@ else {
|
||||
my $tempdir = &transname();
|
||||
$tempdir =~ s/\/([^\/]+)$//;
|
||||
if (!$tempdir || $tempdir eq "/") {
|
||||
$tempdir = "/tmp/.webmin";
|
||||
$tempdir = &default_webmin_temp_dir();
|
||||
}
|
||||
|
||||
my $cutoff = time() - $gconfig{'tempdelete_days'}*24*60*60;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
sleep(1); # This is needed because the stupid crontab -e command
|
||||
# checks the mtime before and after editing, and if they are
|
||||
# the same it assumes no change has been made!!
|
||||
open(SRC, "<".$ENV{"CRON_EDITOR_COPY"});
|
||||
open(DST, ">".$ARGV[0]) || die "Failed to open $ARGV[0] : $!";
|
||||
open(SRC, "<", $ENV{"CRON_EDITOR_COPY"});
|
||||
open(DST, ">", $ARGV[0]) || die "Failed to open $ARGV[0] : $!";
|
||||
while(<SRC>) {
|
||||
if (!/^#.*DO NOT EDIT/i && !/^#.*installed on/i &&
|
||||
!/^#.*Cron version/i) {
|
||||
|
||||
@@ -57,7 +57,7 @@ if ($in{'bg'}) {
|
||||
&open_tempfile(TEMP, ">$temp");
|
||||
&print_tempfile(TEMP, $input);
|
||||
&close_tempfile(TEMP);
|
||||
&execute_command("(($lines[0]) ; rm -f $temp) &", $temp,
|
||||
&execute_command("(($lines[0]) ; rm -f ".quotemeta($temp).") &", $temp,
|
||||
undef, undef);
|
||||
}
|
||||
else {
|
||||
|
||||
205
fastrpc.cgi
205
fastrpc.cgi
@@ -10,6 +10,10 @@ use POSIX;
|
||||
use Socket;
|
||||
$force_lang = $default_lang;
|
||||
&init_config();
|
||||
|
||||
my $DEFAULT_RPC_TIMEOUT = 60;
|
||||
my $DEFAULT_RPC_IDLE_FACTOR = 6;
|
||||
|
||||
print "Content-type: text/plain\n\n";
|
||||
|
||||
# Can this user make remote calls?
|
||||
@@ -34,7 +38,7 @@ if ($aerr) {
|
||||
exit;
|
||||
}
|
||||
if (open(RANDOM, "/dev/urandom")) {
|
||||
local $tmpsid;
|
||||
my $tmpsid;
|
||||
read(RANDOM, $tmpsid, 16);
|
||||
$sid = lc(unpack('h*', $tmpsid));
|
||||
close RANDOM;
|
||||
@@ -45,6 +49,16 @@ else {
|
||||
$version = &get_webmin_version();
|
||||
print "1 $port $sid $version\n";
|
||||
|
||||
# Timeout
|
||||
my $rpc_idle_factor = $gconfig{'rpc_idle_factor'} || $DEFAULT_RPC_IDLE_FACTOR;
|
||||
if ($rpc_idle_factor !~ /^\d+$/ || $rpc_idle_factor < 1) {
|
||||
$rpc_idle_factor = $DEFAULT_RPC_IDLE_FACTOR;
|
||||
}
|
||||
my $config_rpc_timeout = $gconfig{'rpc_timeout'} || $DEFAULT_RPC_TIMEOUT;
|
||||
if ($config_rpc_timeout !~ /^\d+$/ || $config_rpc_timeout < 1) {
|
||||
$config_rpc_timeout = $DEFAULT_RPC_TIMEOUT;
|
||||
}
|
||||
|
||||
# Fork and listen for calls ..
|
||||
$pid = fork();
|
||||
if ($pid < 0) {
|
||||
@@ -57,14 +71,14 @@ untie(*STDIN);
|
||||
untie(*STDOUT);
|
||||
|
||||
# Accept the TCP connection
|
||||
local $rmask;
|
||||
my $rmask;
|
||||
vec($rmask, fileno(MAIN), 1) = 1;
|
||||
if ($use_ipv6) {
|
||||
vec($rmask, fileno(MAIN6), 1) = 1;
|
||||
}
|
||||
$sel = select($rmask, undef, undef, 60);
|
||||
$sel = select($rmask, undef, undef, $config_rpc_timeout);
|
||||
if ($sel <= 0) {
|
||||
print STDERR "fastrpc: accept timed out\n"
|
||||
print STDERR "fastrpc[$$]: accept timed out\n"
|
||||
if ($gconfig{'rpcdebug'});
|
||||
exit;
|
||||
}
|
||||
@@ -78,63 +92,83 @@ else {
|
||||
die "No connection on any socket!";
|
||||
}
|
||||
die "accept failed : $!" if (!$acptaddr);
|
||||
$oldsel = select(SOCK);
|
||||
my $oldsel = select(SOCK);
|
||||
$| = 1;
|
||||
select($oldsel);
|
||||
|
||||
$rcount = 0;
|
||||
my $rcount = 0;
|
||||
my %xfer_kids;
|
||||
while(1) {
|
||||
# Clean up the list of waiting sub-processes
|
||||
foreach my $p (keys %xfer_kids) {
|
||||
my $waited_pid = waitpid($p, POSIX::WNOHANG());
|
||||
delete($xfer_kids{$p}) if ($waited_pid > 0 || $waited_pid == -1);
|
||||
}
|
||||
|
||||
# Wait for the request. Wait longer if this isn't the first one
|
||||
local $rmask;
|
||||
my $rmask;
|
||||
vec($rmask, fileno(SOCK), 1) = 1;
|
||||
local $sel = select($rmask, undef, undef, $rcount ? 360 : 60);
|
||||
my $timeout = $rcount
|
||||
? $config_rpc_timeout * $rpc_idle_factor
|
||||
: $config_rpc_timeout;
|
||||
my $sel = select($rmask, undef, undef, $timeout);
|
||||
if ($sel <= 0) {
|
||||
print STDERR "fastrpc: session timed out\n"
|
||||
# Don't kill the control session while a tcpwrite/tcpread is
|
||||
# running
|
||||
my $nk = scalar(keys %xfer_kids);
|
||||
if ($nk) {
|
||||
print STDERR "fastrpc[$$]: idle timeout, but $nk ".
|
||||
"transfer(s) active: ".
|
||||
join(",", sort keys %xfer_kids)."\n"
|
||||
if ($gconfig{'rpcdebug'});
|
||||
next;
|
||||
}
|
||||
print STDERR "fastrpc[$$]: session timed out\n"
|
||||
if ($gconfig{'rpcdebug'});
|
||||
last;
|
||||
}
|
||||
|
||||
local $line = <SOCK>;
|
||||
my $line = <SOCK>;
|
||||
last if (!$line);
|
||||
local ($len, $auth) = split(/\s+/, $line);
|
||||
my ($len, $auth) = split(/\s+/, $line);
|
||||
die "Invalid session ID" if ($auth ne $sid);
|
||||
local $rawarg;
|
||||
my $rawarg;
|
||||
while(length($rawarg) < $len) {
|
||||
local $got;
|
||||
local $rv = read(SOCK, $got, $len - length($rawarg));
|
||||
my $got;
|
||||
my $rv = read(SOCK, $got, $len - length($rawarg));
|
||||
exit if ($rv <= 0);
|
||||
$rawarg .= $got;
|
||||
}
|
||||
print STDERR "fastrpc: raw $rawarg\n" if ($gconfig{'rpcdebug'});
|
||||
local $dumper = substr($rawarg, 0, 5) eq '$VAR1' ? 1 : 0;
|
||||
local $arg = &unserialise_variable($rawarg);
|
||||
print STDERR "fastrpc[$$]: raw $rawarg\n" if ($gconfig{'rpcdebug'});
|
||||
my $dumper = substr($rawarg, 0, 5) eq '$VAR1' ? 1 : 0;
|
||||
my $arg = &unserialise_variable($rawarg);
|
||||
|
||||
# Process it
|
||||
local $rv;
|
||||
my $rv;
|
||||
if ($arg->{'action'} eq 'ping') {
|
||||
# Just respond with an OK
|
||||
print STDERR "fastrpc: ping\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: ping\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 1 };
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'check') {
|
||||
# Check if some module is supported
|
||||
print STDERR "fastrpc: check $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: check $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 1,
|
||||
'rv' => &foreign_check($arg->{'module'}, undef, undef,
|
||||
$arg->{'api'}) };
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'config') {
|
||||
# Get the config for some module
|
||||
print STDERR "fastrpc: config $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
|
||||
local %config = &foreign_config($arg->{'module'});
|
||||
print STDERR "fastrpc[$$]: config $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
|
||||
my %config = &foreign_config($arg->{'module'});
|
||||
$rv = { 'status' => 1, 'rv' => \%config };
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'write') {
|
||||
# Transfer data to a local temp file
|
||||
local $file = $arg->{'file'} ? $arg->{'file'} :
|
||||
# Transfer data to a my temp file
|
||||
my $file = $arg->{'file'} ? $arg->{'file'} :
|
||||
$arg->{'name'} ? &tempname($arg->{'name'}) :
|
||||
&tempname();
|
||||
print STDERR "fastrpc: write $file\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: write $file\n" if ($gconfig{'rpcdebug'});
|
||||
open(FILE, ">$file");
|
||||
binmode(FILE);
|
||||
print FILE $arg->{'data'};
|
||||
@@ -142,24 +176,32 @@ while(1) {
|
||||
$rv = { 'status' => 1, 'rv' => $file };
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'tcpwrite') {
|
||||
# Transfer data to a local temp file over TCP connection
|
||||
local $file = $arg->{'file'} ? $arg->{'file'} :
|
||||
# Transfer data to a my temp file over TCP connection
|
||||
my $file = $arg->{'file'} ? $arg->{'file'} :
|
||||
$arg->{'name'} ? &tempname($arg->{'name'}) :
|
||||
&tempname();
|
||||
print STDERR "fastrpc: tcpwrite $file\n" if ($gconfig{'rpcdebug'});
|
||||
local $tsock = time().$$;
|
||||
local $tsock6 = $use_ipv6 ? time().$$."v6" : undef;
|
||||
local $tport = $port + 1;
|
||||
print STDERR "fastrpc[$$]: tcpwrite $file\n" if ($gconfig{'rpcdebug'});
|
||||
my $tsock = time().$$;
|
||||
my $tsock6 = $use_ipv6 ? time().$$."v6" : undef;
|
||||
my $tport = $port + 1;
|
||||
&allocate_socket($tsock, $tsock6, \$tport);
|
||||
if (!fork()) {
|
||||
my $cpid = fork();
|
||||
if (!defined($cpid)) {
|
||||
$rv = { 'status' => 0, 'rv' => "fork() failed : $!" };
|
||||
}
|
||||
elsif ($cpid == 0) {
|
||||
close(SOCK);
|
||||
close(MAIN);
|
||||
close(MAIN6) if ($use_ipv6);
|
||||
# Accept connection in separate process
|
||||
print STDERR "fastrpc: tcpwrite $file port $tport\n" if ($gconfig{'rpcdebug'});
|
||||
local $rmask;
|
||||
print STDERR "fastrpc[$$]: tcpwrite $file port $tport\n"
|
||||
if ($gconfig{'rpcdebug'});
|
||||
my $rmask;
|
||||
vec($rmask, fileno($tsock), 1) = 1;
|
||||
if ($use_ipv6) {
|
||||
vec($rmask, fileno($tsock6), 1) = 1;
|
||||
}
|
||||
local $sel = select($rmask, undef, undef, 30);
|
||||
my $sel = select($rmask, undef, undef, 30);
|
||||
exit if ($sel <= 0);
|
||||
if (vec($rmask, fileno($tsock), 1)) {
|
||||
accept(TRANS, $tsock) || exit;
|
||||
@@ -167,40 +209,44 @@ while(1) {
|
||||
elsif ($use_ipv6 && vec($rmask, fileno($tsock6), 1)) {
|
||||
accept(TRANS, $tsock6) || exit;
|
||||
}
|
||||
print STDERR "fastrpc: tcpwrite $file accepted\n" if ($gconfig{'rpcdebug'});
|
||||
local $buf;
|
||||
local $err;
|
||||
print STDERR "fastrpc[$$]: tcpwrite $file accepted\n" if ($gconfig{'rpcdebug'});
|
||||
my $buf;
|
||||
my $err;
|
||||
if (open(FILE, ">$file")) {
|
||||
binmode(FILE);
|
||||
print STDERR "fastrpc: tcpwrite $file writing\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: tcpwrite $file writing\n" if ($gconfig{'rpcdebug'});
|
||||
my $bs = &get_buffer_size();
|
||||
while(read(TRANS, $buf, $bs) > 0) {
|
||||
local $ok = (print FILE $buf);
|
||||
my $ok = (print FILE $buf);
|
||||
if (!$ok) {
|
||||
$err = "Write to $file failed : $!";
|
||||
last;
|
||||
}
|
||||
}
|
||||
close(FILE);
|
||||
print STDERR "fastrpc: tcpwrite $file written\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: tcpwrite $file written\n" if ($gconfig{'rpcdebug'});
|
||||
}
|
||||
else {
|
||||
print STDERR "fastrpc: tcpwrite $file open failed $!\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: tcpwrite $file open failed $!\n" if ($gconfig{'rpcdebug'});
|
||||
$err = "Failed to open $file : $!";
|
||||
}
|
||||
print TRANS $err ? "$err\n" : "OK\n";
|
||||
close(TRANS);
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
$xfer_kids{$cpid} = 1;
|
||||
print STDERR "fastrpc[$$]: tcpwrite $file started\n"
|
||||
if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 1, 'rv' => [ $file, $tport ] };
|
||||
}
|
||||
close($tsock);
|
||||
close($tsock6);
|
||||
print STDERR "fastrpc: tcpwrite $file done\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 1, 'rv' => [ $file, $tport ] };
|
||||
close($tsock6) if ($use_ipv6);
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'read') {
|
||||
# Transfer data from a file
|
||||
print STDERR "fastrpc: read $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
|
||||
local ($data, $got);
|
||||
print STDERR "fastrpc[$$]: read $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
|
||||
my ($data, $got);
|
||||
open(FILE, "<$arg->{'file'}");
|
||||
binmode(FILE);
|
||||
my $bs = &get_buffer_size();
|
||||
@@ -212,7 +258,7 @@ while(1) {
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'tcpread') {
|
||||
# Transfer data from a file over TCP connection
|
||||
print STDERR "fastrpc: tcpread $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: tcpread $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
|
||||
if (-d $arg->{'file'}) {
|
||||
$rv = { 'status' => 1, 'rv' => [ undef, "$arg->{'file'} is a directory" ] };
|
||||
}
|
||||
@@ -221,18 +267,25 @@ while(1) {
|
||||
}
|
||||
else {
|
||||
binmode(FILE);
|
||||
local $tsock = time().$$;
|
||||
local $tsock6 = $use_ipv6 ? time().$$."v6" : undef;
|
||||
local $tport = $port + 1;
|
||||
my $tsock = time().$$;
|
||||
my $tsock6 = $use_ipv6 ? time().$$."v6" : undef;
|
||||
my $tport = $port + 1;
|
||||
&allocate_socket($tsock, $tsock6, \$tport);
|
||||
if (!fork()) {
|
||||
my $cpid = fork();
|
||||
if (!defined($cpid)) {
|
||||
$rv = { 'status' => 0, 'rv' => "fork() failed : $!" };
|
||||
}
|
||||
elsif ($cpid == 0) {
|
||||
close(SOCK);
|
||||
close(MAIN);
|
||||
close(MAIN6) if ($use_ipv6);
|
||||
# Accept connection in separate process
|
||||
local $rmask;
|
||||
my $rmask;
|
||||
vec($rmask, fileno($tsock), 1) = 1;
|
||||
if ($use_ipv6) {
|
||||
vec($rmask, fileno($tsock6), 1) = 1;
|
||||
}
|
||||
local $sel = select($rmask, undef, undef, 30);
|
||||
my $sel = select($rmask, undef, undef, 30);
|
||||
exit if ($sel <= 0);
|
||||
if (vec($rmask, fileno($tsock), 1)) {
|
||||
accept(TRANS, $tsock) || exit;
|
||||
@@ -240,7 +293,7 @@ while(1) {
|
||||
elsif (vec($rmask, fileno($tsock6), 1)) {
|
||||
accept(TRANS, $tsock6) || exit;
|
||||
}
|
||||
local $buf;
|
||||
my $buf;
|
||||
while(read(FILE, $buf, 1024) > 0) {
|
||||
print TRANS $buf;
|
||||
}
|
||||
@@ -248,33 +301,39 @@ while(1) {
|
||||
close(TRANS);
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
$xfer_kids{$cpid} = 1;
|
||||
print STDERR "fastrpc[$$]: tcpread $arg->{'file'} ".
|
||||
"started\n"
|
||||
if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 1,
|
||||
'rv' => [ $arg->{'file'}, $tport ] };
|
||||
}
|
||||
close(FILE);
|
||||
close($tsock);
|
||||
close($tsock6);
|
||||
print STDERR "fastrpc: tcpread $arg->{'file'} done\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 1, 'rv' => [ $arg->{'file'}, $tport ] };
|
||||
close($tsock6) if ($use_ipv6);
|
||||
}
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'require') {
|
||||
# require a library
|
||||
print STDERR "fastrpc: require $arg->{'module'}/$arg->{'file'}\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: require $arg->{'module'}/$arg->{'file'}\n" if ($gconfig{'rpcdebug'});
|
||||
eval {
|
||||
&foreign_require($arg->{'module'},
|
||||
$arg->{'file'});
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR "fastrpc: require error $@\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: require error $@\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 0, 'rv' => $@ };
|
||||
}
|
||||
else {
|
||||
print STDERR "fastrpc: require done\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: require done\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 1 };
|
||||
}
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'call') {
|
||||
# execute a function
|
||||
print STDERR "fastrpc: call $arg->{'module'}::$arg->{'func'}(",join(",", @{$arg->{'args'}}),")\n" if ($gconfig{'rpcdebug'});
|
||||
local @rv;
|
||||
print STDERR "fastrpc[$$]: call $arg->{'module'}::$arg->{'func'}(",join(",", @{$arg->{'args'}}),")\n" if ($gconfig{'rpcdebug'});
|
||||
my @rv;
|
||||
eval {
|
||||
local $main::error_must_die = 1;
|
||||
@rv = &foreign_call($arg->{'module'},
|
||||
@@ -282,7 +341,7 @@ while(1) {
|
||||
@{$arg->{'args'}});
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR "fastrpc: call error $@\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: call error $@\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 0, 'rv' => $@ };
|
||||
}
|
||||
elsif (@rv == 1) {
|
||||
@@ -291,14 +350,14 @@ while(1) {
|
||||
else {
|
||||
$rv = { 'status' => 1, 'arv' => \@rv };
|
||||
}
|
||||
print STDERR "fastrpc: call $arg->{'module'}::$arg->{'func'} done = ",join(",", @rv),"\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: call $arg->{'module'}::$arg->{'func'} done = ",join(",", @rv),"\n" if ($gconfig{'rpcdebug'});
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'eval') {
|
||||
# eval some perl code
|
||||
print STDERR "fastrpc: eval $arg->{'module'} $arg->{'code'}\n" if ($gconfig{'rpcdebug'});
|
||||
local $erv;
|
||||
print STDERR "fastrpc[$$]: eval $arg->{'module'} $arg->{'code'}\n" if ($gconfig{'rpcdebug'});
|
||||
my $erv;
|
||||
if ($arg->{'module'}) {
|
||||
local $pkg = $arg->{'module'};
|
||||
my $pkg = $arg->{'module'};
|
||||
$pkg =~ s/[^A-Za-z0-9]/_/g;
|
||||
$erv = eval "package $pkg;\n".
|
||||
$arg->{'code'}."\n";
|
||||
@@ -306,7 +365,7 @@ while(1) {
|
||||
else {
|
||||
$erv = eval $arg->{'code'};
|
||||
}
|
||||
print STDERR "fastrpc: eval $arg->{'module'} $arg->{'code'} done = $rv error = $@\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: eval $arg->{'module'} $arg->{'code'} done = $rv error = $@\n" if ($gconfig{'rpcdebug'});
|
||||
if ($@) {
|
||||
$rv = { 'status' => 0, 'rv' => $@ };
|
||||
}
|
||||
@@ -315,11 +374,11 @@ while(1) {
|
||||
}
|
||||
}
|
||||
elsif ($arg->{'action'} eq 'quit') {
|
||||
print STDERR "fastrpc: quit\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: quit\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 1 };
|
||||
}
|
||||
else {
|
||||
print STDERR "fastrpc: unknown $arg->{'action'}\n" if ($gconfig{'rpcdebug'});
|
||||
print STDERR "fastrpc[$$]: unknown $arg->{'action'}\n" if ($gconfig{'rpcdebug'});
|
||||
$rv = { 'status' => 0 };
|
||||
}
|
||||
$rawrv = &serialise_variable($rv, $dumper);
|
||||
@@ -334,8 +393,8 @@ while(1) {
|
||||
# allocate_socket(handle, ipv6-handle, &port)
|
||||
sub allocate_socket
|
||||
{
|
||||
local ($fh, $fh6, $port) = @_;
|
||||
local $proto = getprotobyname('tcp');
|
||||
my ($fh, $fh6, $port) = @_;
|
||||
my $proto = getprotobyname('tcp');
|
||||
if (!socket($fh, PF_INET, SOCK_STREAM, $proto)) {
|
||||
return "socket failed : $!";
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ if( $in{ 'action' } eq $text{ 'hdparm_apply' } )
|
||||
{
|
||||
$command .= "-".$key." ".quotemeta($in{$key})." " if ($in{$key} ne "");
|
||||
}
|
||||
$command .= $in{ 'drive' }."\n";
|
||||
$command .= quotemeta($in{ 'drive' })."\n";
|
||||
|
||||
local $out = "<p>". $text{ 'hdparm_performing' }. " : <b>". $command. "</b><i>". &backquote_logged($command). "</i><p>";
|
||||
$out =~ s/\n/<br>/g;
|
||||
@@ -24,7 +24,8 @@ if( $in{ 'action' } eq $text{ 'hdparm_apply' } )
|
||||
} else {
|
||||
&ui_print_header(undef, $text{'hdparm_speed'}, "");
|
||||
|
||||
local ( $_, $_, $buffered, $buffer_cache ) = split( /\n/, `hdparm -t -T $in{ 'drive' }` );
|
||||
local $qdrive = quotemeta($in{ 'drive' });
|
||||
local ( $_, $_, $buffered, $buffer_cache ) = split( /\n/, `hdparm -t -T $qdrive` );
|
||||
( $_, $buffered ) = split( /=/, $buffered );
|
||||
( $_, $buffer_cache ) = split( /=/, $buffer_cache );
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@ if ( ! &has_command( "hdparm" ) ) {
|
||||
|
||||
%hdparm = ( 'A', "1", 'K', "0", 'P', "0", 'X', "0", 'W', "0", 'S', "0" );
|
||||
@yesno = ( "1", $text{ 'hdparm_on' }, "0", $text{ 'hdparm_off' } );
|
||||
local $qdevice = quotemeta($d->{'device'});
|
||||
|
||||
foreach $argument ( 'a', 'd', 'r', 'k', 'u', 'm', 'c' )
|
||||
{
|
||||
$out = `hdparm -$argument $d->{'device'}`;
|
||||
local $qargument = quotemeta($argument);
|
||||
$out = `hdparm -$qargument $qdevice`;
|
||||
if ($out =~ /\s+=\s+(\S+)/) {
|
||||
$hdparm{ $argument } = $1;
|
||||
}
|
||||
|
||||
@@ -241,14 +241,15 @@ foreach my $id (readdir(IDS)) {
|
||||
closedir(IDS);
|
||||
|
||||
# Call fdisk to get partition and geometry information
|
||||
local $devs = join(" ", @devs);
|
||||
local $qdevs = join(" ", map { quotemeta($_) } @devs);
|
||||
local ($disk, $m2);
|
||||
if ($has_parted) {
|
||||
open(FDISK, join(" ; ",
|
||||
map { "parted $_ unit cyl print 2>/dev/null" } @devs)." |");
|
||||
map { "parted ".quotemeta($_)." unit cyl print 2>/dev/null" }
|
||||
@devs)." |");
|
||||
}
|
||||
else {
|
||||
open(FDISK, "fdisk -l -u=cylinders $devs 2>/dev/null || fdisk -l $devs 2>/dev/null |");
|
||||
open(FDISK, "fdisk -l -u=cylinders $qdevs 2>/dev/null || fdisk -l $qdevs 2>/dev/null |");
|
||||
}
|
||||
while(<FDISK>) {
|
||||
if (($m4 = ($_ =~ /Disk\s+([^ :]+):\s+([\d\.]+)\s+(\S+),\s+(\d+)\s+bytes,\s+(\d+)\s+sectors/)) ||
|
||||
@@ -736,7 +737,7 @@ sub delete_partition
|
||||
my ($disk, $part) = @_;
|
||||
if ($has_parted) {
|
||||
# Using parted
|
||||
my $cmd = "parted -s ".$disk." rm ".$part;
|
||||
my $cmd = "parted -s ".quotemeta($disk)." rm ".quotemeta($part);
|
||||
my $out = &backquote_logged("$cmd </dev/null 2>&1");
|
||||
if ($?) {
|
||||
&error("$cmd failed : $out");
|
||||
@@ -765,19 +766,23 @@ my ($disk, $part, $start, $end, $type) = @_;
|
||||
if ($has_parted) {
|
||||
# Using parted
|
||||
my $pe = $part > 4 ? "logical" : "primary";
|
||||
my $qdisk = quotemeta($disk);
|
||||
my $qpart = quotemeta($part);
|
||||
my $qstart = quotemeta($start-1);
|
||||
my $qend = quotemeta($end);
|
||||
my $cmd;
|
||||
if ($type eq "raid") {
|
||||
$cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
|
||||
"ext2 ".($start-1)." ".$end;
|
||||
$cmd .= " ; parted -s ".$disk." set $part raid on";
|
||||
$cmd = "parted -s ".$qdisk." unit cyl mkpart ".$pe." ".
|
||||
"ext2 ".$qstart." ".$qend;
|
||||
$cmd .= " ; parted -s ".$qdisk." set $qpart raid on";
|
||||
}
|
||||
elsif ($type && $type ne 'ext2') {
|
||||
$cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
|
||||
$type." ".($start-1)." ".$end;
|
||||
$cmd = "parted -s ".$qdisk." unit cyl mkpart ".$pe." ".
|
||||
quotemeta($type)." ".$qstart." ".$qend;
|
||||
}
|
||||
else {
|
||||
$cmd = "parted -s ".$disk." unit cyl mkpart ".$pe." ".
|
||||
($start-1)." ".$end;
|
||||
$cmd = "parted -s ".$qdisk." unit cyl mkpart ".$pe." ".
|
||||
$qstart." ".$qend;
|
||||
}
|
||||
my $out = &backquote_logged("$cmd </dev/null 2>&1");
|
||||
if ($?) {
|
||||
@@ -830,8 +835,8 @@ sub create_extended
|
||||
my ($disk, $part, $start, $end) = @_;
|
||||
if ($has_parted) {
|
||||
# Create using parted
|
||||
my $cmd = "parted -s ".$disk." unit cyl mkpart extended ".
|
||||
($start-1)." ".$end;
|
||||
my $cmd = "parted -s ".quotemeta($disk)." unit cyl mkpart ".
|
||||
"extended ".quotemeta($start-1)." ".quotemeta($end);
|
||||
my $out = &backquote_logged("$cmd </dev/null 2>&1");
|
||||
if ($?) {
|
||||
&error("$cmd failed : $out");
|
||||
@@ -1027,7 +1032,8 @@ elsif ($_[0] eq "btrfs") {
|
||||
# given device. Options are taken from %in.
|
||||
sub mkfs_parse
|
||||
{
|
||||
local($cmd);
|
||||
local($cmd, $qdev);
|
||||
$qdev = quotemeta($_[1]);
|
||||
if ($_[0] eq "msdos" || $_[0] eq "vfat") {
|
||||
$cmd = "mkfs -t $_[0]";
|
||||
$cmd .= &opt_check("msdos_ff", '[1-2]', "-f");
|
||||
@@ -1035,17 +1041,17 @@ if ($_[0] eq "msdos" || $_[0] eq "vfat") {
|
||||
$in{'msdos_F_other'} =~ /^\d+$/ ||
|
||||
&error(&text('opt_error', $in{'msdos_F_other'},
|
||||
$text{'msdos_F'}));
|
||||
$cmd .= " -F ".$in{'msdos_F_other'};
|
||||
$cmd .= " -F ".quotemeta($in{'msdos_F_other'});
|
||||
}
|
||||
elsif ($in{'msdos_F'}) {
|
||||
$cmd .= " -F ".$in{'msdos_F'};
|
||||
$cmd .= " -F ".quotemeta($in{'msdos_F'});
|
||||
}
|
||||
$cmd .= &opt_check("msdos_i", '[0-9a-f]{8}', "-i");
|
||||
$cmd .= &opt_check("msdos_n", '\S{1,11}', "-n");
|
||||
$cmd .= &opt_check("msdos_r", '\d+', "-r");
|
||||
$cmd .= &opt_check("msdos_s", '\d+', "-s");
|
||||
$cmd .= $in{'msdos_c'} ? " -c" : "";
|
||||
$cmd .= " $_[1]";
|
||||
$cmd .= " $qdev";
|
||||
}
|
||||
elsif ($_[0] eq "minix") {
|
||||
local(@plist, $disk, $part, $i, @pinfo);
|
||||
@@ -1054,13 +1060,13 @@ elsif ($_[0] eq "minix") {
|
||||
$cmd .= &opt_check("minix_i", '\d+', "-i ");
|
||||
$cmd .= $in{'minix_c'} ? " -c" : "";
|
||||
$cmd .= &opt_check("minix_b", '\d+', " ");
|
||||
$cmd .= " $_[1]";
|
||||
$cmd .= " $qdev";
|
||||
}
|
||||
elsif ($_[0] eq "reiserfs") {
|
||||
$cmd = "yes | mkreiserfs";
|
||||
$cmd .= " -f" if ($in{'reiserfs_f'});
|
||||
$cmd .= " -h $in{'reiserfs_h'}" if ($in{'reiserfs_h'});
|
||||
$cmd .= " $_[1]";
|
||||
$cmd .= " -h ".quotemeta($in{'reiserfs_h'}) if ($in{'reiserfs_h'});
|
||||
$cmd .= " $qdev";
|
||||
}
|
||||
elsif ($_[0] =~ /^ext\d+$/) {
|
||||
if (&has_command("mkfs.$_[0]")) {
|
||||
@@ -1081,7 +1087,7 @@ elsif ($_[0] =~ /^ext\d+$/) {
|
||||
$in{'ext3_j'} =~ /^\d+$/ ||
|
||||
&error(&text('opt_error', $in{'ext3_j'},
|
||||
$text{'ext3_j'}));
|
||||
$cmd .= " -J size=$in{'ext3_j'}";
|
||||
$cmd .= " -J size=".quotemeta($in{'ext3_j'});
|
||||
}
|
||||
}
|
||||
$cmd .= &opt_check("ext2_b", '\d+', "-b");
|
||||
@@ -1090,29 +1096,29 @@ elsif ($_[0] =~ /^ext\d+$/) {
|
||||
$cmd .= &opt_check("ext2_m", '\d+', "-m");
|
||||
$cmd .= $in{'ext2_c'} ? " -c" : "";
|
||||
$cmd .= " -q";
|
||||
$cmd .= " $_[1]";
|
||||
$cmd .= " $qdev";
|
||||
}
|
||||
elsif ($_[0] eq "xfs") {
|
||||
$cmd = "mkfs -t $_[0]";
|
||||
$cmd .= " -f" if ($in{'xfs_f'});
|
||||
$cmd .= " -b size=$in{'xfs_b'}" if (!$in{'xfs_b_def'});
|
||||
$cmd .= " $_[1]";
|
||||
$cmd .= " -b size=".quotemeta($in{'xfs_b'}) if (!$in{'xfs_b_def'});
|
||||
$cmd .= " $qdev";
|
||||
}
|
||||
elsif ($_[0] eq "jfs") {
|
||||
$cmd = "mkfs -t $_[0] -q";
|
||||
$cmd .= &opt_check("jfs_s", '\d+', "-s");
|
||||
$cmd .= " -c" if ($in{'jfs_c'});
|
||||
$cmd .= " $_[1]";
|
||||
$cmd .= " $qdev";
|
||||
}
|
||||
elsif ($_[0] eq "fatx") {
|
||||
$cmd = "mkfs -t $_[0] $_[1]";
|
||||
$cmd = "mkfs -t $_[0] $qdev";
|
||||
}
|
||||
elsif ($_[0] eq "btrfs") {
|
||||
$cmd = "mkfs -t $_[0]";
|
||||
$cmd .= " -l $in{'btrfs_l'}" if (!$in{'btrfs_l_def'});
|
||||
$cmd .= " -n $in{'btrfs_n'}" if (!$in{'btrfs_n_def'});
|
||||
$cmd .= " -s $in{'btrfs_s'}" if (!$in{'btrfs_s_def'});
|
||||
$cmd .= " $_[1]";
|
||||
$cmd .= " -l ".quotemeta($in{'btrfs_l'}) if (!$in{'btrfs_l_def'});
|
||||
$cmd .= " -n ".quotemeta($in{'btrfs_n'}) if (!$in{'btrfs_n_def'});
|
||||
$cmd .= " -s ".quotemeta($in{'btrfs_s'}) if (!$in{'btrfs_s_def'});
|
||||
$cmd .= " $qdev";
|
||||
}
|
||||
if (&has_command("partprobe")) {
|
||||
$cmd = "partprobe ; $cmd";
|
||||
@@ -1170,16 +1176,17 @@ if ($_[0] =~ /^ext\d+$/) {
|
||||
# Returns the tuning command based on user inputs
|
||||
sub tunefs_parse
|
||||
{
|
||||
local $qdev = quotemeta($_[1]);
|
||||
if ($_[0] =~ /^ext\d+$/) {
|
||||
$cmd = "tune2fs";
|
||||
$cmd .= &opt_check("tunefs_c", '\d+', "-c");
|
||||
$cmd .= $in{'tunefs_e_def'} ? "" : " -e$in{'tunefs_e'}";
|
||||
$cmd .= $in{'tunefs_u_def'} ? "" : " -u".getpwnam($in{'tunefs_u'});
|
||||
$cmd .= $in{'tunefs_g_def'} ? "" : " -g".getgrnam($in{'tunefs_g'});
|
||||
$cmd .= $in{'tunefs_e_def'} ? "" : " -e".quotemeta($in{'tunefs_e'});
|
||||
$cmd .= $in{'tunefs_u_def'} ? "" : " -u".quotemeta(getpwnam($in{'tunefs_u'}));
|
||||
$cmd .= $in{'tunefs_g_def'} ? "" : " -g".quotemeta(getgrnam($in{'tunefs_g'}));
|
||||
$cmd .= &opt_check("tunefs_m",'\d+',"-m");
|
||||
$cmd .= &opt_check("tunefs_i", '\d+', "-i").
|
||||
($in{'tunefs_i_def'} ? "" : $in{'tunefs_i_unit'});
|
||||
$cmd .= " $_[1]";
|
||||
($in{'tunefs_i_def'} ? "" : quotemeta($in{'tunefs_i_unit'}));
|
||||
$cmd .= " $qdev";
|
||||
}
|
||||
return $cmd;
|
||||
}
|
||||
@@ -1316,11 +1323,12 @@ return ($_[0] =~ /^ext\d+$/ && &has_command("fsck.$_[0]") ||
|
||||
# Returns the fsck command to unconditionally check a filesystem
|
||||
sub fsck_command
|
||||
{
|
||||
local $qdev = quotemeta($_[1]);
|
||||
if ($_[0] =~ /^ext\d+$/) {
|
||||
return "fsck -t $_[0] -p $_[1]";
|
||||
return "fsck -t $_[0] -p $qdev";
|
||||
}
|
||||
elsif ($_[0] eq "minix") {
|
||||
return "fsck -t minix -a $_[1]";
|
||||
return "fsck -t minix -a $qdev";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1436,21 +1444,27 @@ else {
|
||||
sub open_fdisk
|
||||
{
|
||||
local $fpath = &check_fdisk();
|
||||
local $qfpath = quotemeta($fpath);
|
||||
my $cylarg;
|
||||
if ($fpath =~ /\/fdisk/) {
|
||||
my $out = &backquote_command("$fpath -h 2>&1 </dev/null");
|
||||
my $out = &backquote_command("$qfpath -h 2>&1 </dev/null");
|
||||
if ($out =~ /-u\s+<size>/) {
|
||||
$cylarg = "-u=cylinders";
|
||||
}
|
||||
}
|
||||
local @qargs = map { quotemeta($_) } grep { defined($_) && $_ ne "" }
|
||||
($fpath, $cylarg, @_);
|
||||
($fh, $fpid) = &foreign_call("proc", "pty_process_exec",
|
||||
join(" ", $fpath, $cylarg, @_));
|
||||
join(" ", @qargs));
|
||||
}
|
||||
|
||||
sub open_sfdisk
|
||||
{
|
||||
local $sfpath = &has_command("sfdisk");
|
||||
($fh, $fpid) = &foreign_call("proc", "pty_process_exec", join(" ",$sfpath, @_));
|
||||
local @qargs = map { quotemeta($_) } grep { defined($_) && $_ ne "" }
|
||||
($sfpath, @_);
|
||||
($fh, $fpid) = &foreign_call("proc", "pty_process_exec",
|
||||
join(" ", @qargs));
|
||||
}
|
||||
|
||||
sub check_fdisk
|
||||
@@ -1482,7 +1496,7 @@ if ($in{"$_[0]_def"}) { return ""; }
|
||||
elsif ($in{$_[0]} !~ /^$_[1]$/) {
|
||||
&error(&text('opt_error', $in{$_[0]}, $text{$_[0]}));
|
||||
}
|
||||
else { return " $_[2] $in{$_[0]}"; }
|
||||
else { return " $_[2] ".quotemeta($in{$_[0]}); }
|
||||
}
|
||||
|
||||
%tags = ('0', 'Empty',
|
||||
@@ -1611,7 +1625,8 @@ return 0;
|
||||
sub disk_space
|
||||
{
|
||||
local $w = $_[1] || $_[0];
|
||||
local $out = `df -k '$w'`;
|
||||
local $qw = quotemeta($w);
|
||||
local $out = `df -k $qw`;
|
||||
if ($out =~ /Mounted on\s*\n\s*\S+\s+(\S+)\s+\S+\s+(\S+)/i) {
|
||||
return ($1, $2);
|
||||
}
|
||||
@@ -1649,19 +1664,21 @@ return @fstypes;
|
||||
sub get_label
|
||||
{
|
||||
local $label;
|
||||
local $qdev = quotemeta($_[0]);
|
||||
if ($has_e2label) {
|
||||
$label = `e2label $_[0] 2>&1`;
|
||||
$label = `e2label $qdev 2>&1`;
|
||||
chop($label);
|
||||
}
|
||||
if (($? || $label !~ /\S/) && $has_xfs_db) {
|
||||
$label = undef;
|
||||
local $out = &backquote_with_timeout("xfs_db -x -p xfs_admin -c label -r $_[0] 2>&1", 5);
|
||||
local $out = &backquote_with_timeout(
|
||||
"xfs_db -x -p xfs_admin -c label -r $qdev 2>&1", 5);
|
||||
$label = $1 if ($out =~ /label\s*=\s*"(.*)"/ &&
|
||||
$1 ne '(null)');
|
||||
}
|
||||
if (($? || $label !~ /\S/) && $has_reiserfstune) {
|
||||
$label = undef;
|
||||
local $out = &backquote_command("reiserfstune $_[0]");
|
||||
local $out = &backquote_command("reiserfstune $qdev");
|
||||
if ($out =~ /LABEL:\s*(\S+)/) {
|
||||
$label = $1;
|
||||
}
|
||||
@@ -1703,12 +1720,15 @@ return $uuid;
|
||||
# Tries to set the label for some device's filesystem
|
||||
sub set_label
|
||||
{
|
||||
local $qdev = quotemeta($_[0]);
|
||||
local $qlabel = quotemeta($_[1]);
|
||||
if ($has_e2label && ($_[2] =~ /^ext[23]$/ || !$_[2])) {
|
||||
&system_logged("e2label '$_[0]' '$_[1]' >/dev/null 2>&1");
|
||||
&system_logged("e2label $qdev $qlabel >/dev/null 2>&1");
|
||||
return 1 if (!$?);
|
||||
}
|
||||
if ($has_xfs_db && ($_[2] eq "xfs" || !$_[2])) {
|
||||
&system_logged("xfs_db -x -p xfs_admin -c \"label $_[1]\" $_[0] >/dev/null 2>&1");
|
||||
&system_logged(
|
||||
"xfs_db -x -p xfs_admin -c \"label $qlabel\" $qdev >/dev/null 2>&1");
|
||||
return 1 if (!$?);
|
||||
}
|
||||
return 0;
|
||||
@@ -1719,7 +1739,8 @@ return 0;
|
||||
sub set_name
|
||||
{
|
||||
my ($dinfo, $pinfo, $name) = @_;
|
||||
my $cmd = "parted -s ".$dinfo->{'device'}." name ".$pinfo->{'number'}." ";
|
||||
my $cmd = "parted -s ".quotemeta($dinfo->{'device'})." name ".
|
||||
quotemeta($pinfo->{'number'})." ";
|
||||
if ($name) {
|
||||
$cmd .= quotemeta($name);
|
||||
}
|
||||
@@ -1737,7 +1758,7 @@ if ($?) {
|
||||
sub set_partition_table
|
||||
{
|
||||
my ($disk, $table) = @_;
|
||||
my $cmd = "parted -s ".$disk." mktable ".$table;
|
||||
my $cmd = "parted -s ".quotemeta($disk)." mktable ".quotemeta($table);
|
||||
my $out = &backquote_logged("$cmd </dev/null 2>&1");
|
||||
if ($?) {
|
||||
&error("$cmd failed : $out");
|
||||
|
||||
@@ -17,14 +17,14 @@ else {
|
||||
|
||||
&ui_print_unbuffered_header($uheader, $text{'check_title'}, "");
|
||||
|
||||
$cmd = "$config{'fetchmail_path'} -v -f '$file'";
|
||||
$cmd = "$config{'fetchmail_path'} -v -f ".quotemeta($file);
|
||||
if ($config{'mda_command'}) {
|
||||
$cmd .= " -m '$config{'mda_command'}'";
|
||||
$cmd .= " -m ".quotemeta($config{'mda_command'});
|
||||
}
|
||||
if (defined($in{'idx'})) {
|
||||
@conf = &parse_config_file($file);
|
||||
$poll = $conf[$in{'idx'}];
|
||||
$cmd .= " $poll->{'poll'}";
|
||||
$cmd .= " ".quotemeta($poll->{'poll'});
|
||||
}
|
||||
|
||||
print &text('check_exec', "<tt>$cmd</tt>"),"<p>\n";
|
||||
|
||||
@@ -19,10 +19,10 @@ print &ui_table_start($text{'cron_header'}, "width=100%", 2);
|
||||
|
||||
if ($job) {
|
||||
if ($job->{'command'} =~ /--mail\s+(\S+)/) {
|
||||
$mail = `echo $1`;
|
||||
($mail = $1) =~ s/\\(.)/$1/g;
|
||||
}
|
||||
elsif ($job->{'command'} =~ /--file\s+(\S+)/) {
|
||||
$file = `echo $1`;
|
||||
($file = $1) =~ s/\\(.)/$1/g;
|
||||
}
|
||||
elsif ($job->{'command'} =~ /--output/) {
|
||||
$output = 1;
|
||||
@@ -31,7 +31,7 @@ if ($job) {
|
||||
$owner = 1;
|
||||
}
|
||||
if ($job->{'command'} =~ /--user\s+(\S+)/) {
|
||||
$user = $1;
|
||||
($user = $1) =~ s/\\(.)/$1/g;
|
||||
}
|
||||
if ($job->{'command'} =~ /--errors/) {
|
||||
$errors = 1;
|
||||
|
||||
@@ -36,7 +36,7 @@ if ($in{'errors'}) {
|
||||
}
|
||||
if ($cron_user eq "root" && $fetchmail_config) {
|
||||
defined(getpwnam($in{'user'})) || &error($text{'cron_euser'});
|
||||
$cmd .= " --user $in{'user'}";
|
||||
$cmd .= " --user ".quotemeta($in{'user'});
|
||||
}
|
||||
|
||||
if ($job && $in{'enabled'}) {
|
||||
|
||||
@@ -51,9 +51,9 @@ if ($found) {
|
||||
else {
|
||||
&create_poll($poll, $file);
|
||||
if ($in{'user'} && $< == 0) {
|
||||
&system_logged("chown $in{'user'} $file");
|
||||
&system_logged("chown ".quotemeta($in{'user'})." ".quotemeta($file));
|
||||
}
|
||||
&system_logged("chmod 700 $file");
|
||||
&system_logged("chmod 700 ".quotemeta($file));
|
||||
}
|
||||
&unlock_file($file);
|
||||
&webmin_log("global", undef, $config{'config_file'} ? $file : $in{'user'},
|
||||
|
||||
@@ -101,9 +101,9 @@ else {
|
||||
&create_poll($poll, $file);
|
||||
if ($in{'user'} && $< == 0) {
|
||||
local @uinfo = getpwnam($in{'user'});
|
||||
&system_logged("chown $uinfo[2]:$uinfo[3] $file");
|
||||
&system_logged("chown $uinfo[2]:$uinfo[3] ".quotemeta($file));
|
||||
}
|
||||
&system_logged("chmod 700 $file");
|
||||
&system_logged("chmod 700 ".quotemeta($file));
|
||||
}
|
||||
else {
|
||||
&modify_poll($poll, $file);
|
||||
|
||||
@@ -13,17 +13,21 @@ if ($config{'start_cmd'}) {
|
||||
}
|
||||
else {
|
||||
$in{'interval'} =~ /^\d+$/ || &error($text{'start_einterval'});
|
||||
$mda = " -m '$config{'mda_command'}'" if ($config{'mda_command'});
|
||||
$mda = " -m ".quotemeta($config{'mda_command'}) if ($config{'mda_command'});
|
||||
my $qinterval = quotemeta($in{'interval'});
|
||||
my $qconfig_file = quotemeta($config{'config_file'});
|
||||
if ($< == 0) {
|
||||
if ($config{'daemon_user'} eq 'root') {
|
||||
$out = &backquote_logged("$config{'fetchmail_path'} -d $in{'interval'} -f $config{'config_file'} $mda 2>&1");
|
||||
$out = &backquote_logged("$config{'fetchmail_path'} -d $qinterval -f $qconfig_file $mda 2>&1");
|
||||
}
|
||||
else {
|
||||
$out = &backquote_logged("su - '$config{'daemon_user'}' -c '$config{'fetchmail_path'} -d $in{'interval'} -f $config{'config_file'} $mda' 2>&1");
|
||||
my $qdaemon_user = quotemeta($config{'daemon_user'});
|
||||
my $daemon_cmd = "$config{'fetchmail_path'} -d $qinterval -f $qconfig_file $mda";
|
||||
$out = &backquote_logged("su - $qdaemon_user -c ".quotemeta($daemon_cmd)." 2>&1");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$out = &backquote_logged("$config{'fetchmail_path'} -d $in{'interval'} $mda 2>&1");
|
||||
$out = &backquote_logged("$config{'fetchmail_path'} -d $qinterval $mda 2>&1");
|
||||
}
|
||||
}
|
||||
if ($?) {
|
||||
|
||||
@@ -16,7 +16,8 @@ elsif ($< == 0) {
|
||||
$out = &backquote_logged("$config{'fetchmail_path'} -q 2>&1");
|
||||
}
|
||||
else {
|
||||
$out = &backquote_logged("su - '$config{'daemon_user'}' -c '$config{'fetchmail_path'} -q' 2>&1");
|
||||
my $qdaemon_user = quotemeta($config{'daemon_user'});
|
||||
$out = &backquote_logged("su - $qdaemon_user -c ".quotemeta("$config{'fetchmail_path'} -q")." 2>&1");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -7,8 +7,9 @@ $access{'view'} && &error($text{'ecannot'});
|
||||
&ReadParse();
|
||||
&can_edit_disk($in{'dev'}) || &error($text{'fsck_ecannot'});
|
||||
&ui_print_header(undef, $text{'fsck_title'}, "");
|
||||
$in{'mode'} =~ /^-(m|n|y)$/ || &error($text{'fsck_ecannot'});
|
||||
$in{dev} =~ s/dsk/rdsk/g;
|
||||
$cmd = "fsck -F ufs $in{mode} $in{dev}";
|
||||
$cmd = "fsck -F ufs $in{mode} ".quotemeta($in{dev});
|
||||
|
||||
print &text('fsck_exec', "<tt>$cmd</tt>"),"<p>\n";
|
||||
print "<pre>\n";
|
||||
|
||||
@@ -16,13 +16,15 @@ $cmd .= &opt_check("ufs_f", '\d+', "-f");
|
||||
$cmd .= &opt_check("ufs_i", '\d+', "-i");
|
||||
$cmd .= &opt_check("ufs_m", '\d+', "-m");
|
||||
$cmd .= &opt_check("ufs_n", '\d+', "-n");
|
||||
$in{ufs_o} =~ /^(space|time)$/ || !$in{ufs_o} ||
|
||||
&error($text{'newfs_ecannot'});
|
||||
$cmd .= $in{ufs_o} ? " -o $in{ufs_o}" : "";
|
||||
$cmd .= &opt_check("ufs_r", '\d+', "-r");
|
||||
$cmd .= &opt_check("ufs_s", '\d+', "-s");
|
||||
$cmd .= &opt_check("ufs_t", '\d+', "-t");
|
||||
$cmd .= &opt_check("ufs_cb", '\d+', "-C");
|
||||
$in{dev} =~ s/dsk/rdsk/g;
|
||||
$cmd .= " $in{dev}";
|
||||
$cmd .= " ".quotemeta($in{dev});
|
||||
|
||||
&ui_print_header(undef, $text{'newfs_title'}, "");
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ $cmd .= &opt_check("tunefs_a", '\d+', "-a");
|
||||
$cmd .= &opt_check("tunefs_d", '\d+', "-d");
|
||||
$cmd .= &opt_check("tunefs_e", '\d+', "-e");
|
||||
$cmd .= &opt_check("tunefs_m", '\d+', "-m");
|
||||
$in{tunefs_o} =~ /^(space|time)$/ || !$in{tunefs_o} ||
|
||||
&error($text{'tunefs_ecannot'});
|
||||
$cmd .= $in{tunefs_o} ? " -o $in{tunefs_o}" : "";
|
||||
$in{dev} =~ s/dsk/rdsk/g;
|
||||
$cmd .= " $in{dev}";
|
||||
$cmd .= " ".quotemeta($in{dev});
|
||||
|
||||
&ui_print_header(undef, $text{'tunefs_title'}, "");
|
||||
|
||||
|
||||
@@ -132,7 +132,6 @@ if ($out && $dump->{'email'} && &foreign_check("mailboxes")) {
|
||||
# Check for any dumps scheduled to run after this one
|
||||
foreach $follow (&list_dumps()) {
|
||||
if ($follow->{'follow'} eq $dump->{'id'} && $follow->{'enabled'} == 2) {
|
||||
system("$cron_cmd $follow->{'id'}");
|
||||
system("$cron_cmd ".quotemeta($follow->{'id'}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,14 +177,15 @@ local $fh = $_[1];
|
||||
local ($cmd, $flags);
|
||||
|
||||
if ($_[0]->{'huser'}) {
|
||||
$flags = "-f '$_[0]->{'huser'}\@$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4])."'";
|
||||
$flags = "-f ".quotemeta("$_[0]->{'huser'}\@$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4]));
|
||||
}
|
||||
elsif ($_[0]->{'host'}) {
|
||||
$flags = "-f '$_[0]->{'host'}:".&date_subs($_[0]->{'hfile'}, $_[4])."'";
|
||||
$flags = "-f ".quotemeta("$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4]));
|
||||
}
|
||||
else {
|
||||
$flags = "-f '".&date_subs($_[0]->{'file'}, $_[4])."'";
|
||||
$flags = "-f ".quotemeta(&date_subs($_[0]->{'file'}, $_[4]));
|
||||
}
|
||||
local $tapecmd = $_[0]->{'multi'} && $_[0]->{'fs'} eq 'tar' ? $multi_cmd :
|
||||
$_[0]->{'multi'} ? undef :
|
||||
@@ -192,16 +193,16 @@ local $tapecmd = $_[0]->{'multi'} && $_[0]->{'fs'} eq 'tar' ? $multi_cmd :
|
||||
if ($_[0]->{'fs'} eq 'tar') {
|
||||
# Construct tar command
|
||||
$cmd = "$tar_command -c $flags";
|
||||
$cmd .= " -V '$_[0]->{'label'}'" if ($_[0]->{'label'});
|
||||
$cmd .= " -V ".quotemeta($_[0]->{'label'}) if ($_[0]->{'label'});
|
||||
$cmd .= " -L $_[0]->{'blocks'}" if ($_[0]->{'blocks'});
|
||||
$cmd .= " -z" if ($_[0]->{'gzip'});
|
||||
$cmd .= " -M" if ($_[0]->{'multi'});
|
||||
$cmd .= " -h" if ($_[0]->{'links'});
|
||||
$cmd .= " -l" if ($_[0]->{'xdev'});
|
||||
$cmd .= " -F \"$tapecmd $_[0]->{'id'}\""
|
||||
$cmd .= " -F ".quotemeta("$tapecmd $_[0]->{'id'}")
|
||||
if (!$_[0]->{'gzip'} && ($_[0]->{'file'} =~ /^\/dev/ ||
|
||||
$_[0]->{'hfile'} =~ /^\/dev/));
|
||||
$cmd .= " --rsh-command=$_[0]->{'rsh'}"
|
||||
$cmd .= " --rsh-command=".quotemeta($_[0]->{'rsh'})
|
||||
if ($_[0]->{'rsh'} && $_[0]->{'host'});
|
||||
$cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
|
||||
$cmd .= " ".quotemeta(&date_subs($_[0]->{'dir'}));
|
||||
@@ -227,7 +228,8 @@ $ENV{'DUMP_PASSWORD'} = $_[0]->{'pass'};
|
||||
local $got = &run_ssh_command($cmd, $fh, $_[2], $_[0]->{'pass'});
|
||||
if ($_[0]->{'multi'} && $_[0]->{'fs'} eq 'tar') {
|
||||
# Run multi-file switch command one last time
|
||||
&execute_command("$multi_cmd $_[0]->{'id'} >/dev/null 2>&1");
|
||||
&execute_command("$multi_cmd ".quotemeta($_[0]->{'id'}).
|
||||
" >/dev/null 2>&1");
|
||||
}
|
||||
return $got ? 0 : 1;
|
||||
}
|
||||
@@ -347,7 +349,7 @@ else {
|
||||
}
|
||||
if ($in{'mode'} == 0) {
|
||||
$in{'file'} || &error($text{'restore_efile'});
|
||||
$cmd .= " -f '$in{'file'}'";
|
||||
$cmd .= " -f ".quotemeta($in{'file'});
|
||||
}
|
||||
else {
|
||||
&to_ipaddress($in{'host'}) ||
|
||||
@@ -356,10 +358,10 @@ else {
|
||||
$in{'huser'} =~ /^\S*$/ || &error($text{'restore_ehuser'});
|
||||
$in{'hfile'} || &error($text{'restore_ehfile'});
|
||||
if ($in{'huser'}) {
|
||||
$cmd .= " -f '$in{'huser'}\@$in{'host'}:$in{'hfile'}'";
|
||||
$cmd .= " -f ".quotemeta("$in{'huser'}\@$in{'host'}:$in{'hfile'}");
|
||||
}
|
||||
else {
|
||||
$cmd .= " -f '$in{'host'}:$in{'hfile'}'";
|
||||
$cmd .= " -f ".quotemeta("$in{'host'}:$in{'hfile'}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +374,7 @@ if ($_[0] eq 'tar') {
|
||||
!-c $in{'file'} && !-b $in{'file'} ||
|
||||
&error($text{'restore_emulti'});
|
||||
$in{'mode'} == 0 || &error($text{'restore_emulti2'});
|
||||
$cmd .= " -M -F \"$rmulti_cmd $in{'file'}\"";
|
||||
$cmd .= " -M -F ".quotemeta("$rmulti_cmd $in{'file'}");
|
||||
}
|
||||
local $rsh = &rsh_command_parse("rsh_def", "rsh");
|
||||
if ($rsh) {
|
||||
@@ -384,9 +386,9 @@ if ($_[0] eq 'tar') {
|
||||
$cmd .= " $in{'files'}";
|
||||
}
|
||||
-d $in{'dir'} || &error($text{'restore_edir'});
|
||||
$cmd = "cd '$in{'dir'}' && $cmd";
|
||||
$cmd = "cd ".quotemeta($in{'dir'})." && $cmd";
|
||||
if ($in{'multi'}) {
|
||||
$cmd = "$rmulti_cmd $in{'file'} 1 && $cmd";
|
||||
$cmd = "$rmulti_cmd ".quotemeta($in{'file'})." 1 && $cmd";
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -412,7 +414,8 @@ $ENV{'DUMP_PASSWORD'} = $in{'pass'};
|
||||
|
||||
# Need to supply prompts
|
||||
&foreign_require("proc", "proc-lib.pl");
|
||||
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", "cd '$in{'dir'}' ; $_[1]");
|
||||
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec",
|
||||
"cd ".quotemeta($in{'dir'})." ; $_[1]");
|
||||
local $donevolume;
|
||||
while(1) {
|
||||
local $rv = &wait_for($fh, "(next volume #)", "(set owner.mode for.*\\[yn\\])", "((.*)\\[yn\\])", "password:", "yes\\/no", "(.*\\n)");
|
||||
|
||||
@@ -121,28 +121,29 @@ sub execute_dump
|
||||
local $fh = $_[1];
|
||||
local ($cmd, $flag);
|
||||
if ($_[0]->{'huser'}) {
|
||||
$flag = " -f '$_[0]->{'huser'}\@$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4])."'";
|
||||
$flag = " -f ".quotemeta("$_[0]->{'huser'}\@$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4]));
|
||||
}
|
||||
elsif ($_[0]->{'host'}) {
|
||||
$flag = " -f '$_[0]->{'host'}:".&date_subs($_[0]->{'hfile'}, $_[4])."'";
|
||||
$flag = " -f ".quotemeta("$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4]));
|
||||
}
|
||||
else {
|
||||
$flag = " -f '".&date_subs($_[0]->{'file'}, $_[4])."'";
|
||||
$flag = " -f ".quotemeta(&date_subs($_[0]->{'file'}, $_[4]));
|
||||
}
|
||||
$cmd = "xfsdump -l $_[0]->{'level'}";
|
||||
$cmd .= $flag;
|
||||
$cmd .= " -L '$_[0]->{'label'}'" if ($_[0]->{'label'});
|
||||
$cmd .= " -M '$_[0]->{'label'}'" if ($_[0]->{'label'});
|
||||
$cmd .= " -z '$_[0]->{'max'}'" if ($_[0]->{'max'});
|
||||
$cmd .= " -L ".quotemeta($_[0]->{'label'}) if ($_[0]->{'label'});
|
||||
$cmd .= " -M ".quotemeta($_[0]->{'label'}) if ($_[0]->{'label'});
|
||||
$cmd .= " -z ".quotemeta($_[0]->{'max'}) if ($_[0]->{'max'});
|
||||
$cmd .= " -A" if ($_[0]->{'noattribs'});
|
||||
$cmd .= " -F" if ($_[0]->{'over'});
|
||||
$cmd .= " -J" if ($_[0]->{'noinvent'});
|
||||
$cmd .= " -o" if ($_[0]->{'overwrite'});
|
||||
$cmd .= " -c \"$_[3] $_[0]->{'id'}\"" if ($_[3]);
|
||||
$cmd .= " -c ".quotemeta("$_[3] $_[0]->{'id'}") if ($_[3]);
|
||||
$cmd .= " -E -F" if ($_[0]->{'erase'});
|
||||
$cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
|
||||
$cmd .= " '".&date_subs($_[0]->{'dir'})."'";
|
||||
$cmd .= " ".quotemeta(&date_subs($_[0]->{'dir'}));
|
||||
|
||||
&system_logged("sync");
|
||||
sleep(1);
|
||||
@@ -229,7 +230,7 @@ local $cmd = "xfsrestore";
|
||||
$cmd .= " -t" if ($in{'test'});
|
||||
if ($in{'mode'} == 0) {
|
||||
$in{'file'} || &error($text{'restore_efile'});
|
||||
$cmd .= " -f '$in{'file'}'";
|
||||
$cmd .= " -f ".quotemeta($in{'file'});
|
||||
}
|
||||
else {
|
||||
&to_ipaddress($in{'host'}) ||
|
||||
@@ -238,21 +239,21 @@ else {
|
||||
$in{'huser'} =~ /^\S*$/ || &error($text{'restore_ehuser'});
|
||||
$in{'hfile'} || &error($text{'restore_ehfile'});
|
||||
if ($in{'huser'}) {
|
||||
$cmd .= " -f '$in{'huser'}\@$in{'host'}:$in{'hfile'}'";
|
||||
$cmd .= " -f ".quotemeta("$in{'huser'}\@$in{'host'}:$in{'hfile'}");
|
||||
}
|
||||
else {
|
||||
$cmd .= " -f '$in{'host'}:$in{'hfile'}'";
|
||||
$cmd .= " -f ".quotemeta("$in{'host'}:$in{'hfile'}");
|
||||
}
|
||||
}
|
||||
$cmd .= " -E" if ($in{'over'} == 1);
|
||||
$cmd .= " -e" if ($in{'over'} == 2);
|
||||
$cmd .= " -A" if ($in{'noattribs'});
|
||||
$cmd .= " -L '$in{'label'}'" if ($in{'label'});
|
||||
$cmd .= " -L ".quotemeta($in{'label'}) if ($in{'label'});
|
||||
$cmd .= " -F";
|
||||
$cmd .= " $in{'extra'}" if ($in{'extra'});
|
||||
if (!$in{'test'}) {
|
||||
-d $in{'dir'} || &error($text{'restore_edir'});
|
||||
$cmd .= " '$in{'dir'}'";
|
||||
$cmd .= " ".quotemeta($in{'dir'});
|
||||
}
|
||||
return $cmd;
|
||||
}
|
||||
@@ -274,7 +275,8 @@ if ($_[0] eq 'xfs') {
|
||||
else {
|
||||
# Need to supply prompts
|
||||
&foreign_require("proc", "proc-lib.pl");
|
||||
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", "cd '$in{'dir'}' ; $_[1]");
|
||||
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec",
|
||||
"cd ".quotemeta($in{'dir'})." ; $_[1]");
|
||||
local $donevolume;
|
||||
while(1) {
|
||||
local $rv = &wait_for($fh, "(next volume #)", "(set owner.mode for.*\\[yn\\])", "((.*)\\[yn\\])", "(.*\\n)");
|
||||
|
||||
@@ -343,6 +343,19 @@ else {
|
||||
$_[0]->{'remount'} = $in{'remount'};
|
||||
}
|
||||
|
||||
# split_shell_words(string)
|
||||
# Splits command-line text to shell words while rejecting line breaks
|
||||
sub split_shell_words
|
||||
{
|
||||
my ($str) = @_;
|
||||
return () if (!defined($str) || $str !~ /\S/);
|
||||
my @words = &split_quoted_string($str);
|
||||
foreach my $w (@words) {
|
||||
$w =~ /[\r\n\0]/ && &error("Invalid command-line parameter");
|
||||
}
|
||||
return @words;
|
||||
}
|
||||
|
||||
# execute_dump(&dump, filehandle, escape, background-mode, [time])
|
||||
# Executes a dump and displays the output
|
||||
sub execute_dump
|
||||
@@ -360,7 +373,7 @@ local @dirs = $_[0]->{'tabs'} ? split(/\t+/, $_[0]->{'dir'})
|
||||
if ($_[0]->{'fs'} eq 'tar') {
|
||||
# tar format backup
|
||||
$cmd = "tar ".($_[0]->{'update'} ? "-u" : "-c")." ".$flag;
|
||||
$cmd .= " -V '$_[0]->{'label'}'" if ($_[0]->{'label'});
|
||||
$cmd .= " -V ".quotemeta($_[0]->{'label'}) if ($_[0]->{'label'});
|
||||
$cmd .= " -L $_[0]->{'blocks'}" if ($_[0]->{'blocks'});
|
||||
$cmd .= " -z" if ($_[0]->{'gzip'} == 1);
|
||||
$cmd .= " --bzip" if ($_[0]->{'gzip'} == 2);
|
||||
@@ -368,7 +381,7 @@ if ($_[0]->{'fs'} eq 'tar') {
|
||||
$cmd .= " -M" if ($_[0]->{'multi'});
|
||||
$cmd .= " -h" if ($_[0]->{'links'});
|
||||
$cmd .= " --one-file-system" if ($_[0]->{'xdev'});
|
||||
$cmd .= " -F \"$tapecmd $_[0]->{'id'}\""
|
||||
$cmd .= " -F ".quotemeta("$tapecmd $_[0]->{'id'}")
|
||||
if (!$_[0]->{'gzip'} && $tapecmd);
|
||||
$cmd .= " --rsh-command=".quotemeta($_[0]->{'rsh'})
|
||||
if ($_[0]->{'rsh'} && $_[0]->{'host'});
|
||||
@@ -381,24 +394,26 @@ if ($_[0]->{'fs'} eq 'tar') {
|
||||
$cmd .= " --exclude ".quotemeta($e);
|
||||
}
|
||||
}
|
||||
$cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
|
||||
$cmd .= " ".join(" ", map { "'$_'" } @dirs);
|
||||
my @extra = &split_shell_words($_[0]->{'extra'});
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @extra) if (@extra);
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @dirs);
|
||||
}
|
||||
elsif ($_[0]->{'fs'} eq 'xfs') {
|
||||
# xfs backup
|
||||
$cmd = "xfsdump -l $_[0]->{'level'}";
|
||||
$cmd .= $flag;
|
||||
$cmd .= " -L '$_[0]->{'label'}'" if ($_[0]->{'label'});
|
||||
$cmd .= " -M '$_[0]->{'label'}'" if ($_[0]->{'label'});
|
||||
$cmd .= " -z '$_[0]->{'max'}'" if ($_[0]->{'max'});
|
||||
$cmd .= " -L ".quotemeta($_[0]->{'label'}) if ($_[0]->{'label'});
|
||||
$cmd .= " -M ".quotemeta($_[0]->{'label'}) if ($_[0]->{'label'});
|
||||
$cmd .= " -z ".quotemeta($_[0]->{'max'}) if ($_[0]->{'max'});
|
||||
$cmd .= " -A" if ($_[0]->{'noattribs'});
|
||||
$cmd .= " -F" if ($_[0]->{'over'});
|
||||
$cmd .= " -J" if ($_[0]->{'noinvent'});
|
||||
$cmd .= " -o" if ($_[0]->{'overwrite'});
|
||||
$cmd .= " -E -F" if ($_[0]->{'erase'});
|
||||
$cmd .= " -b $_[0]->{'bsize'}" if ($_[0]->{'bsize'});
|
||||
$cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
|
||||
$cmd .= " ".join(" ", map { "'$_'" } @dirs);
|
||||
my @extra = &split_shell_words($_[0]->{'extra'});
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @extra) if (@extra);
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @dirs);
|
||||
}
|
||||
else {
|
||||
# ext2/3 backup
|
||||
@@ -406,19 +421,21 @@ else {
|
||||
$cmd .= $flag;
|
||||
$cmd .= " -u" if ($_[0]->{'update'});
|
||||
$cmd .= " -M" if ($_[0]->{'multi'});
|
||||
$cmd .= " -L '$_[0]->{'label'}'" if ($_[0]->{'label'});
|
||||
$cmd .= " -L ".quotemeta($_[0]->{'label'}) if ($_[0]->{'label'});
|
||||
$cmd .= " -B $_[0]->{'blocks'}" if ($_[0]->{'blocks'});
|
||||
$cmd .= " -b $_[0]->{'bsize'}" if ($_[0]->{'bsize'});
|
||||
$cmd .= " -h0" if ($_[0]->{'honour'});
|
||||
$cmd .= " -j$_[0]->{'comp'}" if ($_[0]->{'comp'});
|
||||
$cmd .= " -F \"$tapecmd $_[0]->{'id'}\"" if ($tapecmd);
|
||||
$cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
|
||||
$cmd .= " '$_[0]->{'dir'}'";
|
||||
$cmd .= " -F ".quotemeta("$tapecmd $_[0]->{'id'}") if ($tapecmd);
|
||||
my @extra = &split_shell_words($_[0]->{'extra'});
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @extra) if (@extra);
|
||||
$cmd .= " ".quotemeta($_[0]->{'dir'});
|
||||
if ($_[0]->{'rsh'}) {
|
||||
$cmd = "RSH=\"$_[0]->{'rsh'}\" RMT=\"touch '$hfile'; /etc/rmt\" $cmd";
|
||||
$cmd = "RSH=".quotemeta($_[0]->{'rsh'})." ".
|
||||
"RMT=\"touch ".quotemeta($hfile)."; /etc/rmt\" $cmd";
|
||||
}
|
||||
else {
|
||||
$cmd = "RMT=\"touch '$hfile'; /etc/rmt\" $cmd";
|
||||
$cmd = "RMT=\"touch ".quotemeta($hfile)."; /etc/rmt\" $cmd";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,7 +461,8 @@ $ENV{'DUMP_PASSWORD'} = $_[0]->{'pass'};
|
||||
local $got = &run_ssh_command($cmd, $fh, $_[2], $_[0]->{'pass'});
|
||||
if ($_[0]->{'multi'} && $_[0]->{'fs'} eq 'tar') {
|
||||
# Run multi-file switch command one last time
|
||||
&execute_command("$multi_cmd $_[0]->{'id'} >/dev/null 2>&1");
|
||||
&execute_command("$multi_cmd ".quotemeta($_[0]->{'id'}).
|
||||
" >/dev/null 2>&1");
|
||||
}
|
||||
|
||||
# Remount with atime option
|
||||
@@ -467,14 +485,14 @@ sub dump_flag
|
||||
local ($flag, $hfile);
|
||||
if ($_[0]->{'huser'}) {
|
||||
$hfile = &date_subs($_[0]->{'hfile'}, $_[1]);
|
||||
$flag = " -f '$_[0]->{'huser'}\@$_[0]->{'host'}:$hfile'";
|
||||
$flag = " -f ".quotemeta("$_[0]->{'huser'}\@$_[0]->{'host'}:$hfile");
|
||||
}
|
||||
elsif ($_[0]->{'host'}) {
|
||||
$hfile = &date_subs($_[0]->{'hfile'}, $_[1]);
|
||||
$flag = " -f '$_[0]->{'host'}:$hfile'";
|
||||
$flag = " -f ".quotemeta("$_[0]->{'host'}:$hfile");
|
||||
}
|
||||
else {
|
||||
$flag = " -f '".&date_subs($_[0]->{'file'}, $_[1])."'";
|
||||
$flag = " -f ".quotemeta(&date_subs($_[0]->{'file'}, $_[1]));
|
||||
}
|
||||
return ($flag, $hfile);
|
||||
}
|
||||
@@ -503,15 +521,16 @@ else {
|
||||
}
|
||||
$vcmd .= $flag;
|
||||
if ($_[0]->{'fs'} eq "tar") {
|
||||
$vcmd .= " --rsh-command=$_[0]->{'rsh'}"
|
||||
$vcmd .= " --rsh-command=".quotemeta($_[0]->{'rsh'})
|
||||
if ($_[0]->{'rsh'} && $_[0]->{'host'});
|
||||
}
|
||||
elsif ($_[0]->{'fs'} ne "xfs") {
|
||||
if ($_[0]->{'rsh'}) {
|
||||
$vcmd = "RSH=\"$_[0]->{'rsh'}\" RMT=\"touch '$hfile'; /etc/rmt\" $vcmd";
|
||||
$vcmd = "RSH=".quotemeta($_[0]->{'rsh'})." ".
|
||||
"RMT=\"touch ".quotemeta($hfile)."; /etc/rmt\" $vcmd";
|
||||
}
|
||||
else {
|
||||
$vcmd = "RMT=\"touch '$hfile'; /etc/rmt\" $vcmd";
|
||||
$vcmd = "RMT=\"touch ".quotemeta($hfile)."; /etc/rmt\" $vcmd";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,7 +699,7 @@ else {
|
||||
}
|
||||
if ($in{'mode'} == 0) {
|
||||
$in{'file'} || &error($text{'restore_efile'});
|
||||
$cmd .= " -f '$in{'file'}'";
|
||||
$cmd .= " -f ".quotemeta($in{'file'});
|
||||
}
|
||||
else {
|
||||
&to_ipaddress($in{'host'}) ||
|
||||
@@ -689,10 +708,10 @@ else {
|
||||
$in{'huser'} =~ /^\S*$/ || &error($text{'restore_ehuser'});
|
||||
$in{'hfile'} || &error($text{'restore_ehfile'});
|
||||
if ($in{'huser'}) {
|
||||
$cmd .= " -f '$in{'huser'}\@$in{'host'}:$in{'hfile'}'";
|
||||
$cmd .= " -f ".quotemeta("$in{'huser'}\@$in{'host'}:$in{'hfile'}");
|
||||
}
|
||||
else {
|
||||
$cmd .= " -f '$in{'host'}:$in{'hfile'}'";
|
||||
$cmd .= " -f ".quotemeta("$in{'host'}:$in{'hfile'}");
|
||||
}
|
||||
}
|
||||
if ($_[0] eq 'tar') {
|
||||
@@ -706,7 +725,7 @@ if ($_[0] eq 'tar') {
|
||||
!-c $in{'file'} && !-b $in{'file'} ||
|
||||
&error($text{'restore_emulti'});
|
||||
$in{'mode'} == 0 || &error($text{'restore_emulti2'});
|
||||
$cmd .= " -M -F \"$rmulti_cmd $in{'file'}\"";
|
||||
$cmd .= " -M -F ".quotemeta("$rmulti_cmd $in{'file'}");
|
||||
}
|
||||
local $rsh = &rsh_command_parse("rsh_def", "rsh");
|
||||
if ($rsh) {
|
||||
@@ -716,15 +735,21 @@ if ($_[0] eq 'tar') {
|
||||
$in{'rmt'} =~ /^\S+$/ || &error($text{'dump_ermt'});
|
||||
$cmd .= " --rmt-command=".quotemeta($in{'rmt'});
|
||||
}
|
||||
$cmd .= " $in{'extra'}" if ($in{'extra'});
|
||||
if ($in{'extra'} && !$access{'extra'}) {
|
||||
&error($text{'restore_ecannot'});
|
||||
}
|
||||
my @extra = &split_shell_words($in{'extra'});
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @extra) if (@extra);
|
||||
if (!$in{'files_def'}) {
|
||||
$in{'files'} || &error($text{'restore_efiles'});
|
||||
$cmd .= " $in{'files'}";
|
||||
my @files = &split_shell_words($in{'files'});
|
||||
@files || &error($text{'restore_efiles'});
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @files);
|
||||
}
|
||||
-d $in{'dir'} || &error($text{'restore_edir'});
|
||||
$cmd = "cd '$in{'dir'}' && $cmd";
|
||||
$cmd = "cd ".quotemeta($in{'dir'})." && $cmd";
|
||||
if ($in{'multi'}) {
|
||||
$cmd = "$rmulti_cmd $in{'file'} 1 && $cmd";
|
||||
$cmd = "$rmulti_cmd ".quotemeta($in{'file'})." 1 && $cmd";
|
||||
}
|
||||
}
|
||||
elsif ($_[0] eq 'xfs') {
|
||||
@@ -732,19 +757,23 @@ elsif ($_[0] eq 'xfs') {
|
||||
$cmd .= " -E" if ($in{'over'} == 1);
|
||||
$cmd .= " -e" if ($in{'over'} == 2);
|
||||
$cmd .= " -A" if ($in{'noattribs'});
|
||||
$cmd .= " -L '$in{'label'}'" if ($in{'label'});
|
||||
$cmd .= " -L ".quotemeta($in{'label'}) if ($in{'label'});
|
||||
$cmd .= " -F";
|
||||
$cmd .= " $in{'extra'}" if ($in{'extra'});
|
||||
if ($in{'extra'} && !$access{'extra'}) {
|
||||
&error($text{'restore_ecannot'});
|
||||
}
|
||||
my @extra = &split_shell_words($in{'extra'});
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @extra) if (@extra);
|
||||
if (!$in{'test'}) {
|
||||
-d $in{'dir'} || &error($text{'restore_edir'});
|
||||
$cmd .= " '$in{'dir'}'";
|
||||
$cmd .= " ".quotemeta($in{'dir'});
|
||||
}
|
||||
}
|
||||
else {
|
||||
# parse ext2/3 options
|
||||
local $rsh = &rsh_command_parse("rsh_def", "rsh");
|
||||
if ($rsh) {
|
||||
$cmd = "RSH=\"$rsh\" $cmd";
|
||||
$cmd = "RSH=".quotemeta($rsh)." $cmd";
|
||||
}
|
||||
|
||||
if ($in{'multi'}) {
|
||||
@@ -754,10 +783,16 @@ else {
|
||||
}
|
||||
}
|
||||
$cmd .= " -u"; # force overwrite
|
||||
$cmd .= " $in{'extra'}" if ($in{'extra'});
|
||||
if ($in{'extra'} && !$access{'extra'}) {
|
||||
&error($text{'restore_ecannot'});
|
||||
}
|
||||
my @extra = &split_shell_words($in{'extra'});
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @extra) if (@extra);
|
||||
if (!$in{'files_def'}) {
|
||||
$in{'files'} || &error($text{'restore_efiles'});
|
||||
$cmd .= " $in{'files'}";
|
||||
my @files = &split_shell_words($in{'files'});
|
||||
@files || &error($text{'restore_efiles'});
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @files);
|
||||
}
|
||||
-d $in{'dir'} || &error($text{'restore_edir'});
|
||||
}
|
||||
@@ -782,7 +817,8 @@ if ($_[0] eq 'xfs') {
|
||||
else {
|
||||
# Need to supply prompts
|
||||
&foreign_require("proc", "proc-lib.pl");
|
||||
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", "cd '$in{'dir'}' ; $_[1]");
|
||||
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec",
|
||||
"cd ".quotemeta($in{'dir'})." ; $_[1]");
|
||||
local $donevolume;
|
||||
while(1) {
|
||||
local $rv = &wait_for($fh, "(.*next volume #)", "(.*set owner.mode for.*\\[yn\\])", "((.*)\\[yn\\])", "(.*enter volume name)", "password:", "yes\\/no", "(.*\\n)");
|
||||
|
||||
@@ -75,6 +75,10 @@ else {
|
||||
$dump->{'email'} = $in{'email_def'} ? '*' : $in{'email'};
|
||||
$dump->{'subject'} = $in{'subject_def'} ? undef : $in{'subject'};
|
||||
if ($access{'extra'}) {
|
||||
if (defined($in{'extra'}) &&
|
||||
$in{'extra'} =~ /[;&|`\$<>\r\n\0]/) {
|
||||
&error("Invalid extra command-line parameters");
|
||||
}
|
||||
$dump->{'extra'} = $in{'extra'};
|
||||
}
|
||||
if ($access{'cmds'}) {
|
||||
|
||||
@@ -92,20 +92,21 @@ $cmd .= "u" if ($_[0]->{'update'});
|
||||
$cmd .= "v" if ($_[0]->{'verify'});
|
||||
$cmd .= "o" if ($_[0]->{'offline'});
|
||||
if ($_[0]->{'huser'}) {
|
||||
$cmd .= "f '$_[0]->{'huser'}\@$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4])."'";
|
||||
$cmd .= "f ".quotemeta("$_[0]->{'huser'}\@$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4]));
|
||||
}
|
||||
elsif ($_[0]->{'host'}) {
|
||||
$cmd .= "f '$_[0]->{'host'}:".&date_subs($_[0]->{'hfile'}, $_[4])."'";
|
||||
$cmd .= "f ".quotemeta("$_[0]->{'host'}:".
|
||||
&date_subs($_[0]->{'hfile'}, $_[4]));
|
||||
}
|
||||
else {
|
||||
$cmd .= "f '".&date_subs($_[0]->{'file'}, $_[4])."'";
|
||||
$cmd .= "f ".quotemeta(&date_subs($_[0]->{'file'}, $_[4]));
|
||||
}
|
||||
$cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
|
||||
local @dirs = $_[0]->{'tabs'} ? split(/\t+/, $_[0]->{'dir'})
|
||||
: split(/\s+/, $_[0]->{'dir'});
|
||||
@dirs = map { &date_subs($_) } @dirs;
|
||||
$cmd .= " ".join(" ", map { "'$_'" } @dirs);
|
||||
$cmd .= " ".join(" ", map { quotemeta($_) } @dirs);
|
||||
|
||||
&system_logged("sync");
|
||||
sleep(1);
|
||||
@@ -182,7 +183,7 @@ $cmd = "ufsrestore";
|
||||
$cmd .= ($in{'test'} ? " t" : " x");
|
||||
if ($in{'mode'} == 0) {
|
||||
$in{'file'} || &error($text{'restore_efile'});
|
||||
$cmd .= "f '$in{'file'}'";
|
||||
$cmd .= "f ".quotemeta($in{'file'});
|
||||
}
|
||||
else {
|
||||
&to_ipaddress($in{'host'}) ||
|
||||
@@ -191,10 +192,10 @@ else {
|
||||
$in{'huser'} =~ /^\S*$/ || &error($text{'restore_ehuser'});
|
||||
$in{'hfile'} || &error($text{'restore_ehfile'});
|
||||
if ($in{'huser'}) {
|
||||
$cmd .= "f '$in{'huser'}\@$in{'host'}:$in{'hfile'}'";
|
||||
$cmd .= "f ".quotemeta("$in{'huser'}\@$in{'host'}:$in{'hfile'}");
|
||||
}
|
||||
else {
|
||||
$cmd .= "f '$in{'host'}:$in{'hfile'}'";
|
||||
$cmd .= "f ".quotemeta("$in{'host'}:$in{'hfile'}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +216,8 @@ sub restore_backup
|
||||
&additional_log('exec', undef, $_[1]);
|
||||
|
||||
&foreign_require("proc", "proc-lib.pl");
|
||||
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", "cd '$in{'dir'}' ; $_[1]");
|
||||
local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec",
|
||||
"cd ".quotemeta($in{'dir'})." ; $_[1]");
|
||||
local $donevolume;
|
||||
while(1) {
|
||||
local $rv = &wait_for($fh, "(next volume #)", "(set owner.mode for.*\\[yn\\])", "(Directories already exist, set modes anyway. \\[yn\\])", "((.*)\\[yn\\])", "(.*\\n)");
|
||||
|
||||
@@ -893,7 +893,7 @@ body > .mode > b[data-mode="server-manager"] > a > .ff-cloudmin {
|
||||
}
|
||||
.field-sizing-content {
|
||||
field-sizing: content !important;
|
||||
min-width: 40px !important;
|
||||
min-width: 15px !important;
|
||||
}
|
||||
.text-danger {
|
||||
color: #bc0303;
|
||||
|
||||
10
help.cgi
10
help.cgi
@@ -37,16 +37,6 @@ if (&foreign_exists($module) &&
|
||||
}
|
||||
}
|
||||
|
||||
# if any template variables were given as URL params, substitute them into the file
|
||||
foreach my $k (keys %in) {
|
||||
if ($k =~ /^tmpl_(\S+)$/i) {
|
||||
$hash{$1} = $in{$k};
|
||||
}
|
||||
}
|
||||
if (%hash) {
|
||||
$help = &substitute_template($help, \%hash);
|
||||
}
|
||||
|
||||
# find and replace the <header> section
|
||||
if ($help =~ s/<header>([^<]+)<\/header>//i) {
|
||||
&popup_header($1);
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
require './init-lib.pl';
|
||||
&ReadParse();
|
||||
$access{'bootup'} == 1 || &error($text{'change_ecannot'});
|
||||
my %ok_levels = map { $_, 1 } (&list_runlevels(), "S", "s");
|
||||
$ok_levels{$in{'level'}} || &error($text{'change_ecannot'});
|
||||
|
||||
&ui_print_header(undef, $text{'change_title'}, "");
|
||||
|
||||
$cmd = "telinit '$in{'level'}'";
|
||||
$cmd = "telinit ".quotemeta($in{'level'});
|
||||
print "<p>",&text('change_cmd', $in{'level'}, "<tt>$cmd</tt>"),"<p>\n";
|
||||
&system_logged("$cmd </dev/null >/dev/null 2>&1 &");
|
||||
&webmin_log("telinit", $in{'level'});
|
||||
|
||||
@@ -2816,14 +2816,15 @@ foreach my $a (@rv) {
|
||||
return @rv;
|
||||
}
|
||||
|
||||
=head2 create_launchd_agent(name, start-script, boot-flag)
|
||||
=head2 create_launchd_agent(name, start-script, boot-flag, [load-now])
|
||||
|
||||
Creates a new my launchd agent
|
||||
|
||||
=cut
|
||||
sub create_launchd_agent
|
||||
{
|
||||
my ($name, $start, $boot) = @_;
|
||||
my ($name, $start, $boot, $load) = @_;
|
||||
$load = 1 if (!defined($load));
|
||||
my $file = "/Library/LaunchDaemons/".$name.".plist";
|
||||
my $plist = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n".
|
||||
@@ -2846,8 +2847,10 @@ $plist .= "</plist>\n";
|
||||
&open_lock_tempfile(PLIST, ">$file");
|
||||
&print_tempfile(PLIST, $plist);
|
||||
&close_tempfile(PLIST);
|
||||
my $out = &backquote_logged("launchctl load ".quotemeta($file)." 2>&1");
|
||||
&error("Failed to load plist : $out") if ($?);
|
||||
if ($load) {
|
||||
my $out = &backquote_logged("launchctl load ".quotemeta($file)." 2>&1");
|
||||
&error("Failed to load plist : $out") if ($?);
|
||||
}
|
||||
}
|
||||
|
||||
=head2 delete_launchd_agent(name)
|
||||
|
||||
@@ -17,7 +17,25 @@ foreach $a ('start', 'restart', 'condrestart', 'reload', 'status', 'stop') {
|
||||
}
|
||||
$action ||= 'stop';
|
||||
&ui_print_header(undef, $text{'ss_'.$action}, "");
|
||||
$cmd = $in{'file'}." ".$action;
|
||||
|
||||
# Only allow known init action files
|
||||
my %ok_files;
|
||||
foreach my $a (&list_actions()) {
|
||||
my ($name) = split(/\s+/, $a);
|
||||
my $file = $name =~ /^\// ? $name : "$config{'init_dir'}/$name";
|
||||
$ok_files{$file} = 1;
|
||||
}
|
||||
foreach my $rl (&list_runlevels()) {
|
||||
foreach my $w ("S", "K") {
|
||||
foreach my $a (&runlevel_actions($rl, $w)) {
|
||||
my ($order, $name) = split(/\s+/, $a);
|
||||
my $file = "$config{'init_base'}/rc$rl.d/$w$order$name";
|
||||
$ok_files{$file} = 1 if (-r $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
$ok_files{$in{'file'}} || &error($text{'ss_ecannot'});
|
||||
$cmd = quotemeta($in{'file'})." ".quotemeta($action);
|
||||
|
||||
# In case the action was Webmin
|
||||
$SIG{'TERM'} = 'ignore';
|
||||
|
||||
@@ -56,6 +56,27 @@ if ($product) {
|
||||
quotemeta($product).".service >/dev/null 2>&1");
|
||||
}
|
||||
}
|
||||
elsif ($init_mode eq "launchd") {
|
||||
# Update or create launchd agent to use start init wrapper
|
||||
my $name = &launchd_name($product);
|
||||
my %miniserv;
|
||||
&get_miniserv_config(\%miniserv);
|
||||
my $want_boot = $miniserv{'atboot'} ? 1 : 0;
|
||||
my @agents = &list_launchd_agents();
|
||||
my ($agent) = grep { $_->{'name'} eq $name } @agents;
|
||||
if ($agent) {
|
||||
my $boot = $agent->{'boot'} ? 1 : 0;
|
||||
&delete_launchd_agent($name);
|
||||
&create_launchd_agent($name,
|
||||
"$config_directory/.start-init --nofork",
|
||||
$boot, 0);
|
||||
}
|
||||
elsif ($want_boot) {
|
||||
&create_launchd_agent($name,
|
||||
"$config_directory/.start-init --nofork",
|
||||
1, 0);
|
||||
}
|
||||
}
|
||||
elsif (-d "/etc/init.d") {
|
||||
copy_source_dest("$root_directory/webmin-init", "/etc/init.d/$product");
|
||||
system("chkconfig --add $product >/dev/null 2>&1");
|
||||
|
||||
@@ -8,8 +8,15 @@ $| = 1;
|
||||
$theme_no_table++;
|
||||
&ui_print_header(undef, $text{'up_title'}, "");
|
||||
|
||||
# Validate connection name against configured connections
|
||||
my @conf = &get_config();
|
||||
my %ok_conns = map { $_->{'value'}, 1 }
|
||||
grep { $_->{'name'} eq 'conn' && $_->{'value'} ne '%default' }
|
||||
@conf;
|
||||
$ok_conns{$in{'conn'}} || &error($text{'save_ename'});
|
||||
|
||||
# Try to connect
|
||||
$cmd = "$config{'ipsec'} auto --up '$in{'conn'}'";
|
||||
$cmd = "$config{'ipsec'} auto --up ".quotemeta($in{'conn'});
|
||||
print "<b>",&text('up_cmd', "<tt>$cmd</tt>"),"</b>\n";
|
||||
print "<pre>";
|
||||
&foreign_require("proc", "proc-lib.pl");
|
||||
|
||||
8
lang/en
8
lang/en
@@ -475,6 +475,14 @@ time_in_mins=In $1 minutes
|
||||
time_in_sec=In $1 second
|
||||
time_in_secs=In $1 seconds
|
||||
time_now=Just now
|
||||
time_second=second
|
||||
time_seconds=seconds
|
||||
time_minute=minute
|
||||
time_minutes=minutes
|
||||
time_hour=hour
|
||||
time_hours=hours
|
||||
time_day=day
|
||||
time_days=days
|
||||
|
||||
defcert_error=Default $1 bundled SSL certificate is being used. It is highly advised to update default <tt>$2</tt> certificate before proceeding with login.
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ foreach $c (@$conf) {
|
||||
}
|
||||
print TEMP map { "$_\n" } &directive_lines($_[0]);
|
||||
close(TEMP);
|
||||
&set_ownership_permissions(undef, undef, 0700, $temp);
|
||||
&set_ownership_permissions(undef, undef, 0644, $temp);
|
||||
local $out = &backquote_logged("$config{'logrotate'} -f $temp 2>&1");
|
||||
return ($?, $out);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
skip_index=1
|
||||
lines=100
|
||||
include_context=0
|
||||
others=1
|
||||
reverse=1
|
||||
log_any=0
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
skip_index=Open log view on module load?,1,1-Yes,0-No
|
||||
lines=Default number of lines to display,0,6
|
||||
include_context=Context lines around each match,3,None
|
||||
compressed=Include compressed logs in searches?,1,1-Yes,0-No
|
||||
refresh=Seconds between log view refreshes,3,Never
|
||||
others=Show logs from other modules?,1,1-Yes,0-No
|
||||
|
||||
@@ -41,6 +41,8 @@ view_header3=Lines of $1
|
||||
view_empty=Log file is empty
|
||||
view_loading=Log file is being watched .. No new lines yet.
|
||||
view_filter=Filter lines with text $1
|
||||
view_filter_surround=With context
|
||||
view_filter_regex=Use regex
|
||||
view_filter_btn=Filter
|
||||
|
||||
save_efile='$1' is not a valid filename : $2
|
||||
|
||||
@@ -56,6 +56,7 @@ my $fselect = shift;
|
||||
my $lines = $in{'lines'} ? int($in{'lines'}) : int($config{'lines'}) || 1000;
|
||||
my $journalctl_cmd = &has_command('journalctl');
|
||||
return () if (!$journalctl_cmd);
|
||||
my $systemctl_cmd = &has_command('systemctl') || 'systemctl';
|
||||
my $eflags = "";
|
||||
$eflags = " --reverse" if ($config{'reverse'});
|
||||
my $jver = &get_journalctl_version();
|
||||
@@ -89,8 +90,8 @@ my (%ucache, %uread);
|
||||
my $units_cache = "$module_config_directory/units.cache";
|
||||
&read_file($units_cache, \%ucache);
|
||||
if (!%ucache) {
|
||||
my $out = &backquote_command("systemctl list-units --all --no-legend ".
|
||||
"--no-pager");
|
||||
my $out = &backquote_command(quotemeta($systemctl_cmd).
|
||||
" list-units --all --no-legend --no-pager");
|
||||
foreach my $line (split(/\r?\n/, $out)) {
|
||||
$line =~ s/^[^a-z0-9\-\_\.]+//i;
|
||||
my ($unit, $desc) = (split(/\s+/, $line, 5))[0, 4];
|
||||
@@ -104,9 +105,10 @@ if ($fselect) {
|
||||
my %units = %uread ? %uread : %ucache;
|
||||
foreach my $u (sort keys %units) {
|
||||
my $uname = $u;
|
||||
my $qu = quotemeta($u);
|
||||
$uname =~ s/\\x([0-9A-Fa-f]{2})/pack('H2', $1)/eg;
|
||||
push(@rs, { 'cmd' => "$journalctl_cmd --lines ".
|
||||
"$lines --unit $u",
|
||||
"$lines --unit $qu",
|
||||
'desc' => $uname,
|
||||
'id' => "journal-a-$u", });
|
||||
}
|
||||
@@ -287,7 +289,8 @@ else {
|
||||
sub get_journalctl_version
|
||||
{
|
||||
my $bin = &has_command('journalctl');
|
||||
my $out = &backquote_command("\"$bin\" --version 2>&1");
|
||||
return undef if (!$bin);
|
||||
my $out = &backquote_command(quotemeta($bin)." --version 2>&1");
|
||||
if ($out =~ /systemd\s+([0-9]+(?:\.[0-9A-Za-z\-\+]+)*)/) {
|
||||
return $1;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ if ($in{'idx'} ne '') {
|
||||
my @systemctl_cmds = &get_systemctl_cmds(1);
|
||||
my ($log);
|
||||
if ($in{'idx'} eq 'journal-u') {
|
||||
($log) = grep { $_->{'cmd'} =~ /--unit\s+\w+/ }
|
||||
($log) = grep { $_->{'cmd'} =~ /--unit\s+\S+/ }
|
||||
@systemctl_cmds;
|
||||
$in{'idx'} = $log->{'id'};
|
||||
}
|
||||
@@ -117,6 +117,11 @@ print "Refresh: $config{'refresh'}\r\n"
|
||||
my $lines = $in{'lines'} ? int($in{'lines'}) : int($config{'lines'});
|
||||
my $jfilter = $in{'filter'} ? $in{'filter'} : "";
|
||||
my $filter = $jfilter ? quotemeta($jfilter) : "";
|
||||
my $include_surrounding = $in{'surrounding'} ? 1 : 0;
|
||||
my $context_lines = $config{'include_context'} =~ /^\d+$/
|
||||
? int($config{'include_context'}) : 0;
|
||||
my $has_context = $include_surrounding && $context_lines > 0;
|
||||
my $use_regex = $in{'regex'} ? 1 : 0;
|
||||
my $reverse = $config{'reverse'} ? 1 : 0;
|
||||
my $follow = $in{'since'} eq '--follow' ? 1 : 0;
|
||||
my $no_navlinks = $in{'nonavlinks'} == 1 ? 1 : undef;
|
||||
@@ -168,15 +173,18 @@ if (!$follow) {
|
||||
if ($reverse) {
|
||||
$tailcmd .= " | tac" if ($cmd !~ /journalctl/);
|
||||
}
|
||||
$eflag = $gconfig{'os_type'} =~ /-linux/ ? "-E" : "";
|
||||
$dashflag = $gconfig{'os_type'} =~ /-linux/ ? "--" : "";
|
||||
$dashflag = "--";
|
||||
my $grep_mode = $use_regex ? "-E" : "-F";
|
||||
if (@cats) {
|
||||
my $fcmd;
|
||||
my $context_opts = $has_context ? " -C $context_lines" : "";
|
||||
if ($cmd =~ /journalctl/) {
|
||||
$fcmd = "$cmd --grep $filter";
|
||||
$fcmd = "$cmd | grep -a $grep_mode$context_opts ".
|
||||
"$dashflag $filter";
|
||||
}
|
||||
else {
|
||||
$fcmd = "$cat | grep -i -a $eflag $dashflag $filter ".
|
||||
$fcmd = "$cat | grep -i -a $grep_mode$context_opts ".
|
||||
"$dashflag $filter ".
|
||||
"| $tailcmd";
|
||||
}
|
||||
open(my $output_fh, '>', \$safe_proc_out);
|
||||
@@ -254,10 +262,14 @@ else {
|
||||
}
|
||||
// Update log viewer with new data from the server
|
||||
(async function () {
|
||||
const progressUrl =
|
||||
"view_log_progress.cgi?idx=$in{'idx'}&filter=" +
|
||||
"@{[&urlize($jfilter)]}®ex=$use_regex";
|
||||
const logviewer_progress_abort = new AbortController();
|
||||
const logDataElement = document.getElementById("logdata"),
|
||||
response = await fetch("view_log_progress.cgi?idx=$in{'idx'}&filter=$jfilter",
|
||||
{ signal: logviewer_progress_abort.signal }),
|
||||
response = await fetch(
|
||||
progressUrl,
|
||||
{ signal: logviewer_progress_abort.signal }),
|
||||
reader = response.body.getReader(),
|
||||
decoder = new TextDecoder("utf-8"),
|
||||
processText = async function () {
|
||||
@@ -417,13 +429,30 @@ if ($follow) {
|
||||
print &text('view_header3', " $sel"),"\n";
|
||||
}
|
||||
else {
|
||||
print &text($text_view_header, " " . &ui_textbox("lines", $lines, 3), " $sel"),"\n";
|
||||
print &text(
|
||||
$text_view_header,
|
||||
" " . &ui_textbox("lines", $lines, 3),
|
||||
" $sel"),"\n";
|
||||
}
|
||||
print " \n";
|
||||
print &text('view_filter', " " . &ui_textbox("filter", $in{'filter'}, 12)),"\n";
|
||||
|
||||
print &text(
|
||||
'view_filter',
|
||||
" " . &ui_textbox("filter", $in{'filter'}, 15)),"\n";
|
||||
print " \n";
|
||||
print &ui_submit($text{'view_filter_btn'});
|
||||
print " \n";
|
||||
print &ui_tag(
|
||||
'span',
|
||||
&ui_checkbox("regex", 1,
|
||||
$text{'view_filter_regex'}, $use_regex),
|
||||
{ style => "vertical-align: middle; margin-left: 2px;" });
|
||||
if ($context_lines > 0 && !$follow) {
|
||||
print " \n";
|
||||
print &ui_tag(
|
||||
'span',
|
||||
&ui_checkbox("surrounding", 1,
|
||||
$text{'view_filter_surround'}, $include_surrounding),
|
||||
{ style => "vertical-align: middle;" });
|
||||
}
|
||||
print &ui_form_end(),"<br>\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -36,13 +36,19 @@ $log->{'cmd'} .= " --follow";
|
||||
|
||||
# Add filter to the command if present
|
||||
my $filter = $in{'filter'} ? quotemeta($in{'filter'}) : "";
|
||||
my $use_regex = $in{'regex'} ? 1 : 0;
|
||||
my $readcmd = $log->{'cmd'};
|
||||
if ($filter) {
|
||||
$log->{'cmd'} .= " --grep $filter";
|
||||
my $grep_flag = $use_regex ? "-E" : "-F";
|
||||
$readcmd .= " | grep --line-buffered -a $grep_flag -- $filter";
|
||||
}
|
||||
|
||||
# Open a pipe to the journalctl command
|
||||
my $pid = open(my $fh, '-|', $log->{'cmd'}) ||
|
||||
print &text('save_ecannot4', $log->{'cmd'}).": $!";
|
||||
my $pid = open(my $fh, '-|', $readcmd);
|
||||
if (!defined($pid)) {
|
||||
print &text('save_ecannot4', $readcmd).": $!";
|
||||
exit;
|
||||
}
|
||||
|
||||
# Read and output the log
|
||||
while (my $line = <$fh>) {
|
||||
|
||||
@@ -51,6 +51,7 @@ spam_report=Report spam using,1,sa_learn-<tt>sa-learn --spam</tt>,spamassass
|
||||
line3.5=From address options,11
|
||||
from_addr=<tt>From:</tt> address to use when sending email manually,3,From mailbox username
|
||||
webmin_from=<tt>From:</tt> address to use when Webmin sends email,3,Default (<tt>webmin-noreply@<i>yourhost</i></tt>)
|
||||
webmin_from_name=Real name to use in From: address,3,None
|
||||
from_virtualmin=Get <tt>From:</tt> address from Virtualmin?,1,1-Yes,0-No
|
||||
from_dom=Domain to use in <tt>From:</tt> address,3,System hostname
|
||||
no_orig_ip=Include browser IP in <tt>X-Originating</tt>-IP header?,1,0-Yes,1-No
|
||||
|
||||
@@ -600,16 +600,21 @@ return &ui_link("list_mail.cgi?user=$_[0]&folder=$_[1]->{'index'}",$text{'mail_r
|
||||
# Returns the address to use when sending email from a script
|
||||
sub get_from_address
|
||||
{
|
||||
local $host = &get_from_domain();
|
||||
my $host = &get_from_domain();
|
||||
my $rv;
|
||||
if ($config{'webmin_from'} =~ /\@/) {
|
||||
return $config{'webmin_from'};
|
||||
$rv = $config{'webmin_from'};
|
||||
}
|
||||
elsif (!$config{'webmin_from'}) {
|
||||
return "webmin-noreply\@$host";
|
||||
$rv = "webmin-noreply\@$host";
|
||||
}
|
||||
else {
|
||||
return "$config{'webmin_from'}\@$host";
|
||||
$rv = "$config{'webmin_from'}\@$host";
|
||||
}
|
||||
if ($config{'webmin_from_name'}) {
|
||||
$rv = "\"$config{'webmin_from_name'}\" <$rv>";
|
||||
}
|
||||
return $rv;
|
||||
}
|
||||
|
||||
# get_from_domain()
|
||||
|
||||
@@ -22,7 +22,7 @@ sub list_mailcap
|
||||
{
|
||||
if (!scalar(@list_mailcap_cache)) {
|
||||
@list_mailcap_cache = ( );
|
||||
open(CAP, "<".$mailcap_file);
|
||||
open(CAP, "<", $mailcap_file);
|
||||
local $lnum = 0;
|
||||
while(<CAP>) {
|
||||
local ($slnum, $elnum) = ($lnum, $lnum);
|
||||
|
||||
@@ -109,7 +109,7 @@ if ($product eq "webmin") {
|
||||
$size = int(`du -sk $tmp_dir`);
|
||||
@deps = ( "perl", "libnet-ssleay-perl", "openssl", "libauthen-pam-perl", "libpam-runtime", "libio-pty-perl", "unzip", "shared-mime-info", "tar", "libdigest-sha-perl", "libdigest-md5-perl", "gzip" );
|
||||
$deps = join(", ", @deps);
|
||||
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "html2text", "qrencode", "libdbi-perl", "libdbd-mysql-perl", "libjson-xs-perl", "libsys-syslog-perl" );
|
||||
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "html2text", "qrencode", "libdbi-perl", "libdbd-mysql-perl", "libdbd-mariadb-perl", "libjson-xs-perl", "libsys-syslog-perl" );
|
||||
$recommends = join(", ", @recommends);
|
||||
open(CONTROL, ">$control_file");
|
||||
print CONTROL <<EOF;
|
||||
|
||||
@@ -287,4 +287,3 @@ sub usage
|
||||
{
|
||||
die "Usage: $0 [--minimal] [--mod-list type] <version>\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ if ($< == 0) {
|
||||
system("cd $usr_dir && chown -R root:bin .");
|
||||
}
|
||||
system("find $usr_dir -name .git | xargs rm -rf");
|
||||
system("find $usr_dir -name .github | xargs rm -rf");
|
||||
system("find $usr_dir -name RELEASE | xargs rm -rf");
|
||||
system("find $usr_dir -name RELEASE.sh | xargs rm -rf");
|
||||
if (-r "$usr_dir/$mod/EXCLUDE") {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user