mirror of
https://github.com/webmin/webmin.git
synced 2026-08-21 14:30:41 +01:00
Compare commits
317 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce1ab74c6f | ||
|
|
77d817357d | ||
|
|
350908ed56 | ||
|
|
94ac2ff7d7 | ||
|
|
a24e5371b7 | ||
|
|
0810083588 | ||
|
|
2ec6cadbbd | ||
|
|
241abfe719 | ||
|
|
f78413549b | ||
|
|
b53cce9084 | ||
|
|
a92b52f502 | ||
|
|
c0c0cc323c | ||
|
|
da49c5b9b8 | ||
|
|
5b5c3cc4b1 | ||
|
|
f0b7f10b64 | ||
|
|
bdad9fd173 | ||
|
|
7e01b3eb44 | ||
|
|
95fee0cc33 | ||
|
|
18a6fc3b53 | ||
|
|
c38793c955 | ||
|
|
45376dee35 | ||
|
|
44ff20e5c6 | ||
|
|
159cc14dfc | ||
|
|
aa84044375 | ||
|
|
02b5c9c121 | ||
|
|
b5518df7bd | ||
|
|
2c608b2c71 | ||
|
|
d1523b9648 | ||
|
|
9987b2d493 | ||
|
|
83eb3eeffc | ||
|
|
a6e6b1d21c | ||
|
|
e65c88e7ef | ||
|
|
2bcf00b50d | ||
|
|
eb4fafc504 | ||
|
|
5101871760 | ||
|
|
0c8f74597b | ||
|
|
d3a09db938 | ||
|
|
f4fea0026f | ||
|
|
dbb19c30c4 | ||
|
|
5c66e544b1 | ||
|
|
5671bd4ce6 | ||
|
|
12eff54277 | ||
|
|
6c1de362e3 | ||
|
|
1d2e1983bf | ||
|
|
91f7d5b5c5 | ||
|
|
9d7f4f9d05 | ||
|
|
bedca1546b | ||
|
|
0e4349565a | ||
|
|
a0fb85c152 | ||
|
|
599caa1cd8 | ||
|
|
1d5b426043 | ||
|
|
d61852cc72 | ||
|
|
fd567e6860 | ||
|
|
0cdcb932b1 | ||
|
|
2fe57dd456 | ||
|
|
c8bcccd9b9 | ||
|
|
272a8715f7 | ||
|
|
a664b9d0c0 | ||
|
|
80d1e688c8 | ||
|
|
3c9d53109b | ||
|
|
6825dc11d6 | ||
|
|
1ed1ff84a2 | ||
|
|
4220d18072 | ||
|
|
7f2b4b00aa | ||
|
|
6fb304e384 | ||
|
|
82e9e1c7dd | ||
|
|
2dcb17c408 | ||
|
|
684bb4cc67 | ||
|
|
1d67452a00 | ||
|
|
a6db30bb49 | ||
|
|
df9eb7239b | ||
|
|
8eb0868574 | ||
|
|
95b43e91e1 | ||
|
|
f8bdac8e90 | ||
|
|
1293a2cf4f | ||
|
|
f0a5b92b18 | ||
|
|
dc4668a6b4 | ||
|
|
60951853a9 | ||
|
|
d4208ada15 | ||
|
|
122752f384 | ||
|
|
83a0156217 | ||
|
|
1bb477d2ee | ||
|
|
c9e577e220 | ||
|
|
3977873c0e | ||
|
|
d0604b5d4d | ||
|
|
41e32f4e37 | ||
|
|
a7afa3a4f6 | ||
|
|
db17606643 | ||
|
|
7c7ee616f0 | ||
|
|
fd9b736175 | ||
|
|
25afc8aa02 | ||
|
|
68aab1c057 | ||
|
|
19190a6f5b | ||
|
|
cf432879a1 | ||
|
|
cef294dc5a | ||
|
|
843fc26d97 | ||
|
|
25f15ce10e | ||
|
|
cc9c51c42f | ||
|
|
6a61d50078 | ||
|
|
948f5bfd77 | ||
|
|
73fadd13c8 | ||
|
|
d567b09d24 | ||
|
|
12069d87af | ||
|
|
6c211e721c | ||
|
|
bb39d752a9 | ||
|
|
ae35b100c2 | ||
|
|
84c50bba95 | ||
|
|
544703c1b8 | ||
|
|
44c7b36160 | ||
|
|
6639768aa4 | ||
|
|
39a4b9454d | ||
|
|
9d6b7bc939 | ||
|
|
4f3e993ae6 | ||
|
|
632232fff0 | ||
|
|
4d680b1ea9 | ||
|
|
f03140314e | ||
|
|
6c77b24da6 | ||
|
|
a293fff996 | ||
|
|
da18a16c84 | ||
|
|
0424a98a9b | ||
|
|
ba336c297b | ||
|
|
7183af2fdd | ||
|
|
02dd602d07 | ||
|
|
59fae69c6b | ||
|
|
3bd85ab407 | ||
|
|
87d8969efb | ||
|
|
97774b829d | ||
|
|
33e03595cf | ||
|
|
4b79787fb2 | ||
|
|
bd7e91c712 | ||
|
|
67950afe3a | ||
|
|
a6156ff76b | ||
|
|
6de6529bba | ||
|
|
cf98ce3f55 | ||
|
|
c0fb00a6e4 | ||
|
|
49b8f1a2fa | ||
|
|
3a1df9d797 | ||
|
|
11fafb738d | ||
|
|
0c2987a326 | ||
|
|
f37a73f0fb | ||
|
|
07321eec44 | ||
|
|
ac8cbf57f9 | ||
|
|
995f4fa6c1 | ||
|
|
3f3a84c830 | ||
|
|
dde3f1642e | ||
|
|
2c01d21a1d | ||
|
|
6337f2fefa | ||
|
|
de9e95facf | ||
|
|
5cf228d4a0 | ||
|
|
0e45e0bcbd | ||
|
|
bdb98c49c9 | ||
|
|
9369441fc5 | ||
|
|
77e488b4b6 | ||
|
|
26faad0d39 | ||
|
|
886cf142db | ||
|
|
d4439b445c | ||
|
|
5a336902f8 | ||
|
|
19b0f187ba | ||
|
|
4a283d2180 | ||
|
|
670f33c872 | ||
|
|
b5b65f06b7 | ||
|
|
b02d797bc6 | ||
|
|
b9fbf4b2c3 | ||
|
|
ef1fb66954 | ||
|
|
316529c8ca | ||
|
|
d1e72c08a0 | ||
|
|
7ed4763f00 | ||
|
|
fe90fb2479 | ||
|
|
e44a25191f | ||
|
|
3dbeb4e4db | ||
|
|
7c5d4087fc | ||
|
|
83e4fed616 | ||
|
|
5b8dba4a5f | ||
|
|
2404e1ddfc | ||
|
|
3a75f0f3f8 | ||
|
|
d2b4fa89c5 | ||
|
|
3a3b202a96 | ||
|
|
30f08f73fb | ||
|
|
e499b5b3a5 | ||
|
|
443cf449eb | ||
|
|
58d6308589 | ||
|
|
02ed8e8fbd | ||
|
|
3a0dea1d2c | ||
|
|
916d22b55b | ||
|
|
1f1a7e4562 | ||
|
|
ac68a0be0c | ||
|
|
263cc142a6 | ||
|
|
19fdea395b | ||
|
|
40f82d0df3 | ||
|
|
39ab2c5f02 | ||
|
|
f39a59bdce | ||
|
|
a4846f5f32 | ||
|
|
5558910722 | ||
|
|
bebd99d656 | ||
|
|
bad0d2f821 | ||
|
|
4797852f6f | ||
|
|
d467810076 | ||
|
|
6d1ec1a3e1 | ||
|
|
82ea895c81 | ||
|
|
36e699eb29 | ||
|
|
04e8df863a | ||
|
|
96c2312349 | ||
|
|
1d594e82f0 | ||
|
|
cbd96a4176 | ||
|
|
ed17ade510 | ||
|
|
dc63aa22a5 | ||
|
|
1b9b9ae21f | ||
|
|
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 | ||
|
|
a1091556db | ||
|
|
48269733b1 | ||
|
|
9145c23126 | ||
|
|
4368e00250 | ||
|
|
31d4b6dfd6 | ||
|
|
859580a224 | ||
|
|
163dd04175 | ||
|
|
3f96fb8adb | ||
|
|
30b2e8b6d4 | ||
|
|
8242714b99 | ||
|
|
179ddf751b | ||
|
|
63fd68b2f9 | ||
|
|
8402db53d5 | ||
|
|
e92fc730ee | ||
|
|
d546116ab7 | ||
|
|
736c320a60 | ||
|
|
0ecbb04e00 | ||
|
|
2dfa6cfc1b | ||
|
|
e6184a0036 | ||
|
|
0d70782330 | ||
|
|
d89f1ee1de |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -22,3 +22,4 @@ tarballs/
|
||||
minimal/
|
||||
.DS_Store
|
||||
.vscode/settings.json
|
||||
debug.cgi
|
||||
|
||||
49
CHANGELOG.md
49
CHANGELOG.md
@@ -1,5 +1,52 @@
|
||||
## Changelog
|
||||
|
||||
#### 2.641 (May 10, 2026)
|
||||
* Fixed a bug when editing monitors in the System and Server Status module
|
||||
* Fix Fail2Ban default jail options
|
||||
* Added support for trusted proxy IP addresses
|
||||
|
||||
#### 2.640 (May 4, 2026)
|
||||
* Add new nftables module with profiles, saved tables, and chains/sets management
|
||||
* Add new Nginx module with look and feel matching the Apache module
|
||||
* Add option to hide sensitive values (like passwords or tokens) from Webmin's request logs
|
||||
* Add custom ACME server support for Webmin SSL renewal
|
||||
* Add support for the latest MariaDB on Ubuntu 26.04
|
||||
* Add multi-statement SQL query support when executing inline in MySQL/MariaDB module
|
||||
* Add support for ext4 hidden inode quota mode
|
||||
* Add used space and usage percentage reporting for ZFS in the dashboard
|
||||
* Add mass enable and disable buttons for status monitors in the System and Server Status module
|
||||
* Update tiny ACME client to the latest version
|
||||
* Update DHCP default config for openSUSE 16 [#2678](https://github.com/webmin/webmin/issues/2678)
|
||||
* Fix to prevent bypassing two-factor authentication in RPC requests
|
||||
* Fix session cookies to use safer defaults
|
||||
* Fix handling of connections coming through a reverse proxy
|
||||
* Fix unsafe mailbox attachment handling in Mailbox module
|
||||
* Fix unsafe decoding of Outlook `winmail.dat` attachments
|
||||
* Fix Certbot standalone port conflicts
|
||||
* Fix to correctly preserve full quoted action parameters in the Fail2Ban jail editor [#2647](https://github.com/webmin/webmin/issues/2647)
|
||||
* Fix Fail2Ban default jail options to preserve required timing defaults when saving
|
||||
* Fix ZFS to fall back to `df` when disk space cannot be computed from `zpool`
|
||||
* Fix to allow toggling process priority and I/O controls on or off
|
||||
* Fix issue where disabled email notifications were still being processed
|
||||
* Update Authentic theme to the latest version with various improvements and fixes:
|
||||
- Upgrade stats history graphs from laggy SVG to a blazing-fast canvas renderer
|
||||
- Add option to control corner roundness for the menu, content area and right-side slider
|
||||
- Change the content area to use rounded corners and a margin by default
|
||||
- Fix message of the day display in login page correctly [webmin#2555](https://github.com/webmin/webmin/issues/2685)
|
||||
- Fix tooltip visibility in dark palette
|
||||
- Fix session login button spinner
|
||||
- Fix various button styling issues (active state, tiny buttons, airy buttons, stack position)
|
||||
|
||||
#### 2.630 (March 24, 2026)
|
||||
* Add improvements to user input validation across all modules
|
||||
* Update Authentic theme to the latest version with various improvements and fixes:
|
||||
- Add a new airy button style to the light palette to match the dark one
|
||||
- Fix to optimize stats server to reduce WebSocket memory usage
|
||||
- Fix the real-time follow indicator when viewing the journal
|
||||
- Fix regex-based match highlighting when viewing the journal
|
||||
- Fix mail compose panel sizing in HTML mode on low-DPR screens
|
||||
- Fix display of the 2FA QR code in the dark palette
|
||||
|
||||
#### 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
|
||||
@@ -532,7 +579,7 @@ This release adds automatic translations for all languages in UTF-8, updates the
|
||||
This release updates the built-in Let's Encrypt client, adds support for creating "safe-mode" Webmin users, support for CAA records in the BIND module, and the ability to search Postfix maps. It also updates the Authentic theme to the latest version, which includes numerous improvements to the File Manager and overall UI.
|
||||
|
||||
#### 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.
|
||||
These updates fix a [security vulnerability](https://webmin.com/security/) 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 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.
|
||||
|
||||
10
README-zh.md
10
README-zh.md
@@ -1,7 +1,7 @@
|
||||
## 内容
|
||||
* [更新日志](https://github.com/webmin/webmin/blob/master/CHANGELOG.md)
|
||||
* [关于](#关于)
|
||||
* [安装](#安装)[<img src="https://github.com/webmin-devel/webmin/blob/master/media/download-23x14-stable.png?raw=true" title="稳定版">](http://webmin.com/download.html)[<img src="https://github.com/webmin-devel/webmin/blob/master/media/download-23x14-devel.png?raw=true" title="Development Versions">](http://webmin.com/devel.html)
|
||||
* [安装](#安装)[<img src="https://github.com/webmin-devel/webmin/blob/master/media/download-23x14-stable.png?raw=true" title="稳定版">](https://webmin.com/download.html)[<img src="https://github.com/webmin-devel/webmin/blob/master/media/download-23x14-devel.png?raw=true" title="Development Versions">](https://webmin.com/devel.html)
|
||||
* [文档](#文档)
|
||||
* [致谢](#致谢)
|
||||
* [许可](#许可)
|
||||
@@ -26,11 +26,11 @@ Perl 5.10 或更高。
|
||||
## 安装
|
||||
Webmin 可以两种方法安装:
|
||||
|
||||
1. 下载一个预编译包,可用于不同的发行版(CentOS, Fedora, SuSE, Mandriva, Debian, Ubuntu, Solaris 和 [其他发行版](http://www.webmin.com/support.html))。[下载页面直达车](http://webmin.com/download.html);
|
||||
1. 下载一个预编译包,可用于不同的发行版(CentOS, Fedora, SuSE, Mandriva, Debian, Ubuntu, Solaris 和 [其他发行版](https://webmin.com/support.html))。[下载页面直达车](https://webmin.com/download.html);
|
||||
<kbd>注:非常建议[在你的系统添加源](https://doxfer.webmin.com/Webmin/Installation),这样可以自动更新</kbd>
|
||||
|
||||
2. 下载并解压[源码](https://prdownloads.sourceforge.net/webadmin/webmin-1.996.tar.gz)然后运行[_setup.sh_](http://www.webmin.com/tgz.html) 脚本,无需任何选项,也就是说会直接安装到当前目录。或者使用命令行参数,例如目标目录。
|
||||
<kbd>注:如果你正在安装 Webmin [到Windows](http://www.webmin.com/windows.html) 系统上,你必须运行 `perl setup.pl` 命令。Windows 版能否正常运行取决于许多程序,和可能不属于标准发行版的模块。你需要 _process.exe_ 命令, _sc.exe_ 命令,以及 _Win32::Daemon_ Perl 模块。</kbd>
|
||||
2. 下载并解压[源码](https://prdownloads.sourceforge.net/webadmin/webmin-1.996.tar.gz)然后运行[_setup.sh_](https://webmin.com/tgz.html) 脚本,无需任何选项,也就是说会直接安装到当前目录。或者使用命令行参数,例如目标目录。
|
||||
<kbd>注:如果你正在安装 Webmin [到Windows](https://webmin.com/windows.html) 系统上,你必须运行 `perl setup.pl` 命令。Windows 版能否正常运行取决于许多程序,和可能不属于标准发行版的模块。你需要 _process.exe_ 命令, _sc.exe_ 命令,以及 _Win32::Daemon_ Perl 模块。</kbd>
|
||||
|
||||
## 文档
|
||||
完整的 Webmin 还有它所有模块的详细配置都在[维基页面](https://doxfer.webmin.com/Webmin/Main_Page).
|
||||
@@ -39,7 +39,7 @@ Webmin 可以两种方法安装:
|
||||
|
||||
### 首席开发者
|
||||
|
||||
* [Jamie Cameron](http://www.webmin.com/about.html) [](https://www.linkedin.com/in/jamiecameron2)
|
||||
* [Jamie Cameron](https://webmin.com/about.html) [](https://www.linkedin.com/in/jamiecameron2)
|
||||
|
||||
### 贡献者
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ For detailed installation instructions check our guide on [webmin.com/download](
|
||||
|
||||
### Lead developer
|
||||
|
||||
* [Jamie Cameron](https://www.webmin.com/about.html) [](https://www.linkedin.com/in/jamiecameron2)
|
||||
* [Jamie Cameron](https://webmin.com/about.html) [](https://www.linkedin.com/in/jamiecameron2)
|
||||
|
||||
### Developers
|
||||
* [Ilia Rostovtsev](https://github.com/iliaross)
|
||||
|
||||
File diff suppressed because one or more lines are too long
11
acl/lang/en
11
acl/lang/en
@@ -514,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,19 @@ if ($in{'enable'}) {
|
||||
&webmin_log("twofactor", "user", $user->{'name'},
|
||||
{ 'provider' => $user->{'twofactor_provider'},
|
||||
'id' => $user->{'twofactor_id'} });
|
||||
|
||||
# Show a test form only when enrolling for yourself
|
||||
if ($user->{'name'} eq $base_remote_user) {
|
||||
print &ui_form_start("test_twofactor.cgi");
|
||||
print &ui_tag('p', $text{'twofactor_testdesc'});
|
||||
print &ui_tag('p', "$text{'twofactor_testfield'}".
|
||||
" ".
|
||||
&ui_textbox("test", undef, 12));
|
||||
print &ui_hidden("user", $in{'user'}) if ($in{'user'});
|
||||
print &ui_tag('p');
|
||||
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'});
|
||||
@@ -5,115 +5,84 @@ require 'apache-lib.pl';
|
||||
# Output HTML for editing security options for the apache module
|
||||
sub acl_security_form
|
||||
{
|
||||
print "<tr> <td valign=top rowspan=4><b>$text{'acl_virts'}</b></td>\n";
|
||||
print "<td rowspan=4 valign=top>\n";
|
||||
printf "<input type=radio name=virts_def value=1 %s> %s\n",
|
||||
$_[0]->{'virts'} eq '*' ? 'checked' : '', $text{'acl_vall'};
|
||||
printf "<input type=radio name=virts_def value=0 %s> %s<br>\n",
|
||||
$_[0]->{'virts'} eq '*' ? '' : 'checked', $text{'acl_vsel'};
|
||||
print "<select name=virts multiple size=5>\n";
|
||||
local $conf = &get_config();
|
||||
local @virts = ( { 'value' => '__default__' },
|
||||
&find_directive_struct("VirtualHost", $conf) );
|
||||
local %vcan = map { $_, 1 } split(/\s+/, $_[0]->{'virts'});
|
||||
local $v;
|
||||
foreach $v (@virts) {
|
||||
local @vn = &virt_acl_name($v);
|
||||
local ($can) = grep { $vcan{$_} } @vn;
|
||||
local $vn = $can || $vn[0];
|
||||
printf "<option value=\"%s\" %s>%s</option>\n",
|
||||
$vn, $can ? "selected" : "",
|
||||
$vn eq "__default__" ? $text{'acl_defserv'} : $vn;
|
||||
my ($o) = @_;
|
||||
|
||||
my $conf = &get_config();
|
||||
my @virts = ( { 'value' => '__default__' },
|
||||
&find_directive_struct("VirtualHost", $conf) );
|
||||
my @vsel = $o->{'virts'} eq '*' ? () : split(/\s+/, $o->{'virts'});
|
||||
my %vcan = map { $_, 1 } @vsel;
|
||||
my @vopts;
|
||||
foreach my $v (@virts) {
|
||||
my @vn = &virt_acl_name($v);
|
||||
my ($can) = grep { $vcan{$_} } @vn;
|
||||
my $show = $can || $vn[0];
|
||||
push(@vopts, [ $show,
|
||||
$show eq "__default__" ? $text{'acl_defserv'}
|
||||
: $show ]);
|
||||
delete($vcan{$can}) if ($can);
|
||||
}
|
||||
foreach $vn (keys %vcan) {
|
||||
next if ($vn eq "*");
|
||||
printf "<option value=\"%s\" %s>%s</option>\n",
|
||||
$vn, "selected",
|
||||
$vn eq "__default__" ? $text{'acl_defserv'} : $vn;
|
||||
print &ui_table_row($text{'acl_virts'},
|
||||
&ui_radio("virts_def", $o->{'virts'} eq '*' ? 1 : 0,
|
||||
[ [ 1, $text{'acl_vall'} ],
|
||||
[ 0, $text{'acl_vsel'} ] ])."<br>\n".
|
||||
&ui_select("virts", \@vsel, \@vopts, 5, 1, 1),
|
||||
3);
|
||||
|
||||
print &ui_table_row($text{'acl_global'},
|
||||
&ui_select("global",
|
||||
defined($o->{'global'}) && $o->{'global'} ne '' ?
|
||||
$o->{'global'} : 0,
|
||||
[ [ 1, $text{'yes'} ],
|
||||
[ 2, $text{'acl_htaccess'} ],
|
||||
[ 0, $text{'no'} ] ]));
|
||||
print &ui_table_row($text{'acl_create'},
|
||||
&ui_yesno_radio("create", $o->{'create'}));
|
||||
|
||||
print &ui_table_row($text{'acl_vuser'},
|
||||
&ui_yesno_radio("vuser", $o->{'vuser'}));
|
||||
print &ui_table_row($text{'acl_vaddr'},
|
||||
&ui_yesno_radio("vaddr", $o->{'vaddr'}));
|
||||
|
||||
print &ui_table_row($text{'acl_pipe'},
|
||||
&ui_yesno_radio("pipe", $o->{'pipe'}));
|
||||
print &ui_table_row($text{'acl_stop'},
|
||||
&ui_yesno_radio("stop", $o->{'stop'}));
|
||||
|
||||
print &ui_table_row($text{'acl_apply'},
|
||||
&ui_yesno_radio("apply", $o->{'apply'}));
|
||||
print &ui_table_row($text{'acl_names'},
|
||||
&ui_yesno_radio("names", $o->{'names'}));
|
||||
|
||||
print &ui_table_row($text{'acl_dir'},
|
||||
&ui_textbox("dir", $o->{'dir'}, 30)." ".
|
||||
&file_chooser_button("dir", 1),
|
||||
3);
|
||||
|
||||
print &ui_table_row($text{'acl_aliasdir'},
|
||||
&ui_textbox("aliasdir", $o->{'aliasdir'}, 30)." ".
|
||||
&file_chooser_button("aliasdir", 1),
|
||||
3);
|
||||
|
||||
my @typesel = $o->{'types'} eq '*' ? () : split(/\s+/, $o->{'types'});
|
||||
my @typeopts;
|
||||
for (my $i = 0; $text{"type_$i"}; $i++) {
|
||||
push(@typeopts, [ $i, $text{"type_$i"} ]);
|
||||
}
|
||||
print "</select></td>\n";
|
||||
print &ui_table_row($text{'acl_types'},
|
||||
&ui_radio("types_def", $o->{'types'} eq '*' ? 1 : 0,
|
||||
[ [ 1, $text{'acl_all'} ],
|
||||
[ 0, $text{'acl_sel'} ] ])."<br>\n".
|
||||
&ui_select("types", \@typesel, \@typeopts, 5, 1),
|
||||
3);
|
||||
|
||||
print "<td><b>$text{'acl_global'}</b></td> <td><select name=global>\n";
|
||||
printf "<option value=1 %s>$text{'yes'}</option>\n",
|
||||
$_[0]->{'global'} == 1 ? "selected" : "";
|
||||
printf "<option value=2 %s>$text{'acl_htaccess'}</option>\n",
|
||||
$_[0]->{'global'} == 2 ? "selected" : "";
|
||||
printf "<option value=0 %s>$text{'no'}</option></select></td> </tr>\n",
|
||||
$_[0]->{'global'} == 0 ? "selected" : "";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_create'}</b></td> <td>\n";
|
||||
printf "<input type=radio name=create value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'create'} ? "checked" : "";
|
||||
printf "<input type=radio name=create value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'create'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_vuser'}</b></td> <td>\n";
|
||||
printf "<input type=radio name=vuser value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'vuser'} ? "checked" : "";
|
||||
printf "<input type=radio name=vuser value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'vuser'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_vaddr'}</b></td> <td>\n";
|
||||
printf "<input type=radio name=vaddr value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'vaddr'} ? "checked" : "";
|
||||
printf "<input type=radio name=vaddr value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'vaddr'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_pipe'}</b></td> <td>\n";
|
||||
printf "<input type=radio name=pipe value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'pipe'} ? "checked" : "";
|
||||
printf "<input type=radio name=pipe value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'pipe'} ? "" : "checked";
|
||||
|
||||
print "<td><b>$text{'acl_stop'}</b></td> <td>\n";
|
||||
printf "<input type=radio name=stop value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'stop'} ? "checked" : "";
|
||||
printf "<input type=radio name=stop value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'stop'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_apply'}</b></td> <td>\n";
|
||||
printf "<input type=radio name=apply value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'apply'} ? "checked" : "";
|
||||
printf "<input type=radio name=apply value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'apply'} ? "" : "checked";
|
||||
|
||||
print "<td><b>$text{'acl_names'}</b></td> <td>\n";
|
||||
printf "<input type=radio name=names value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'names'} ? "checked" : "";
|
||||
printf "<input type=radio name=names value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'names'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_dir'}</b></td>\n";
|
||||
printf "<td colspan=3><input name=dir size=30 value='%s'> %s</td> </tr>\n",
|
||||
$_[0]->{'dir'}, &file_chooser_button("dir", 1);
|
||||
|
||||
print "<tr> <td><b>$text{'acl_aliasdir'}</b></td>\n";
|
||||
printf "<td colspan=3><input name=aliasdir size=30 value='%s'> %s</td> </tr>\n",
|
||||
$_[0]->{'aliasdir'}, &file_chooser_button("aliasdir", 1);
|
||||
|
||||
print "<tr> <td valign=top><b>$text{'acl_types'}</b></td>\n";
|
||||
print "<td colspan=3>\n";
|
||||
printf "<input type=radio name=types_def value=1 %s> $text{'acl_all'} \n",
|
||||
$_[0]->{'types'} eq '*' ? "checked" : "";
|
||||
printf "<input type=radio name=types_def value=0 %s> $text{'acl_sel'}<br>\n",
|
||||
$_[0]->{'types'} eq '*' ? "" : "checked";
|
||||
map { $types{$_}++ } split(/\s+/, $_[0]->{'types'});
|
||||
print "<select name=types size=5 multiple>\n";
|
||||
for($i=0; $text{"type_$i"}; $i++) {
|
||||
printf "<option value=\"%d\" %s>%s</option>\n",
|
||||
$i, $types{$i} ? "selected" : "", $text{"type_$i"};
|
||||
}
|
||||
print "</select></td> </tr>\n";
|
||||
|
||||
print "<tr> <td valign=top><b>$text{'acl_dirs'}</b></td>\n";
|
||||
print "<td colspan=3>\n";
|
||||
print &ui_radio("dirsmode", $_[0]->{'dirsmode'},
|
||||
[ [ 0, $text{'acl_dirs0'} ],
|
||||
[ 1, $text{'acl_dirs1'} ],
|
||||
[ 2, $text{'acl_dirs2'} ] ]),"<br>\n";
|
||||
print &ui_textarea("dirs", join("\n", split(/\s+/, $_[0]->{'dirs'})), 5, 50);
|
||||
print "</td> </tr>\n";
|
||||
print &ui_table_row($text{'acl_dirs'},
|
||||
&ui_radio("dirsmode", $o->{'dirsmode'},
|
||||
[ [ 0, $text{'acl_dirs0'} ],
|
||||
[ 1, $text{'acl_dirs1'} ],
|
||||
[ 2, $text{'acl_dirs2'} ] ])."<br>\n".
|
||||
&ui_textarea("dirs", join("\n", split(/\s+/, $o->{'dirs'})), 5, 50),
|
||||
3);
|
||||
}
|
||||
|
||||
# acl_security_save(&options)
|
||||
@@ -141,4 +110,3 @@ $_[0]->{'names'} = $in{'names'};
|
||||
$_[0]->{'dirsmode'} = $in{'dirsmode'};
|
||||
$_[0]->{'dirs'} = join(" ", split(/\s+/, $in{'dirs'}));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
233
apache/core.pl
233
apache/core.pl
@@ -73,11 +73,12 @@ $rv = [ [ 'AccessFileName', 0, 5, 'virtual', undef, 5 ],
|
||||
[ 'Options', 0, 5, 'virtual directory htaccess', undef, 3 ],
|
||||
[ 'PidFile', 0, 9, 'global', -2.0 ],
|
||||
[ 'Protocols', 0, 1, 'virtual', 2.4 ],
|
||||
[ 'require', 0, 4, 'directory htaccess', undef, 6 ],
|
||||
[ 'require', 0, 4, 'directory htaccess', '-2.4', 6 ],
|
||||
[ 'Require allow deny order Satisfy', 1, 4, 'directory htaccess', 2.4, 6 ],
|
||||
[ 'RLimitCPU', 0, 0, 'virtual', 1.2 ],
|
||||
[ 'RLimitMEM', 0, 0, 'virtual', 1.2 ],
|
||||
[ 'RLimitNPROC', 0, 0, 'virtual', 1.2 ],
|
||||
[ 'Satisfy', 0, 4, 'directory htaccess', 1.2, 4 ],
|
||||
[ 'Satisfy', 0, 4, 'directory htaccess', '1.2-2.4', 4 ],
|
||||
[ 'ScoreBoardFile', 0, 9, 'global', '1.2-2.0' ],
|
||||
[ 'SendBufferSize', 0, 1, 'global', -2.0 ],
|
||||
[ 'ServerAdmin', 0, 1, 'virtual' ],
|
||||
@@ -958,6 +959,234 @@ if ($in{'AuthType'}) { return ( [ $in{'AuthType'} ] ); }
|
||||
else { return ( [ ] ); }
|
||||
}
|
||||
|
||||
# parse_access_require_rule(value)
|
||||
# Maps a Require line to a UI mode and value.
|
||||
sub parse_access_require_rule
|
||||
{
|
||||
my ($value) = @_;
|
||||
my $lv = lc($value);
|
||||
return ('granted', '') if ($lv eq 'all granted');
|
||||
return ('denied', '') if ($lv eq 'all denied');
|
||||
return ('local', '') if ($lv eq 'local');
|
||||
return ('valid-user', '') if ($lv eq 'valid-user');
|
||||
return ('file-owner', '') if ($lv eq 'file-owner');
|
||||
return ('file-group', '') if ($lv eq 'file-group');
|
||||
if ($value =~ /^(ip|host|env|user|group|method|expr)\s+(.+)$/i) {
|
||||
return (lc($1), $2);
|
||||
}
|
||||
return ('other', $value);
|
||||
}
|
||||
|
||||
# legacy_access_rule_to_require(&directive)
|
||||
# Converts a single Allow rule from legacy syntax to a Require mode/value pair.
|
||||
sub legacy_access_rule_to_require
|
||||
{
|
||||
my ($dir) = @_;
|
||||
my $what = join(" ", @{$dir->{'words'}});
|
||||
$what =~ s/^from\s+//i;
|
||||
return ('granted', '') if (lc($what) eq 'all');
|
||||
return ('env', $1) if ($what =~ /^env=(\S+)$/i);
|
||||
return ($what =~ /^[0-9a-f\.\:\/]+$/i ? 'ip' : 'host', $what);
|
||||
}
|
||||
|
||||
# legacy_access_to_require_rules(&allow, &deny, &order)
|
||||
# For the common Apache 2.4 cases, convert old Allow/Deny/Order rules to Require.
|
||||
sub legacy_access_to_require_rules
|
||||
{
|
||||
my ($allow, $deny, $order) = @_;
|
||||
my $orderv = @$order ? lc($order->[0]->{'value'}) : '';
|
||||
|
||||
# allow from all
|
||||
if (@$allow == 1 && !@$deny && $allow->[0]->{'value'} =~ /^from\s+all$/i) {
|
||||
return [ [ 'granted', '' ] ];
|
||||
}
|
||||
|
||||
# deny from all
|
||||
if (@$deny == 1 && !@$allow && $deny->[0]->{'value'} =~ /^from\s+all$/i) {
|
||||
return [ [ 'denied', '' ] ];
|
||||
}
|
||||
|
||||
# deny from all, then allow some specific requests
|
||||
if ($orderv eq 'deny,allow' &&
|
||||
@$deny == 1 &&
|
||||
$deny->[0]->{'value'} =~ /^from\s+all$/i &&
|
||||
@$allow) {
|
||||
my @rv;
|
||||
foreach my $dir (@$allow) {
|
||||
push(@rv, [ &legacy_access_rule_to_require($dir) ]);
|
||||
}
|
||||
return \@rv;
|
||||
}
|
||||
|
||||
return [ ];
|
||||
}
|
||||
|
||||
# access_require_conf_root()
|
||||
# Returns the outer-most config list that contains the current section.
|
||||
sub access_require_conf_root
|
||||
{
|
||||
return $vconf if (defined($vconf));
|
||||
return $hconf if (defined($hconf));
|
||||
return $conf;
|
||||
}
|
||||
|
||||
# access_require_container_info(&requires, &satisfy)
|
||||
# Determines the current top-level Require container mode and rules.
|
||||
sub access_require_container_info
|
||||
{
|
||||
my ($requires, $satisfy) = @_;
|
||||
my @all = grep { $_->{'type'} } &find_directive_struct("RequireAll", $conf);
|
||||
my @any = grep { $_->{'type'} } &find_directive_struct("RequireAny", $conf);
|
||||
my @rules = @$requires;
|
||||
my $mode = @$satisfy ? lc($satisfy->[0]->{'value'}) : 'default';
|
||||
my $advanced = 0;
|
||||
|
||||
if (@all || @any) {
|
||||
$advanced = 1 if (@all + @any > 1 || @$requires || @$satisfy);
|
||||
my $container = @all ? $all[0] : $any[0];
|
||||
$mode = @all ? 'all' : 'any';
|
||||
@rules = ( );
|
||||
foreach my $m (@{$container->{'members'}}) {
|
||||
next if ($m->{'name'} eq 'dummy');
|
||||
if ($m->{'type'} || lc($m->{'name'}) ne 'require') {
|
||||
$advanced = 1;
|
||||
next;
|
||||
}
|
||||
push(@rules, $m);
|
||||
}
|
||||
}
|
||||
|
||||
return ($mode, \@rules, $advanced);
|
||||
}
|
||||
|
||||
sub edit_Require_allow_deny_order_Satisfy
|
||||
{
|
||||
my ($reqs, $allow, $deny, $order, $satisfy, $e) = @_;
|
||||
my (@mode, @value);
|
||||
my ($logic, $active_reqs, $advanced) =
|
||||
&access_require_container_info($reqs, $satisfy);
|
||||
foreach my $req (@$active_reqs) {
|
||||
my ($m, $v) = &parse_access_require_rule($req->{'value'});
|
||||
push(@mode, $m);
|
||||
push(@value, $v);
|
||||
}
|
||||
|
||||
my $has_legacy = @$allow || @$deny || @$order || @$satisfy;
|
||||
if (!@mode && $has_legacy) {
|
||||
foreach my $r (@{&legacy_access_to_require_rules($allow, $deny, $order)}) {
|
||||
push(@mode, $r->[0]);
|
||||
push(@value, $r->[1]);
|
||||
}
|
||||
}
|
||||
|
||||
push(@mode, "");
|
||||
push(@value, "");
|
||||
|
||||
my $rv = "";
|
||||
if ($has_legacy) {
|
||||
$rv .= &ui_alert($text{'core_require_legacy'}, 'warning',
|
||||
["fa-info-circle", "", 1],
|
||||
{ style => "margin-bottom: 5px;" });
|
||||
}
|
||||
if ($advanced) {
|
||||
$rv .= "<i>$text{'core_require_advanced'}</i><br>\n";
|
||||
}
|
||||
$rv .= "$text{'core_require_logic'} ".
|
||||
&select_input($logic, "Require_logic", "default",
|
||||
"$text{'core_require_logic_default'},default",
|
||||
"$text{'core_require_logic_any'},any",
|
||||
"$text{'core_require_logic_all'},all")."<br>\n";
|
||||
$rv .= &ui_tag("div", "", { style => "height: 5px;" });
|
||||
$rv .= "<table border>\n".
|
||||
"<tr $tb> <td><b>$text{'core_require_type'}</b></td> ".
|
||||
"<td><b>$text{'core_require_value'}</b></td> </tr>\n";
|
||||
for(my $i=0; $i<@mode; $i++) {
|
||||
$rv .= "<tr $cb>\n";
|
||||
$rv .= "<td>".&select_input($mode[$i], "Require_mode_$i", "",
|
||||
"$text{'default'},",
|
||||
"$text{'core_require_granted'},granted",
|
||||
"$text{'core_require_denied'},denied",
|
||||
"$text{'core_require_local'},local",
|
||||
"$text{'core_require_ip'},ip",
|
||||
"$text{'core_require_host'},host",
|
||||
"$text{'core_require_env'},env",
|
||||
"$text{'core_require_user'},user",
|
||||
"$text{'core_require_group'},group",
|
||||
"$text{'core_require_validuser'},valid-user",
|
||||
"$text{'core_require_fileowner'},file-owner",
|
||||
"$text{'core_require_filegroup'},file-group",
|
||||
"$text{'core_require_method'},method",
|
||||
"$text{'core_require_expr'},expr",
|
||||
"$text{'core_require_other'},other")."</td>\n";
|
||||
$rv .= sprintf "<td><input name=Require_value_%d size=40 value=\"%s\"></td>\n",
|
||||
$i, &html_escape($value[$i]);
|
||||
$rv .= "</tr>\n";
|
||||
}
|
||||
$rv .= "</table>\n";
|
||||
return (2, $text{'mod_access_restr'}, $rv);
|
||||
}
|
||||
|
||||
sub save_Require_allow_deny_order_Satisfy
|
||||
{
|
||||
my (@req);
|
||||
for(my $i=0; defined($in{"Require_mode_$i"}); $i++) {
|
||||
my $mode = $in{"Require_mode_$i"};
|
||||
my $value = $in{"Require_value_$i"};
|
||||
$value =~ /\r|\n|\0/ && &error($text{'enewline'});
|
||||
$value =~ s/^\s+//;
|
||||
$value =~ s/\s+$//;
|
||||
next if (!$mode && $value eq '');
|
||||
$mode || &error($text{'core_require_etype'});
|
||||
if ($mode eq 'granted') {
|
||||
push(@req, "all granted");
|
||||
}
|
||||
elsif ($mode eq 'denied') {
|
||||
push(@req, "all denied");
|
||||
}
|
||||
elsif ($mode eq 'local' ||
|
||||
$mode eq 'valid-user' ||
|
||||
$mode eq 'file-owner' ||
|
||||
$mode eq 'file-group') {
|
||||
push(@req, $mode);
|
||||
}
|
||||
elsif ($mode eq 'other') {
|
||||
$value =~ /\S/ || &error($text{'core_require_evalue'});
|
||||
push(@req, $value);
|
||||
}
|
||||
else {
|
||||
$value =~ /\S/ || &error($text{'core_require_evalue'});
|
||||
push(@req, "$mode $value");
|
||||
}
|
||||
}
|
||||
|
||||
my $logic = $in{'Require_logic'} || 'default';
|
||||
my $root = &access_require_conf_root();
|
||||
my @all = grep { $_->{'type'} } &find_directive_struct("RequireAll", $conf);
|
||||
my @any = grep { $_->{'type'} } &find_directive_struct("RequireAny", $conf);
|
||||
my @containers = (@all, @any);
|
||||
|
||||
if (@req && $logic ne 'default') {
|
||||
my $new = {
|
||||
'name' => $logic eq 'all' ? 'RequireAll' : 'RequireAny',
|
||||
'value' => '',
|
||||
'type' => 1,
|
||||
'members' => [ map { {
|
||||
'name' => 'Require',
|
||||
'value' => $_,
|
||||
'type' => 0,
|
||||
} } @req ],
|
||||
};
|
||||
my $old = shift(@containers);
|
||||
&save_directive_struct($old, $new, $conf, $root);
|
||||
}
|
||||
|
||||
foreach my $c (@containers) {
|
||||
&save_directive_struct($c, undef, $conf, $root);
|
||||
}
|
||||
|
||||
return ( $logic eq 'default' ? \@req : [ ], [ ], [ ], [ ], [ ] );
|
||||
}
|
||||
|
||||
sub edit_require
|
||||
{
|
||||
local($rv, $mode, $list);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -11,7 +11,8 @@ print $text{'defines_desc'},"<p>\n";
|
||||
@defs = &get_httpd_defines(1);
|
||||
if (@defs) {
|
||||
print &text('defines_config',
|
||||
"<tt><b>".join(" ", @defs)."</b></tt>"),"<p>\n";
|
||||
"<tt><b>".&html_escape(join(" ", @defs))."</b></tt>"),
|
||||
"<p>\n";
|
||||
}
|
||||
|
||||
print &ui_form_start("save_defines.cgi", "post");
|
||||
|
||||
@@ -22,13 +22,13 @@ 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";
|
||||
@links = ( &ui_link("edit_gmime_type.cgi?file=$mfile",
|
||||
@links = ( &ui_link("edit_gmime_type.cgi?file=".&urlize($mfile),
|
||||
$text{'global_add'}) );
|
||||
print &ui_links_row(\@links);
|
||||
print &ui_columns_start([ $text{'global_type'},
|
||||
@@ -41,7 +41,8 @@ if ($in{'type'} == 6) {
|
||||
if (/^\s*(\S+)\s*(.*)$/) {
|
||||
print &ui_columns_row([
|
||||
&ui_link("edit_gmime_type.cgi?line=$line".
|
||||
"&file=$mfile", $1), $2 ]);
|
||||
"&file=".&urlize($mfile), &html_escape($1)),
|
||||
&html_escape($2) ]);
|
||||
}
|
||||
$line++;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ acl_types=أنواع التوجيه المتاحة
|
||||
acl_all=الكل
|
||||
acl_sel=المحدد ..
|
||||
acl_names=يمكن تعديل أسماء الخادم؟
|
||||
acl_dirs=Apache directives available<br><font size=-1>(Subject to types limit above)</font>
|
||||
acl_dirs=توجيهات Apache المتاحة، وفقًا لأنواع التوجيهات المحددة أعلاه
|
||||
acl_dirs0=الكل
|
||||
acl_dirs1=المدرجة فقط ..
|
||||
acl_dirs2=كل ما عدا المدرجة ..
|
||||
|
||||
@@ -329,7 +329,7 @@ acl_types=Налични видове директиви
|
||||
acl_all=Всички
|
||||
acl_sel=Избрани..
|
||||
acl_names=Може да редактира имена на сървъри?
|
||||
acl_dirs=Достъпни директиви на Apache<br><font size=-1>(съгласно лимита на типове по-горе)</font>
|
||||
acl_dirs=Налични директиви на Apache, ограничени от избраните по-горе типове директиви
|
||||
acl_dirs0=Всички
|
||||
acl_dirs1=Само изброените ..
|
||||
acl_dirs2=Всички освен изброените ..
|
||||
|
||||
@@ -329,7 +329,7 @@ acl_types=Tipus de directives disponibles
|
||||
acl_all=Tots
|
||||
acl_sel=Seleccionats...
|
||||
acl_names=Pot editar els noms dels servidors
|
||||
acl_dirs=Directives Apache disponibles<br><font size=-1>(Subjectes al límit de tipus superior)</font>
|
||||
acl_dirs=Directives Apache disponibles, limitades pels tipus de directiva seleccionats a sobre
|
||||
acl_dirs0=Totes
|
||||
acl_dirs1=Només les llistades
|
||||
acl_dirs2=Totes excepte les llistades
|
||||
|
||||
@@ -320,7 +320,7 @@ acl_types=Typy direktiv k dispozici
|
||||
acl_all=Všechny
|
||||
acl_sel=Vybrané..
|
||||
acl_names=Mohu upravovat názvy serverů?
|
||||
acl_dirs=Direktivy Apache jsou přístupné<br><font size=-1>(Subject to types limit above)</font>
|
||||
acl_dirs=Dostupné direktivy Apache, omezené typy direktiv vybranými výše
|
||||
acl_dirs0=Vše
|
||||
acl_dirs1=Jen vybrané ..
|
||||
acl_dirs2=Vše kromě vybraných ..
|
||||
|
||||
@@ -78,7 +78,7 @@ acl_types=Tilgængelige direktiv typer
|
||||
acl_all=Alle
|
||||
acl_sel=Valgte ..
|
||||
acl_names=Kan redigere servernavne?
|
||||
acl_dirs=Apache direktiver tilgængelige<br><font size=-1>(Subjekt tor type begrænsinger ovenfor)</font>
|
||||
acl_dirs=Tilgængelige Apache-direktiver, begrænset af de direktivtyper, der er valgt ovenfor
|
||||
acl_dirs0=Alle
|
||||
acl_dirs1=Kun listede ..
|
||||
acl_dirs2=Alle undtagen listede ..
|
||||
|
||||
@@ -331,7 +331,7 @@ acl_types=Verfügbare Direktiventypen
|
||||
acl_all=Alle
|
||||
acl_sel=Ausgewählt..
|
||||
acl_names=Kann Servernamen bearbeiten?
|
||||
acl_dirs=Verfügbare Apache-Direktiven<br><font size=-1>(Abhängig von obigen Typenbeschränkungen)</font>
|
||||
acl_dirs=Verfügbare Apache-Direktiven, eingeschränkt durch die oben ausgewählten Direktivtypen
|
||||
acl_dirs0=Alle
|
||||
acl_dirs1=Nur aufgeführte ..
|
||||
acl_dirs2=Alle außer aufgeführte ..
|
||||
|
||||
@@ -329,7 +329,7 @@ acl_types=Directive types available
|
||||
acl_all=All
|
||||
acl_sel=Selected..
|
||||
acl_names=Can edit server names?
|
||||
acl_dirs=Apache directives available<br><font size=-1>(Subject to types limit above)</font>
|
||||
acl_dirs=Διαθέσιμες οδηγίες Apache, περιορισμένες από τους τύπους οδηγιών που έχουν επιλεγεί παραπάνω
|
||||
acl_dirs0=All
|
||||
acl_dirs1=Only listed ..
|
||||
acl_dirs2=All except listed ..
|
||||
|
||||
@@ -332,7 +332,7 @@ acl_types=Directive types available
|
||||
acl_all=All
|
||||
acl_sel=Selected..
|
||||
acl_names=Can edit server names?
|
||||
acl_dirs=Apache directives available<br><font size=-1>(Subject to types limit above)</font>
|
||||
acl_dirs=Available Apache directives, limited by the directive types selected above
|
||||
acl_dirs0=All
|
||||
acl_dirs1=Only listed ..
|
||||
acl_dirs2=All except listed ..
|
||||
@@ -873,6 +873,30 @@ mod_access_epip='$1' is not a valid partial IP address
|
||||
mod_access_emask='$1' is not a valid network/netmask pair
|
||||
mod_access_ecidr='$1' is not a valid network/CIDR pair
|
||||
mod_access_evar='$1' is not a valid variable name
|
||||
core_require_legacy=Legacy Allow/Deny/Order directives were found. Apache 2.4 uses Require rules. Saving this form will replace the legacy directives with the Require rules below.
|
||||
core_require_advanced=Advanced authorization containers were found. This form edits a flat set of Require rules with an optional top-level RequireAny or RequireAll block. Saving will replace nested or mixed authorization containers.
|
||||
core_require_logic=Authorization logic
|
||||
core_require_logic_default=Default flat Require rules
|
||||
core_require_logic_any=Any rule may match
|
||||
core_require_logic_all=All rules must match
|
||||
core_require_type=Rule type
|
||||
core_require_value=Value or arguments
|
||||
core_require_granted=Allow all requests
|
||||
core_require_denied=Deny all requests
|
||||
core_require_local=Only local requests
|
||||
core_require_ip=Request from IP or network
|
||||
core_require_host=Request from host
|
||||
core_require_env=If variable is set
|
||||
core_require_user=Only these users
|
||||
core_require_group=Only these groups
|
||||
core_require_validuser=All valid users
|
||||
core_require_fileowner=File owner matches
|
||||
core_require_filegroup=Group owner matches
|
||||
core_require_method=HTTP method
|
||||
core_require_expr=Expression
|
||||
core_require_other=Raw Require rule
|
||||
core_require_etype=You must select a Require rule type
|
||||
core_require_evalue=You must enter a value for the selected Require rule
|
||||
|
||||
mod_auth_ufile=User text file
|
||||
mod_auth_uedit=Edit users
|
||||
|
||||
@@ -33,7 +33,7 @@ htaccess_err=Error al crear el archivo de opciones
|
||||
defines_list=Parámetros definidos
|
||||
|
||||
acl_aliasdir=Limite los alias al directorio
|
||||
acl_dirs=Directivas de Apache disponibles <br><font size=-1> (Sujeto al límite de tipos anterior) </font>
|
||||
acl_dirs=Directivas de Apache disponibles, limitadas por los tipos de directiva seleccionados arriba
|
||||
acl_dirs0=Todos
|
||||
acl_dirs1=Solo listado ..
|
||||
acl_dirs2=Todos excepto los enumerados.
|
||||
|
||||
@@ -331,7 +331,7 @@ acl_types=Zuzentarau motak eskuragarri
|
||||
acl_all=guztiak
|
||||
acl_sel=Hautatutako ..
|
||||
acl_names=Editatu al ditzakezu zerbitzariaren izenak?
|
||||
acl_dirs=Apache zuzentzaileak eskuragarri daude <font size=-1>(Aurreko muga moten menpe)</font>
|
||||
acl_dirs=Eskuragarri dauden Apache zuzentarauak, goian hautatutako zuzentarau motez mugatuta
|
||||
acl_dirs0=guztiak
|
||||
acl_dirs1=Zerrenda bakarra ..
|
||||
acl_dirs2=Zerrendatu guztiak ..
|
||||
|
||||
@@ -35,7 +35,7 @@ defines_list=پارامترهای تعریف شده
|
||||
|
||||
acl_aliasdir=نام مستعار را به فهرست راهنما محدود كنيد
|
||||
acl_names=آیا می توانید نام سرور را ویرایش کنید؟
|
||||
acl_dirs=Apache directives available<br><font size=-1>(Subject to types limit above)</font>
|
||||
acl_dirs=دستورهای Apache موجود، محدود به انواع دستور انتخابشده در بالا
|
||||
acl_dirs0=همه
|
||||
acl_dirs1=فقط ذکر شده ..
|
||||
acl_dirs2=همه به جز ذکر شده ..
|
||||
|
||||
@@ -331,7 +331,7 @@ acl_types=Direktiivityyppejä saatavana
|
||||
acl_all=Kaikki
|
||||
acl_sel=Valitut ..
|
||||
acl_names=Voiko palvelimen nimiä muokata?
|
||||
acl_dirs=Apache-direktiivit saatavilla <br><font size=-1> (Ellei tyyppirajoitusta ole yllä) </font>
|
||||
acl_dirs=Saatavilla olevat Apache-direktiivit, joita yllä valitut direktiivityypit rajoittavat
|
||||
acl_dirs0=Kaikki
|
||||
acl_dirs1=Vain listattu ..
|
||||
acl_dirs2=Kaikki paitsi luetellut ..
|
||||
|
||||
@@ -331,7 +331,7 @@ acl_types=Types de directives disponibles
|
||||
acl_all=Tous
|
||||
acl_sel=Sélectionnés...
|
||||
acl_names=Peut modifier les noms de serveurs?
|
||||
acl_dirs=Directives Apache disponibles<br><font size=-1>(assujetties aux limites ci-dessus)</font>
|
||||
acl_dirs=Directives Apache disponibles, limitées par les types de directives sélectionnés ci-dessus
|
||||
acl_dirs0=Toutes
|
||||
acl_dirs1=Seulement les suivantes ...
|
||||
acl_dirs2=Toutes sauf les suivantes ...
|
||||
|
||||
@@ -322,7 +322,7 @@ acl_types=Tipovi propisa dostupni
|
||||
acl_all=Sve
|
||||
acl_sel=Odabrano..
|
||||
acl_names=Ne možete urediti nazive poslužitelja?
|
||||
acl_dirs=Apache propisi dostupni<br><font size=-1>(Mterija tipova limita je iznad)</font>
|
||||
acl_dirs=Dostupne Apache direktive, ograničene vrstama direktiva odabranima iznad
|
||||
acl_dirs0=Sve
|
||||
acl_dirs1=Samo izlistano ..
|
||||
acl_dirs2=Sve prihvaćene liste ..
|
||||
|
||||
@@ -32,7 +32,7 @@ htaccess_err=Nem sikerült létrehozni az opciós fájlt
|
||||
defines_list=Meghatározott paraméterek
|
||||
|
||||
acl_aliasdir=Korlátozza az álneveket a könyvtárba
|
||||
acl_dirs=Apache-irányelvek elérhetőek <br><font size=-1> (A fenti típuskorlátozástól függően) </font>
|
||||
acl_dirs=Elérhető Apache-irányelvek, a fent kiválasztott irányelvtípusok által korlátozva
|
||||
acl_dirs0=Összes
|
||||
acl_dirs1=Csak felsorolt ..
|
||||
acl_dirs2=Minden, kivéve a felsorolt ..
|
||||
|
||||
@@ -45,7 +45,7 @@ restart_ecannot=Non è consentito applicare modifiche
|
||||
acl_apply=È possibile applicare le modifiche?
|
||||
acl_aliasdir=Limita gli alias alla directory
|
||||
acl_names=Puoi modificare i nomi dei server?
|
||||
acl_dirs=Direttive Apache disponibili <br><font size=-1> (Soggetto al limite di tipi sopra) </font>
|
||||
acl_dirs=Direttive Apache disponibili, limitate dai tipi di direttiva selezionati sopra
|
||||
acl_dirs0=Tutti
|
||||
acl_dirs1=Solo elencato ..
|
||||
acl_dirs2=Tutti tranne quelli elencati. ..
|
||||
|
||||
@@ -322,7 +322,7 @@ acl_types=利用可能なディレクティブのタイプ
|
||||
acl_all=すべて
|
||||
acl_sel=選択済み..
|
||||
acl_names=サーバー名を編集できますか?
|
||||
acl_dirs=利用可能なApacheディレクティブ<br> <font size = -1>(上記のタイプ制限に従う)</font>
|
||||
acl_dirs=利用可能な Apache ディレクティブ。上で選択したディレクティブタイプに制限されます
|
||||
acl_dirs0=All
|
||||
acl_dirs1=のみ記載..
|
||||
acl_dirs2=All except listed ..
|
||||
|
||||
@@ -77,7 +77,7 @@ restart_ecannot=변경 사항을 적용 할 수 없습니다
|
||||
acl_apply=변경 사항을 적용 할 수 있습니까?
|
||||
acl_aliasdir=디렉토리로 별명 제한
|
||||
acl_names=서버 이름을 편집 할 수 있습니까?
|
||||
acl_dirs=Apache 지시문 사용 가능 <br><font size=-1> (위의 유형 제한에 따름) </font>
|
||||
acl_dirs=사용 가능한 Apache 지시문, 위에서 선택한 지시문 유형으로 제한됨
|
||||
acl_dirs0=모두
|
||||
acl_dirs1=목록 만 ..
|
||||
acl_dirs2=목록을 제외한 모든 ..
|
||||
|
||||
@@ -297,7 +297,7 @@ acl_types=Jenis arahan boleh didapati
|
||||
acl_all=Semua
|
||||
acl_sel=Dipilih ..
|
||||
acl_names=Boleh sunting nama pelayan?
|
||||
acl_dirs=Arahan Apache yang tersedia <br> <font size=-1> (Tertakluk kepada jenis had seperti diatas) </font>
|
||||
acl_dirs=Arahan Apache yang tersedia, dihadkan oleh jenis arahan yang dipilih di atas
|
||||
acl_dirs0=Semua
|
||||
acl_dirs1=Hanya disenaraikan ..
|
||||
acl_dirs2=Semua kecuali yang disenaraikan ..
|
||||
|
||||
@@ -328,7 +328,7 @@ acl_types=Beschikbare soorten richtlijnen
|
||||
acl_all=Alles
|
||||
acl_sel=Geselecteerd..
|
||||
acl_names=Mag server namen bewerken?
|
||||
acl_dirs=Beschikbare Apache richtlijnen<br><font size=-1>(Behalve de soorten hierboven)</font>
|
||||
acl_dirs=Beschikbare Apache-richtlijnen, beperkt door de hierboven geselecteerde richtlijntypen
|
||||
acl_dirs0=Alles
|
||||
acl_dirs1=Alleen die in de lijst
|
||||
acl_dirs2=Alles behalve die in de lijst ..
|
||||
|
||||
@@ -331,7 +331,7 @@ acl_types=Tilgjengelige direktivtyper
|
||||
acl_all=Alle
|
||||
acl_sel=Valgte..
|
||||
acl_names=Kan redigere tjenernavn?
|
||||
acl_dirs=Tilgjengelig Apache direktiver<br><font size=-1>(Med typebegrensninger som angitt ovenfor)</font>
|
||||
acl_dirs=Tilgjengelige Apache-direktiver, begrenset av direktivtypene som er valgt ovenfor
|
||||
acl_dirs0=Alle
|
||||
acl_dirs1=Kun de i listen ..
|
||||
acl_dirs2=Alle unntatt de i listen ..
|
||||
|
||||
@@ -331,7 +331,7 @@ acl_types=Dostępne typy dyrektyw
|
||||
acl_all=Wszystkie
|
||||
acl_sel=Wybrane..
|
||||
acl_names=Może edytować nazwy serwerów?
|
||||
acl_dirs=Dostępne dyrektywy Apache<br><font size=-1>(Zależne od ograniczeń typów powyżej)</font>
|
||||
acl_dirs=Dostępne dyrektywy Apache, ograniczone przez wybrane powyżej typy dyrektyw
|
||||
acl_dirs0=Wszystkie
|
||||
acl_dirs1=Tylko wymienione ..
|
||||
acl_dirs2=Wszystkie oprócz wymienionych ..
|
||||
|
||||
@@ -120,7 +120,7 @@ acl_defserv=Servidor Padrão
|
||||
acl_apply=Pode aplicar alterações?
|
||||
acl_aliasdir=Limitar aliases ao diretório
|
||||
acl_names=Pode editar nomes de servidores?
|
||||
acl_dirs=Diretivas Apache disponíveis <br><font size=-1> (Sujeito ao limite de tipos acima) </font>
|
||||
acl_dirs=Diretivas Apache disponíveis, limitadas pelos tipos de diretiva selecionados acima
|
||||
acl_dirs0=Todos
|
||||
acl_dirs1=Apenas listado ..
|
||||
acl_dirs2=Todos, exceto listados ..
|
||||
|
||||
@@ -323,7 +323,7 @@ acl_types=Tipos de diretivas disponíveis
|
||||
acl_all=Todos
|
||||
acl_sel=Selecionados..
|
||||
acl_names=Editar nome de servidores?
|
||||
acl_dirs=Diretivas do Apache disponíveis<br><font size=-1>(Assunto para os tipos abaixo permitidos)</font>
|
||||
acl_dirs=Diretivas do Apache disponíveis, limitadas pelos tipos de diretiva selecionados acima
|
||||
acl_dirs0=Todos
|
||||
acl_dirs1=Somente listados ..
|
||||
acl_dirs2=Todos exceto os listados ..
|
||||
|
||||
@@ -318,7 +318,7 @@ acl_types=Доступные типы директив
|
||||
acl_all=Все
|
||||
acl_sel=Выбранные..
|
||||
acl_names=Отредактировать имена серверов?
|
||||
acl_dirs=Имеющиеся директивы Apache <br><font size=-1>(С учетом типов ограничить выше) </font>
|
||||
acl_dirs=Доступные директивы Apache, ограниченные выбранными выше типами директив
|
||||
acl_dirs0=Все
|
||||
acl_dirs1=Только перечисленные...
|
||||
acl_dirs2=Все, кроме перечисленных...
|
||||
|
||||
@@ -331,7 +331,7 @@ acl_types=Dostupné typy smernice
|
||||
acl_all=všetko
|
||||
acl_sel=Vybraný ..
|
||||
acl_names=Môžete upravovať názvy serverov?
|
||||
acl_dirs=Dostupné direktívy Apache <br><font size=-1> (podľa obmedzenia typov vyššie) </font>
|
||||
acl_dirs=Dostupné direktívy Apache, obmedzené typmi direktív vybranými vyššie
|
||||
acl_dirs0=všetko
|
||||
acl_dirs1=Iba uvedené ..
|
||||
acl_dirs2=Všetky okrem uvedených ..
|
||||
|
||||
@@ -323,7 +323,7 @@ acl_types=Tillgängliga direktiv typer
|
||||
acl_all=Alla
|
||||
acl_sel=Angivna ...
|
||||
acl_names=Kan ändra server namn?
|
||||
acl_dirs=Apache direktiv tillgängliga<br><font size=-1> (Ämnen av typ begränsning under) </font>
|
||||
acl_dirs=Tillgängliga Apache-direktiv, begränsade av de direktivtyper som valts ovan
|
||||
acl_dirs0=Alla
|
||||
acl_dirs1=Endast listade ..
|
||||
acl_dirs2=Alla utom listade ..
|
||||
|
||||
@@ -134,7 +134,7 @@ acl_defserv=Varsayılan Sunucu
|
||||
acl_apply=Değişiklikler uygulayabilir mi?
|
||||
acl_aliasdir=Diğer adları dizine sınırla
|
||||
acl_names=Sunucu adlarını düzenleyebilir mi?
|
||||
acl_dirs=Apache yönergeleri mevcut <br><font size=-1> (Yukarıdaki tür sınırlamasına tabidir) </font>
|
||||
acl_dirs=Kullanılabilir Apache yönergeleri, yukarıda seçilen yönerge türleriyle sınırlıdır
|
||||
acl_dirs0=Herşey
|
||||
acl_dirs1=Sadece listelenmiş ..
|
||||
acl_dirs2=Listelenenler hariç tümü ..
|
||||
|
||||
@@ -54,7 +54,7 @@ restart_ecannot=Вам заборонено застосовувати змін
|
||||
acl_apply=Чи можна застосувати зміни?
|
||||
acl_aliasdir=Обмежте псевдоніми до каталогу
|
||||
acl_names=Чи можна редагувати імена серверів?
|
||||
acl_dirs=Доступні директиви Apache <br><font size=-1> (За умови обмеження типів вище) </font>
|
||||
acl_dirs=Доступні директиви Apache, обмежені вибраними вище типами директив
|
||||
acl_dirs0=Усі
|
||||
acl_dirs1=Тільки перераховані ..
|
||||
acl_dirs2=Усі, крім перерахованих ..
|
||||
|
||||
@@ -331,7 +331,7 @@ acl_types=可用的指令类型
|
||||
acl_all=全部
|
||||
acl_sel=选择的…
|
||||
acl_names=是否编辑服务器名称吗
|
||||
acl_dirs=Apache指令可用<br><font size=-1>(受上述类型限制)</font>
|
||||
acl_dirs=可用的 Apache 指令,受上方所选指令类型限制
|
||||
acl_dirs0=全部
|
||||
acl_dirs1=仅列出 ..
|
||||
acl_dirs2=除列出外的所有内容 ..
|
||||
|
||||
@@ -39,7 +39,7 @@ start_eafter=Apache似乎沒有運行
|
||||
|
||||
acl_aliasdir=將別名限制為目錄
|
||||
acl_names=可以編輯服務器名稱嗎?
|
||||
acl_dirs=可用的Apache指令<br><font size=-1>(受上述類型限制)</font>
|
||||
acl_dirs=可用的 Apache 指令,受上方所選指令類型限制
|
||||
acl_dirs0=所有
|
||||
acl_dirs1=只列出 ..
|
||||
acl_dirs2=除了列出的所有 ..
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
sub mod_access_directives
|
||||
{
|
||||
local($rv);
|
||||
$rv = [ [ 'allow deny order', 1, 4, 'directory htaccess' ] ];
|
||||
$rv = [ [ 'allow deny order', 1, 4, 'directory htaccess', '-2.0' ] ];
|
||||
return &make_directives($rv, $_[0], "mod_access");
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
sub mod_auth_digest_directives
|
||||
{
|
||||
local($rv);
|
||||
$rv = [ [ 'AuthDigestAuthoritative', 0, 4, 'directory htaccess' ],
|
||||
$rv = [ [ 'AuthDigestAuthoritative', 0, 4, 'directory htaccess', '-2.4' ],
|
||||
[ 'AuthDigestProvider', 0, 4, 'directory htaccess' ],
|
||||
[ 'AuthDigestAlgorithm', 0, 4, 'directory htaccess' ],
|
||||
];
|
||||
@@ -38,9 +38,11 @@ return ( $p ? [ join(" ", split(/\0/, $p)) ] : [ ] );
|
||||
|
||||
sub edit_AuthDigestAlgorithm
|
||||
{
|
||||
# MD5-sess is not correctly implemented yet!
|
||||
# https://httpd.apache.org/docs/2.4/mod/mod_auth_digest.html#authdigestalgorithm
|
||||
return (1, $text{'mod_auth_digest_al'},
|
||||
&choice_input($_[0]->{'value'}, "AuthDigestAlgorithm", "",
|
||||
"MD5", "MD5-sess", "$text{'default'},") );
|
||||
"MD5,MD5", "MD5-sess,MD5-sess", "$text{'default'},") );
|
||||
}
|
||||
sub save_AuthDigestAlgorithm
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ require 'mod_access.pl';
|
||||
sub mod_authz_host_directives
|
||||
{
|
||||
local($rv);
|
||||
$rv = [ [ 'allow deny order', 1, 4, 'directory htaccess' ] ];
|
||||
$rv = [ [ 'allow deny order', 1, 4, 'directory htaccess', '2.0-2.4' ] ];
|
||||
return &make_directives($rv, $_[0], "mod_authz_host");
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -60,12 +60,11 @@ print &ui_tabs_end_tab();
|
||||
|
||||
# Show immediate form
|
||||
print &ui_tabs_start_tab("tab", "backup");
|
||||
my $filename = 'webmin-backup-config-on-';
|
||||
my $hostname = &get_system_hostname();
|
||||
my $hostname = &get_system_hostname() || "localhost";
|
||||
$hostname =~ s/\./-/g;
|
||||
$filename .= $hostname;
|
||||
$filename .= "-".strftime("%Y-%m-%d-%H-%M", localtime);
|
||||
print &ui_form_start("backup.cgi/$filename.tgz", "post");
|
||||
my $filename = $hostname."+configuration_backup-webmin-".
|
||||
strftime("%Y-%m-%d-%H-%M", localtime);
|
||||
print &ui_form_start("backup.cgi/$filename.tar.gz", "post");
|
||||
print &ui_table_start($text{'index_header'}, undef, 2);
|
||||
|
||||
my @dmods = split(/\s+/, $config{'mods'} || "");
|
||||
|
||||
817
bin/webmin
817
bin/webmin
@@ -1,408 +1,507 @@
|
||||
#!/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 Term::ANSIColor qw(:constants);
|
||||
use Getopt::Long qw(:config no_ignore_case permute pass_through);
|
||||
use Term::ANSIColor qw(color);
|
||||
use Pod::Usage;
|
||||
|
||||
# %ansi_specs
|
||||
# Map logical color names to preferred Term::ANSIColor specs and
|
||||
# compatibility fallbacks for older versions.
|
||||
my %ansi_specs = (
|
||||
reset => [ 'reset' ],
|
||||
red => [ 'red' ],
|
||||
green => [ 'green' ],
|
||||
yellow => [ 'yellow' ],
|
||||
cyan => [ 'cyan' ],
|
||||
dark => [ 'dark', 'faint' ],
|
||||
bright_red => [ 'bright_red', 'bold red', 'red' ],
|
||||
bright_green => [ 'bright_green', 'bold green', 'green' ],
|
||||
bright_yellow => [ 'bright_yellow', 'bold yellow', 'yellow' ],
|
||||
);
|
||||
my %ansi_cache;
|
||||
|
||||
# ansi(name)
|
||||
# Resolve a named color to an ANSI escape sequence, with fallbacks
|
||||
# for older Term::ANSIColor versions that may not support newer names.
|
||||
sub ansi
|
||||
{
|
||||
my ($name) = @_;
|
||||
return $ansi_cache{$name} if (exists $ansi_cache{$name});
|
||||
foreach my $spec (@{ $ansi_specs{$name} || [] }) {
|
||||
my $value = eval { color($spec) };
|
||||
return $ansi_cache{$name} = $value if (defined($value));
|
||||
}
|
||||
return $ansi_cache{$name} = "";
|
||||
}
|
||||
|
||||
# print_line(line)
|
||||
# Print a full line without relying on Perl's say syntax,
|
||||
# which is rejected by some older Perl parsers in this script.
|
||||
sub print_line
|
||||
{
|
||||
print @_;
|
||||
print "\n";
|
||||
}
|
||||
|
||||
# Check if root
|
||||
if ($> != 0) {
|
||||
die BRIGHT_RED, "Error: ", RESET, BRIGHT_YELLOW,"webmin", RESET,
|
||||
" command must be run as root\n";
|
||||
exit 1;
|
||||
}
|
||||
die join("",
|
||||
ansi('bright_red'), "Error: ", ansi('reset'),
|
||||
ansi('bright_yellow'), "webmin", ansi('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";
|
||||
}
|
||||
}
|
||||
);
|
||||
# main()
|
||||
# Parse global options and dispatch the requested Webmin CLI action.
|
||||
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{^-}) {
|
||||
print_line("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;
|
||||
|
||||
# Load libs
|
||||
loadlibs(\%opt);
|
||||
# Set defaults
|
||||
$opt{'config'} ||= "/etc/webmin";
|
||||
$opt{'commands'} = $a0;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
# Load libs
|
||||
loadlibs(\%opt);
|
||||
|
||||
my $root = root($opt{'config'});
|
||||
if ($root && -d $root) {
|
||||
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, &$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 @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);
|
||||
}
|
||||
print_line(ansi('cyan'), " $module_type: ",
|
||||
ansi('reset')) if (!$head++);
|
||||
print_line(" $m->{'desc'}: ", ansi('green'),
|
||||
$mod_ver, ansi('reset'),
|
||||
ansi('dark'), " [$m->{'dir'}]",
|
||||
ansi('reset'));
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
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");
|
||||
|
||||
# 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'}) {
|
||||
print_line("$ver$verrel");
|
||||
exit 0;
|
||||
}
|
||||
else {
|
||||
print_line(ansi('cyan'), "Webmin: ",
|
||||
ansi('reset'),
|
||||
ansi('green'),
|
||||
"$ver$verrel", ansi('reset'),
|
||||
ansi('dark'), " [$root]",
|
||||
ansi('reset'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
print_line(ansi('red'), "Error: ",
|
||||
ansi('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);
|
||||
|
||||
# Check for Usermin
|
||||
my $wmumconfig = "$opt{'config'}/usermin/config";
|
||||
if (-r $wmumconfig) {
|
||||
my %wmumconfig;
|
||||
read_file($wmumconfig, \%wmumconfig);
|
||||
# Check for Usermin
|
||||
my $wmumconfig = "$opt{'config'}/usermin/config";
|
||||
if (-r $wmumconfig) {
|
||||
my %wmumconfig;
|
||||
read_file($wmumconfig, \%wmumconfig);
|
||||
|
||||
# Usermin config dir
|
||||
$wmumconfig = $wmumconfig{'usermin_dir'};
|
||||
if ($wmumconfig) {
|
||||
my %uminiserv;
|
||||
read_file("$wmumconfig/miniserv.conf", \%uminiserv);
|
||||
my $uroot = $uminiserv{'root'};
|
||||
# Usermin config dir
|
||||
$wmumconfig = $wmumconfig{'usermin_dir'};
|
||||
if ($wmumconfig) {
|
||||
my %uminiserv;
|
||||
read_file("$wmumconfig/miniserv.conf",
|
||||
\%uminiserv);
|
||||
my $uroot = $uminiserv{'root'};
|
||||
|
||||
# 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);
|
||||
# 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) {
|
||||
print_line(ansi('cyan'),
|
||||
"Usermin: ",
|
||||
ansi('reset'),
|
||||
ansi('green'),
|
||||
$uver, ansi('reset'),
|
||||
ansi('dark'),
|
||||
" [$uroot]",
|
||||
ansi('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);
|
||||
|
||||
my $mod_info_file = "$uroot/$file/module.info";
|
||||
push(@umods, $mod_info_file)
|
||||
if (-r $mod_info_file);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 );
|
||||
}
|
||||
|
||||
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 ) = @_;
|
||||
# run_command(optref, subcmd, remainref)
|
||||
# Run a subcommand with the resolved Webmin config and pass through
|
||||
# its exit status.
|
||||
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 ) ) {
|
||||
print_line(ansi('red'), "Error: ", ansi('reset'),
|
||||
"Command \`$subcmd\` doesn't exist",
|
||||
ansi('reset'));
|
||||
exit 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
my $command_path = get_command_path($root, $subcmd, $optref);
|
||||
|
||||
# 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) {
|
||||
print_line(ansi('red'), "Error: ", ansi('reset'),
|
||||
"Failed to execute \`$command_path\`: $!");
|
||||
exit 1;
|
||||
}
|
||||
else {
|
||||
exit $? >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
sub get_command_path {
|
||||
my ($root, $subcmd, $optref) = @_;
|
||||
# get_command_path(root, subcmd, optref)
|
||||
# Resolve a CLI command name to an executable path under Webmin's
|
||||
# global or module-specific bin directories.
|
||||
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 join("", ansi('red'),
|
||||
"Unrecognized subcommand: $subcmd",
|
||||
ansi('reset'), "\n");
|
||||
}
|
||||
}
|
||||
|
||||
sub list_commands {
|
||||
my ($optref) = @_;
|
||||
# list_commands(optref)
|
||||
# Enumerate available global and module CLI commands, optionally
|
||||
# rendering each command's DESCRIPTION section.
|
||||
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
|
||||
print_line(ansi('yellow'), "$bin", ansi('reset'));
|
||||
pod2usage( -verbose => 99,
|
||||
-sections => [ qw(DESCRIPTION) ],
|
||||
-input => $command,
|
||||
-exitval => "NOEXIT");
|
||||
}
|
||||
else {
|
||||
if (wantarray) {
|
||||
push(@commands, $bin);
|
||||
}
|
||||
else {
|
||||
# Just list the names
|
||||
print_line("$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
|
||||
print_line(ansi('yellow'), "$module-$bin",
|
||||
ansi('reset'));
|
||||
pod2usage( -verbose => 99,
|
||||
-sections => [ qw(DESCRIPTION) ],
|
||||
-input => $command,
|
||||
-exitval => "NOEXIT");
|
||||
}
|
||||
else {
|
||||
if (wantarray) {
|
||||
push(@modules, "$module-$bin");
|
||||
}
|
||||
else {
|
||||
# Just list the names
|
||||
print_line("$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) = @_;
|
||||
|
||||
my $root = root($optref->{'config'});
|
||||
my $command_path = get_command_path($root, $subcmd, $optref);
|
||||
# man_command(optref, subcmd)
|
||||
# Display usage message.
|
||||
sub man_command
|
||||
{
|
||||
my ($optref, $subcmd) = @_;
|
||||
|
||||
$ENV{'PAGER'} ||= "more";
|
||||
open(my $PAGER, "|-", "$ENV{'PAGER'}");
|
||||
if ($optref->{'help'}) {
|
||||
pod2usage( -input => $command_path );
|
||||
} else {
|
||||
pod2usage( -verbose => 99,
|
||||
-input => $command_path,
|
||||
-output => $PAGER);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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";
|
||||
}
|
||||
# root(config)
|
||||
# Read the configured Webmin installation root from miniserv.conf.
|
||||
sub root
|
||||
{
|
||||
my ($config) = @_;
|
||||
open(my $CONF, "<", "$config/miniserv.conf") ||
|
||||
die join("", ansi('red'),
|
||||
"Failed to open $config/miniserv.conf",
|
||||
ansi('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";
|
||||
# loadlibs(optref)
|
||||
# Load bundled libraries from the Webmin vendor dir when they are not
|
||||
# available from system package dependencies.
|
||||
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 +561,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>
|
||||
|
||||
|
||||
@@ -11,18 +11,10 @@ our (%config, %text, %in);
|
||||
# Output HTML for editing security options for the bind8 module
|
||||
sub acl_security_form
|
||||
{
|
||||
my $m = $_[0]->{'zones'} eq '*' ? 1 :
|
||||
$_[0]->{'zones'} =~ /^\!/ ? 2 : 0;
|
||||
print "<tr> <td valign=top><b>$text{'acl_zones'}</b></td>\n";
|
||||
print "<td colspan=3><table cellpadding=0 cellspacing=0> <tr><td valign=top>\n";
|
||||
printf "<input type=radio name=zones_def value=1 %s> %s<br>\n",
|
||||
$m == 1 ? 'checked' : '', $text{'acl_zall'};
|
||||
printf "<input type=radio name=zones_def value=0 %s> %s<br>\n",
|
||||
$m == 0 ? 'checked' : '', $text{'acl_zsel'};
|
||||
printf "<input type=radio name=zones_def value=2 %s> %s</td>\n",
|
||||
$m == 2 ? 'checked' : '', $text{'acl_znsel'};
|
||||
my ($o) = @_;
|
||||
my $m = $o->{'zones'} eq '*' ? 1 :
|
||||
$o->{'zones'} =~ /^\!/ ? 2 : 0;
|
||||
|
||||
print "<td><select name=zones multiple size=4 width=150>\n";
|
||||
my $conf = &get_config();
|
||||
my @zones = grep { $_->{'value'} ne "." }
|
||||
&find("zone", $conf);
|
||||
@@ -31,188 +23,117 @@ foreach my $v (@views) {
|
||||
push(@zones, grep { $_->{'value'} ne "." }
|
||||
&find("zone", $v->{'members'}));
|
||||
}
|
||||
my %zcan;
|
||||
map { $zcan{$_}++ } split(/\s+/, $_[0]->{'zones'});
|
||||
my @zoneopts;
|
||||
foreach my $z (sort { $a->{'value'} cmp $b->{'value'} } @zones) {
|
||||
printf "<option value='%s' %s>%s</option>\n",
|
||||
$z->{'value'},
|
||||
$zcan{$z->{'value'}} ? "selected" : "",
|
||||
&arpa_to_ip($z->{'value'});
|
||||
push(@zoneopts, [ $z->{'value'}, &arpa_to_ip($z->{'value'}) ]);
|
||||
}
|
||||
foreach my $v (sort { $a->{'value'} cmp $b->{'value'} } @views) {
|
||||
printf "<option value='%s' %s>%s</option>\n",
|
||||
'view_'.$v->{'value'},
|
||||
$zcan{'view_'.$v->{'value'}} ? "selected" : "",
|
||||
&text('acl_inview', $v->{'value'});
|
||||
push(@zoneopts, [ 'view_'.$v->{'value'},
|
||||
&text('acl_inview', $v->{'value'}) ]);
|
||||
}
|
||||
print "</select></td> </tr></table></td></tr>\n";
|
||||
|
||||
print &ui_table_row($text{'acl_zones'},
|
||||
&ui_radio("zones_def", $m,
|
||||
[ [ 1, $text{'acl_zall'} ],
|
||||
[ 0, $text{'acl_zsel'} ],
|
||||
[ 2, $text{'acl_znsel'} ] ])."<br>\n".
|
||||
&ui_select("zones", [ split(/\s+/, $o->{'zones'}) ], \@zoneopts, 4, 1),
|
||||
3);
|
||||
|
||||
if (@views) {
|
||||
print "<tr> <td valign=top><b>$text{'acl_inviews'}</b></td>\n";
|
||||
print "<td colspan=3>\n";
|
||||
print &ui_radio("inviews_def", $_[0]->{'inviews'} eq "*" ? 1 : 0,
|
||||
[ [ 1, $text{'acl_vall'} ],
|
||||
[ 0, $text{'acl_vsel'} ] ]),"<br>\n";
|
||||
print "<select name=inviews multiple size=4 width=150>\n";
|
||||
my %vcan;
|
||||
map { $vcan{$_}++ } split(/\s+/, $_[0]->{'inviews'});
|
||||
printf "<option value='%s' %s>%s</option>\n",
|
||||
"_", $vcan{"_"} ? "selected" : "",
|
||||
"<".$text{'acl_toplevel'}.">";
|
||||
my @viewopts = [ "_", "<".$text{'acl_toplevel'}.">" ];
|
||||
foreach my $v (sort { $a->{'value'} cmp $b->{'value'} } @views) {
|
||||
printf "<option value='%s' %s>%s</option>\n",
|
||||
$v->{'value'},
|
||||
$vcan{$v->{'value'}} ? "selected" : "", $v->{'value'};
|
||||
push(@viewopts, [ $v->{'value'}, $v->{'value'} ]);
|
||||
}
|
||||
print "</select></td></tr>\n";
|
||||
|
||||
print &ui_table_row($text{'acl_inviews'},
|
||||
&ui_radio("inviews_def", $o->{'inviews'} eq "*" ? 1 : 0,
|
||||
[ [ 1, $text{'acl_vall'} ],
|
||||
[ 0, $text{'acl_vsel'} ] ])."<br>\n".
|
||||
&ui_select("inviews", [ split(/\s+/, $o->{'inviews'}) ], \@viewopts,
|
||||
4, 1),
|
||||
3);
|
||||
}
|
||||
|
||||
print "<tr> <td><b>$text{'acl_types'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=types_def value=1 %s> %s\n",
|
||||
$_[0]->{'types'} ? "" : "checked", $text{'acl_types1'};
|
||||
printf "<input type=radio name=types_def value=0 %s> %s\n",
|
||||
$_[0]->{'types'} ? "checked" : "", $text{'acl_types0'};
|
||||
printf "<input name=types size=40 value='%s'></td> </tr>\n",
|
||||
$_[0]->{'types'};
|
||||
print &ui_table_row($text{'acl_types'},
|
||||
&ui_opt_textbox("types", $o->{'types'}, 40,
|
||||
$text{'acl_types1'}, $text{'acl_types0'}),
|
||||
3);
|
||||
|
||||
print "<tr> <td valign=top><b>$text{'acl_dir'}</b></td>\n";
|
||||
printf "<td colspan=3><input name=dir size=30 value='%s'> %s<br>\n",
|
||||
$_[0]->{'dir'}, &file_chooser_button("dir", 1);
|
||||
printf "<input type=checkbox name=dironly value=1 %s> %s</td> </tr>\n",
|
||||
$_[0]->{'dironly'} ? "checked" : "", $text{'acl_dironly'};
|
||||
print &ui_table_row($text{'acl_dir'},
|
||||
&ui_textbox("dir", $o->{'dir'}, 30)." ".&file_chooser_button("dir", 1).
|
||||
"<br>\n".&ui_checkbox("dironly", 1, $text{'acl_dironly'}, $o->{'dironly'}),
|
||||
3);
|
||||
|
||||
print "<tr> <td><b>$text{'acl_defaults'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=defaults value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'defaults'} ? "checked" : "";
|
||||
printf "<input type=radio name=defaults value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'defaults'} ? "" : "checked";
|
||||
print &ui_table_row($text{'acl_defaults'},
|
||||
&ui_yesno_radio("defaults", $o->{'defaults'}));
|
||||
|
||||
print "<tr> <td><b>$text{'acl_ztypes'}</b></td> <td colspan=3>\n";
|
||||
foreach my $t ("master", "slave", "forward", "delegation") {
|
||||
printf "<input type=checkbox name=%s %s> %s\n",
|
||||
$t, $_[0]->{$t} ? "checked" : "", $text{'acl_ztypes_'.$t};
|
||||
}
|
||||
print "</td> </tr>\n";
|
||||
print &ui_table_row($text{'acl_ztypes'},
|
||||
join("", map { &ui_checkbox($_, 1, $text{'acl_ztypes_'.$_}, $o->{$_}) }
|
||||
("master", "slave", "forward", "delegation")),
|
||||
3);
|
||||
|
||||
print "<tr> <td><b>$text{'acl_reverse'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=reverse value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'reverse'} ? "checked" : "";
|
||||
printf "<input type=radio name=reverse value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'reverse'} ? "" : "checked";
|
||||
print &ui_table_row($text{'acl_reverse'},
|
||||
&ui_yesno_radio("reverse", $o->{'reverse'}));
|
||||
print &ui_table_row($text{'acl_multiple'},
|
||||
&ui_yesno_radio("multiple", $o->{'multiple'}));
|
||||
|
||||
print "<td><b>$text{'acl_multiple'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=multiple value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'multiple'} ? "checked" : "";
|
||||
printf "<input type=radio name=multiple value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'multiple'} ? "" : "checked";
|
||||
print &ui_table_row($text{'acl_ro'},
|
||||
&ui_yesno_radio("ro", $o->{'ro'}));
|
||||
print &ui_table_row($text{'acl_apply'},
|
||||
&ui_select("apply",
|
||||
defined($o->{'apply'}) && $o->{'apply'} ne '' ? $o->{'apply'} : 0,
|
||||
[ [ 1, $text{'yes'} ],
|
||||
[ 2, $text{'acl_applyonly'} ],
|
||||
[ 3, $text{'acl_applygonly'} ],
|
||||
[ 0, $text{'no'} ] ]));
|
||||
|
||||
print "<tr> <td><b>$text{'acl_ro'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=ro value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'ro'} ? "checked" : "";
|
||||
printf "<input type=radio name=ro value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'ro'} ? "" : "checked";
|
||||
print &ui_table_row($text{'acl_file'},
|
||||
&ui_yesno_radio("file", $o->{'file'}));
|
||||
print &ui_table_row($text{'acl_params'},
|
||||
&ui_yesno_radio("params", $o->{'params'}));
|
||||
|
||||
print "<td><b>$text{'acl_apply'}</b></td> <td nowrap>\n";
|
||||
print &ui_select("apply", $_[0]->{'apply'},
|
||||
[ [ 1, $text{'yes'} ],
|
||||
[ 2, $text{'acl_applyonly'} ],
|
||||
[ 3, $text{'acl_applygonly'} ],
|
||||
[ 0, $text{'no'} ] ]),"</td> </tr>\n";
|
||||
print &ui_table_row($text{'acl_opts'},
|
||||
&ui_yesno_radio("opts", $o->{'opts'}));
|
||||
print &ui_table_row($text{'acl_delete'},
|
||||
&ui_yesno_radio("delete", $o->{'delete'}));
|
||||
|
||||
print "<tr> <td><b>$text{'acl_file'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=file value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'file'} ? "checked" : "";
|
||||
printf "<input type=radio name=file value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'file'} ? "" : "checked";
|
||||
print &ui_table_row($text{'acl_gen'},
|
||||
&ui_yesno_radio("gen", $o->{'gen'}));
|
||||
print &ui_table_row($text{'acl_whois'},
|
||||
&ui_yesno_radio("whois", $o->{'whois'}));
|
||||
|
||||
print "<td><b>$text{'acl_params'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=params value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'params'} ? "checked" : "";
|
||||
printf "<input type=radio name=params value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'params'} ? "" : "checked";
|
||||
print &ui_table_row($text{'acl_findfree'},
|
||||
&ui_yesno_radio("findfree", $o->{'findfree'}));
|
||||
print &ui_table_row($text{'acl_remote'},
|
||||
&ui_yesno_radio("remote", $o->{'remote'}));
|
||||
|
||||
print "<tr> <td><b>$text{'acl_opts'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=opts value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'opts'} ? "checked" : "";
|
||||
printf "<input type=radio name=opts value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'opts'} ? "" : "checked";
|
||||
print &ui_table_row($text{'acl_slaves'},
|
||||
&ui_yesno_radio("slaves", $o->{'slaves'}));
|
||||
print &ui_table_row($text{'acl_dnssec'},
|
||||
&ui_yesno_radio("dnssec", $o->{'dnssec'}));
|
||||
|
||||
print "<td><b>$text{'acl_delete'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=delete value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'delete'} ? "checked" : "";
|
||||
printf "<input type=radio name=delete value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'delete'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_gen'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=gen value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'gen'} ? "checked" : "";
|
||||
printf "<input type=radio name=gen value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'gen'} ? "" : "checked";
|
||||
|
||||
print "<td><b>$text{'acl_whois'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=whois value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'whois'} ? "checked" : "";
|
||||
printf "<input type=radio name=whois value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'whois'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_findfree'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=findfree value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'findfree'} ? "checked" : "";
|
||||
printf "<input type=radio name=findfree value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'findfree'} ? "" : "checked";
|
||||
|
||||
print "<td><b>$text{'acl_remote'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=remote value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'remote'} ? "checked" : "";
|
||||
printf "<input type=radio name=remote value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'remote'} ? "" : "checked";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_slaves'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=slaves value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'slaves'} ? "checked" : "";
|
||||
printf "<input type=radio name=slaves value=0 %s> $text{'no'}</td>\n",
|
||||
$_[0]->{'slaves'} ? "" : "checked";
|
||||
|
||||
print "<td><b>$text{'acl_dnssec'}</b></td> <td nowrap>\n";
|
||||
printf "<input type=radio name=dnssec value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'dnssec'} ? "checked" : "";
|
||||
printf "<input type=radio name=dnssec value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'dnssec'} ? "" : "checked";
|
||||
|
||||
print "</tr>\n";
|
||||
|
||||
print "<tr> <td><b>$text{'acl_views'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=views value=1 %s> $text{'yes'}\n",
|
||||
$_[0]->{'views'} == 1 ? "checked" : "";
|
||||
printf "<input type=radio name=views value=2 %s> $text{'acl_edonly'}\n",
|
||||
$_[0]->{'views'} == 2 ? "checked" : "";
|
||||
printf "<input type=radio name=views value=0 %s> $text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'views'} ? "" : "checked";
|
||||
print &ui_table_row($text{'acl_views'},
|
||||
&ui_radio("views", defined($o->{'views'}) ? $o->{'views'} : 0,
|
||||
[ [ 1, $text{'yes'} ],
|
||||
[ 2, $text{'acl_edonly'} ],
|
||||
[ 0, $text{'no'} ] ]),
|
||||
3);
|
||||
|
||||
if (@views) {
|
||||
my $m = $_[0]->{'vlist'} eq '*' ? 1 :
|
||||
$_[0]->{'vlist'} =~ /^\!/ ? 2 :
|
||||
$_[0]->{'vlist'} eq '' ? 3 : 0;
|
||||
print "<tr> <td valign=top><b>$text{'acl_vlist'}</b></td>\n";
|
||||
print "<td colspan=3><table cellpadding=0 cellspacing=0> <tr><td valign=top>\n";
|
||||
printf "<input type=radio name=vlist_def value=1 %s> %s<br>\n",
|
||||
$m == 1 ? 'checked' : '', $text{'acl_vall'};
|
||||
printf "<input type=radio name=vlist_def value=0 %s> %s<br>\n",
|
||||
$m == 0 ? 'checked' : '', $text{'acl_vsel'};
|
||||
printf "<input type=radio name=vlist_def value=2 %s> %s<br>\n",
|
||||
$m == 2 ? 'checked' : '', $text{'acl_vnsel'};
|
||||
printf "<input type=radio name=vlist_def value=3 %s> %s</td>\n",
|
||||
$m == 3 ? 'checked' : '', $text{'acl_vnone'};
|
||||
|
||||
print "<td><select name=vlist multiple size=4 width=150>\n";
|
||||
my ($v, %vcan);
|
||||
map { $vcan{$_}++ } split(/\s+/, $_[0]->{'vlist'});
|
||||
foreach my $v (sort { $a->{'value'} cmp $b->{'value'} } @views) {
|
||||
printf "<option value='%s' %s>%s</option>\n",
|
||||
$v->{'value'},
|
||||
$vcan{$v->{'value'}} ? "selected" : "", $v->{'value'};
|
||||
}
|
||||
print "</select></td> </tr></table></td></tr>\n";
|
||||
my $vm = $o->{'vlist'} eq '*' ? 1 :
|
||||
$o->{'vlist'} =~ /^\!/ ? 2 :
|
||||
$o->{'vlist'} eq '' ? 3 : 0;
|
||||
my @vopts = map { [ $_->{'value'}, $_->{'value'} ] }
|
||||
sort { $a->{'value'} cmp $b->{'value'} } @views;
|
||||
print &ui_table_row($text{'acl_vlist'},
|
||||
&ui_radio("vlist_def", $vm,
|
||||
[ [ 1, $text{'acl_vall'} ],
|
||||
[ 0, $text{'acl_vsel'} ],
|
||||
[ 2, $text{'acl_vnsel'} ],
|
||||
[ 3, $text{'acl_vnone'} ] ])."<br>\n".
|
||||
&ui_select("vlist", [ split(/\s+/, $o->{'vlist'}) ], \@vopts, 4, 1),
|
||||
3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -700,7 +700,7 @@ if ($v && $v->{'members'}) {
|
||||
push(@av, join(" ", $av->{'name'}, @{$av->{'values'}}));
|
||||
}
|
||||
}
|
||||
if ($_[3] == 0) {
|
||||
if (!$_[3]) {
|
||||
# text area
|
||||
return &ui_table_row($_[0],
|
||||
&ui_textarea($_[1], join("\n", @av), 3, 50));
|
||||
@@ -805,14 +805,14 @@ my $v = &find($_[1], $_[2]);
|
||||
my $n;
|
||||
($n = $_[1]) =~ s/[^A-Za-z0-9_]/_/g;
|
||||
return &ui_table_row($_[0],
|
||||
&ui_opt_textbox($n, $v ? $v->{'value'} : "", $_[4], $_[3])." ".$_[5],
|
||||
&ui_opt_textbox($n, $v ? $v->{'value'} : "", $_[4], $_[3])." ".($_[5] // ""),
|
||||
$_[4] > 30 ? 3 : 1);
|
||||
}
|
||||
|
||||
sub save_opt
|
||||
{
|
||||
my ($dir, $n, $err);
|
||||
($n = $_[0]) =~ s/[^A-Za-z0-9_]/_/g;
|
||||
($n = ($_[0] // "")) =~ s/[^A-Za-z0-9_]/_/g;
|
||||
if ($in{"${n}_def"}) { &save_directive($_[2], $_[0], [ ], $_[3]); }
|
||||
elsif ($err = &{$_[1]}($in{$n})) {
|
||||
&error($err);
|
||||
@@ -906,7 +906,7 @@ my ($fwdconf, $fwdfile, $fwdrec, $ipv6);
|
||||
# find forward domain
|
||||
my $host = $_[0]; $host =~ s/\.$//;
|
||||
my @zl = grep { $_->{'type'} ne 'view' } &list_zone_names();
|
||||
if ($_[1] ne '' && $_[1] ne 'any') {
|
||||
if ($_[1] && $_[1] ne 'any') {
|
||||
@zl = grep { $_->{'view'} && $_->{'viewindex'} == $_[1] } @zl;
|
||||
}
|
||||
else {
|
||||
@@ -968,6 +968,7 @@ else {
|
||||
# Returns 1 if some zone can be edited
|
||||
sub can_edit_zone
|
||||
{
|
||||
$access{'zones'} //= '*';
|
||||
my %zcan;
|
||||
my ($zn, $vn, $file);
|
||||
if ($_[0]->{'members'}) {
|
||||
@@ -2405,7 +2406,7 @@ return undef;
|
||||
sub is_bind_running
|
||||
{
|
||||
my $pidfile = &get_pid_file();
|
||||
my $rv = &check_pid_file(&make_chroot($pidfile, 1));
|
||||
my $rv = &check_pid_file(&make_chroot($pidfile, 1)) || 0;
|
||||
if (!$rv && $gconfig{'os_type'} eq 'windows') {
|
||||
# Fall back to checking for process
|
||||
$rv = &find_byname("named");
|
||||
@@ -2536,6 +2537,7 @@ if ($changed || !$znc{'version'} ||
|
||||
next if (!$type);
|
||||
$type = lc($type);
|
||||
my $file = &find_value("file", $z->{'members'});
|
||||
$file //= "";
|
||||
my $up = &find("update-policy", $z->{'members'});
|
||||
my $au = &find("allow-update", $z->{'members'});
|
||||
my $dynamic = $up || $au || $gau ? 1 : 0;
|
||||
@@ -3198,6 +3200,7 @@ else {
|
||||
$zonename = $zone->{'name'};
|
||||
$zonefile = $zone->{'file'};
|
||||
}
|
||||
return () if (!$zonename || !$zonefile);
|
||||
my $out = &backquote_command(
|
||||
$config{'checkzone'}." ".quotemeta($zonename)." ".
|
||||
quotemeta(&make_chroot(&absolute_path($zonefile)))." 2>&1 </dev/null");
|
||||
@@ -3221,6 +3224,7 @@ else {
|
||||
$zonename = $zone->{'name'};
|
||||
$zonefile = $zone->{'file'};
|
||||
}
|
||||
return () if (!$zonename || !$zonefile);
|
||||
my $absfile = &make_chroot(&absolute_path($zonefile));
|
||||
my $out = &backquote_command(
|
||||
$config{'checkzone'}." ".quotemeta($zonename)." ".
|
||||
@@ -3363,7 +3367,7 @@ if (!$access{'ro'} && $access{'apply'}) {
|
||||
if ($zone && ($access{'apply'} == 1 || $access{'apply'} == 2)) {
|
||||
# Apply this zone
|
||||
my $link = "restart_zone.cgi?return=$r&".
|
||||
"view=$zone->{'viewindex'}&".
|
||||
"view=".($zone->{'viewindex'} // "")."&".
|
||||
"zone=$zone->{'name'}";
|
||||
push(@rv, &ui_link($link, $text{'links_apply'}) );
|
||||
}
|
||||
@@ -3514,7 +3518,7 @@ else {
|
||||
"cd ".quotemeta($fn)." && ".
|
||||
"$config{'keygen'} -a ".quotemeta($alg).
|
||||
" -b ".quotemeta($zonesize).
|
||||
" -n ZONE ".($flag || "")." $dom 2>&1");
|
||||
" -n ZONE ".($flag || "")." ".quotemeta($dom)." 2>&1");
|
||||
if ($?) {
|
||||
kill('KILL', $pid) if ($pid);
|
||||
return $out;
|
||||
@@ -3526,7 +3530,8 @@ else {
|
||||
"cd ".quotemeta($fn)." && ".
|
||||
"$config{'keygen'} -a ".quotemeta($alg).
|
||||
" -b ".quotemeta($size).
|
||||
" -n ZONE -f KSK ".($flag || "")." $dom 2>&1");
|
||||
" -n ZONE -f KSK ".($flag || "")." ".
|
||||
quotemeta($dom)." 2>&1");
|
||||
kill('KILL', $pid) if ($pid);
|
||||
if ($?) {
|
||||
return $out;
|
||||
@@ -3614,7 +3619,7 @@ my $alg = $zonekey->{'algorithm'};
|
||||
my $out = &backquote_logged(
|
||||
"cd ".quotemeta($dir)." && ".
|
||||
"$config{'keygen'} -a ".quotemeta($alg)." -b ".quotemeta($zonesize).
|
||||
" -n ZONE ".($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";
|
||||
@@ -3933,7 +3938,7 @@ if (&find_byname("nscd")) {
|
||||
sub transfer_slave_records
|
||||
{
|
||||
my ($dom, $masters, $file, $source, $sourceport) = @_;
|
||||
my $sourcearg;
|
||||
my $sourcearg = "";
|
||||
if ($source && $source ne "*") {
|
||||
$sourcearg = "-t ".$source;
|
||||
if ($sourceport) {
|
||||
|
||||
@@ -28,12 +28,12 @@ for(my $i=0; $i<@servers; $i++) {
|
||||
my @cols = ( );
|
||||
push(@cols, &ui_textbox("ip_$i", $s->{'value'}, 30));
|
||||
|
||||
my $bogus = &find_value("bogus", $s->{'members'});
|
||||
my $bogus = &find_value("bogus", $s->{'members'}) // "";
|
||||
push(@cols, &ui_radio("bogus_$i", lc($bogus) eq 'yes' ? 1 : 0,
|
||||
[ [ 1, $text{'yes'} ],
|
||||
[ 0, $text{'no'} ] ]));
|
||||
|
||||
my $format = &find_value("transfer-format", $s->{'members'});
|
||||
my $format = &find_value("transfer-format", $s->{'members'}) // "";
|
||||
push(@cols, &ui_radio("format_$i", lc($format),
|
||||
[ [ 'one-answer', $text{'servers_one'} ],
|
||||
[ 'many-answers', $text{'servers_many'} ],
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -45,7 +45,7 @@ else {
|
||||
|
||||
my %bumpedrev;
|
||||
my @delr;
|
||||
foreach my $d (sort { $b <=> $a } @d) {
|
||||
foreach my $d (sort { ($b =~ /^(\d+)/)[0] <=> ($a =~ /^(\d+)/)[0] } @d) {
|
||||
my ($num, $id) = split(/\//, $d, 2);
|
||||
my $r = &find_record_by_id(\@recs, $id, $num);
|
||||
next if (!$r);
|
||||
@@ -77,7 +77,7 @@ else {
|
||||
# Delete the actual record
|
||||
&lock_file(&make_chroot($r->{'file'}));
|
||||
&delete_record($r->{'file'}, $r);
|
||||
splice(@recs, $d, 1);
|
||||
splice(@recs, $num, 1);
|
||||
push(@delr, $r);
|
||||
}
|
||||
&bump_soa_record($zone->{'file'}, \@recs);
|
||||
|
||||
@@ -172,6 +172,7 @@ for(my $i=0; $i<@_; $i++) {
|
||||
else {
|
||||
$name = $r->{'name'};
|
||||
}
|
||||
$name //= "";
|
||||
my @cols;
|
||||
$name = &html_escape($name);
|
||||
my $id = &record_id($r);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -488,6 +488,7 @@ else {
|
||||
}
|
||||
else {
|
||||
# For other record types, just save the lines
|
||||
$in{'values'} //= "";
|
||||
$in{'values'} =~ s/\r//g;
|
||||
my @vlines = split(/\n/, $in{'values'});
|
||||
$vals = join(" ",map { $_ =~ /\s|;/ ? "\"$_\"" : $_ } @vlines);
|
||||
|
||||
@@ -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
Executable file
53
bsdfdisk/change_slice_label.cgi
Executable 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,50 +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-h]$/i || &error($text{'npart_eletter'});
|
||||
$in{'letter'} = lc($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'}));
|
||||
$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'} + 1;
|
||||
$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'},
|
||||
&html_escape( $slice->{'desc'} ) ), "<p>\n";
|
||||
|
||||
print &text('npart_creating', $in{'letter'}, $slice->{'desc'}),"<p>\n";
|
||||
# Actually create the partition inside the slice (initialize BSD label if needed)
|
||||
my $err = &create_partition($disk, $slice, $part);
|
||||
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'} );
|
||||
|
||||
@@ -3,107 +3,145 @@ use strict;
|
||||
use warnings;
|
||||
no warnings 'redefine';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
our ( %in, %text, $module_name );
|
||||
ReadParse();
|
||||
error_setup($text{'nslice_err'});
|
||||
error_setup( $text{'nslice_err'} );
|
||||
|
||||
# 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'}) {
|
||||
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'});
|
||||
$disk or error( $text{'disk_egone'} );
|
||||
|
||||
# Prefer GPART total blocks for bounds
|
||||
(my $base_dev = $in{'device'}) =~ s{^/dev/}{};
|
||||
( 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);
|
||||
my $disk_blocks =
|
||||
( $ds && $ds->{'total_blocks'} )
|
||||
? $ds->{'total_blocks'}
|
||||
: ( $disk->{'blocks'} || 0 );
|
||||
|
||||
# Validate inputs, starting with slice number
|
||||
my $slice = {};
|
||||
$in{'number'} =~ /^\d+$/ or error($text{'nslice_enumber'});
|
||||
$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'}) {
|
||||
foreach my $s ( @{ $disk->{'slices'} } ) {
|
||||
if ( $s->{'number'} == $in{'number'} ) {
|
||||
$clash = $s;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$slice->{'number'} = $in{'number'};
|
||||
|
||||
# Start and end blocks
|
||||
$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'});
|
||||
$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));
|
||||
( $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'} }) {
|
||||
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'});
|
||||
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'} + 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'} =~ /^[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";
|
||||
} else {
|
||||
print text( 'nslice_failed', &html_escape($err) ), "<p>\n";
|
||||
}
|
||||
else {
|
||||
print text('nslice_done'), "<p>\n";
|
||||
# Auto-label the new partition provider with its name if scheme is GPT or BSD
|
||||
my $base = $disk->{'device'}; $base =~ s{^/dev/}{};
|
||||
|
||||
# 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 ( $ds && $ds->{'scheme'} ) {
|
||||
|
||||
# Determine provider and label text
|
||||
my $label_text = slice_name($slice); # e.g., da8s2 or da0p2
|
||||
if ($ds->{'scheme'} =~ /GPT/i) {
|
||||
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) . " $base";
|
||||
my $out2 = `$cmd2 2>&1`;
|
||||
my $cmd2 =
|
||||
"gpart modify -i $idx -l "
|
||||
. quote_path($label_text) . " "
|
||||
. quote_path($base);
|
||||
my $out2 = backquote_command("$cmd2 2>&1");
|
||||
|
||||
# If it fails, ignore silently
|
||||
}
|
||||
} else {
|
||||
# On MBR, if BSD label exists we can set label once created; ignore for now
|
||||
}
|
||||
else {
|
||||
# On MBR, if BSD label exists we can set label once created; ignore for now
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$err && $in{'makepart'}) {
|
||||
if ( !$err && $in{'makepart'} ) {
|
||||
|
||||
# Also create a partition (initialize slice label)
|
||||
my $part_err = initialize_slice($disk, $slice);
|
||||
my $part_err = initialize_slice( $disk, $slice );
|
||||
if ($part_err) {
|
||||
print text('nslice_pfailed', $part_err), "<p>\n";
|
||||
} else {
|
||||
print text( 'nslice_pfailed', &html_escape($part_err) ), "<p>\n";
|
||||
}
|
||||
else {
|
||||
print text('nslice_pdone'), "<p>\n";
|
||||
}
|
||||
}
|
||||
if (!$err) {
|
||||
if ( !$err ) {
|
||||
|
||||
# Auto-label GPT partitions with their device name (e.g., da8p2)
|
||||
my $base = $disk->{'device'}; $base =~ s{^/dev/}{};
|
||||
my $base = $disk->{'device'};
|
||||
$base =~ s{^/dev/}{};
|
||||
my $ds = get_disk_structure($base);
|
||||
if ($ds && $ds->{'scheme'} && $ds->{'scheme'} =~ /GPT/i) {
|
||||
if ( $ds && $ds->{'scheme'} && $ds->{'scheme'} =~ /GPT/i ) {
|
||||
my $slice_devname = $slice->{'device'};
|
||||
$slice_devname =~ s{^/dev/}{}; # e.g., da8p2
|
||||
$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) . " $base 2>&1";
|
||||
my $label_out = `$label_cmd`;
|
||||
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);
|
||||
webmin_log( "create", "slice", $slice->{'device'}, $slice );
|
||||
}
|
||||
ui_print_footer("edit_disk.cgi?device=$in{'device'}", $text{'disk_return'});
|
||||
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'} );
|
||||
|
||||
@@ -5,180 +5,253 @@ 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 = 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'} );
|
||||
|
||||
# Cache commonly used values
|
||||
my $device = $disk->{'device'};
|
||||
my $device = $disk->{'device'};
|
||||
my $device_url = &urlize($device);
|
||||
my $desc = $disk->{'desc'};
|
||||
my $desc = $disk->{'desc'};
|
||||
|
||||
# Prefer total blocks from gpart header when available
|
||||
my $base_device = $disk->{'device'}; $base_device =~ s{^/dev/}{};
|
||||
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);
|
||||
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'}, "");
|
||||
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 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);
|
||||
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'}, $disk->{'model'});
|
||||
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>$disk->{'device'}</tt>");
|
||||
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 ? $disk_structure->{'scheme'} : $text{'disk_unknown'});
|
||||
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'});
|
||||
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->{'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->{'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->{'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'}, $geom_info->{'mode'});
|
||||
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'});
|
||||
} else {
|
||||
print &ui_table_row($text{'disk_rotationrate'}, $geom_info->{'rotationrate'} . " " . $text{'disk_rpm'});
|
||||
if ( $geom_info->{'rotationrate'} ) {
|
||||
if ( $geom_info->{'rotationrate'} eq "0" ) {
|
||||
print &ui_table_row( $text{'disk_rotationrate'},
|
||||
$text{'disk_ssd'} );
|
||||
}
|
||||
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'}, $geom_info->{'ident'});
|
||||
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'}, $geom_info->{'lunid'});
|
||||
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'}, $geom_info->{'descr'});
|
||||
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_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_row( $text{'disk_blocks'}, $disk->{'blocks'} );
|
||||
print &ui_table_end();
|
||||
|
||||
# Debug: print raw outputs if debug mode is enabled
|
||||
if ($in{'debug'}) {
|
||||
if ( $in{'debug'} ) {
|
||||
print "<div class='debug-section'>";
|
||||
|
||||
# Debug: gpart show output
|
||||
my $cmd = "gpart show -l $base_device 2>&1";
|
||||
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-heading'><h3 class='panel-title'>$text{'disk_debug_gpart'}</h3></div>";
|
||||
print "<div class='panel-body'>";
|
||||
print "<pre>Command: $cmd\nOutput:\n$out\n</pre>";
|
||||
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-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') {
|
||||
foreach my $key ( sort keys %$disk_structure ) {
|
||||
if ( $key eq 'entries' ) {
|
||||
print "entries: [\n";
|
||||
foreach my $entry (@{$disk_structure->{'entries'}}) {
|
||||
foreach my $entry ( @{ $disk_structure->{'entries'} } ) {
|
||||
print " {\n";
|
||||
foreach my $k (sort keys %$entry) {
|
||||
print " $k: $entry->{$k}\n";
|
||||
foreach my $k ( sort keys %$entry ) {
|
||||
print " $k: " . &html_escape( $entry->{$k} ) . "\n";
|
||||
}
|
||||
print " },\n";
|
||||
}
|
||||
print "]\n";
|
||||
} else {
|
||||
print "$key: $disk_structure->{$key}\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-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 &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'}) {
|
||||
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'});
|
||||
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}->{'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';
|
||||
$part_details{$part_num}->{'type'} ||=
|
||||
$entry->{'part_type'} || 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
# Build ZFS devices cache
|
||||
my ($zfs_pools, $zfs_devices) = &build_zfs_devices_cache();
|
||||
my ( $zfs_pools, $zfs_devices ) = &build_zfs_devices_cache();
|
||||
|
||||
# Debug ZFS pools if debug mode is enabled
|
||||
if ($in{'debug'}) {
|
||||
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-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: $cmd\nOutput:\n$out\n";
|
||||
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'}) {
|
||||
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-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) {
|
||||
foreach my $pnum ( sort { $a <=> $b } keys %part_details ) {
|
||||
print " $pnum: {\n";
|
||||
foreach my $k (sort keys %{$part_details{$pnum}}) {
|
||||
print " $k: $part_details{$pnum}->{$k}\n";
|
||||
foreach my $k ( sort keys %{ $part_details{$pnum} } ) {
|
||||
print " $k: "
|
||||
. &html_escape( $part_details{$pnum}->{$k} ) . "\n";
|
||||
}
|
||||
print " },\n";
|
||||
}
|
||||
@@ -186,155 +259,204 @@ if ($in{'debug'}) {
|
||||
print "</div></div>";
|
||||
print "</div>";
|
||||
}
|
||||
|
||||
# Get sector size
|
||||
my $sectorsize = $disk_structure->{'sectorsize'} || &get_disk_sectorsize($device) || 512;
|
||||
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=$device_url&new=1'>".$text{'disk_add'}."</a>" );
|
||||
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
|
||||
]);
|
||||
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') {
|
||||
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 $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 {
|
||||
$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>");
|
||||
|
||||
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>" );
|
||||
|
||||
my $part_info = $part_details{$part_num};
|
||||
my $part_name = $part_info ? $part_info->{'name'} : "-";
|
||||
push(@cols, $part_name);
|
||||
my $part_label = $part_info ? $part_info->{'label'} : ($entry->{'label'} eq "(null)" ? "-" : $entry->{'label'});
|
||||
push(@cols, $part_label);
|
||||
|
||||
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) );
|
||||
|
||||
# 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, $sub_part_info);
|
||||
|
||||
push(@cols, $ext);
|
||||
push(@cols, $entry->{'start'});
|
||||
push(@cols, $entry->{'start'} + $entry->{'size'} - 1);
|
||||
push(@cols, $entry->{'size_human'});
|
||||
|
||||
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(
|
||||
my ( $format_type, $usage, $role ) = classify_partition_row(
|
||||
base_device => $base_device,
|
||||
scheme => ($disk_structure->{'scheme'} || ''),
|
||||
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,
|
||||
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, $format_type || '-');
|
||||
push(@cols, $usage || $text{'part_nouse'});
|
||||
push(@cols, $role || '-');
|
||||
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_row( \@cols );
|
||||
}
|
||||
print &ui_columns_end();
|
||||
} else {
|
||||
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'}}) {
|
||||
}
|
||||
else {
|
||||
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 $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, $use || $text{'part_nouse'});
|
||||
print &ui_columns_row(\@cols);
|
||||
push( @cols, &html_escape( $use || $text{'part_nouse'} ) );
|
||||
print &ui_columns_row( \@cols );
|
||||
}
|
||||
print &ui_columns_end();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
print "<p>$text{'disk_none'}</p>\n";
|
||||
}
|
||||
}
|
||||
print &ui_links_row(\@links);
|
||||
print &ui_links_row( \@links );
|
||||
|
||||
# Show SMART status link if available
|
||||
if (&has_command("smartctl")) {
|
||||
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_row( "smart.cgi", $text{'disk_smart'},
|
||||
$text{'disk_smartdesc'}, &ui_hidden( "device", $device ) );
|
||||
print &ui_buttons_end();
|
||||
}
|
||||
|
||||
# Debug: ZFS cache detail
|
||||
if ($in{'debug'}) {
|
||||
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-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) {
|
||||
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
|
||||
"$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'});
|
||||
&ui_print_footer( "", $text{'disk_return'} );
|
||||
|
||||
@@ -1,105 +1,153 @@
|
||||
#!/usr/local/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# Load required libraries
|
||||
require "./bsdfdisk-lib.pl";
|
||||
our ( %in, %text, $module_name );
|
||||
ReadParse();
|
||||
|
||||
# Cache input parameters to avoid repeated hash lookups
|
||||
my $device = $in{'device'};
|
||||
my $slice_num = $in{'slice'};
|
||||
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{'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) {
|
||||
if ( $d->{'device'} eq $device ) {
|
||||
$disk = $d;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$disk or error($text{'disk_egone'});
|
||||
$disk or error( $text{'disk_egone'} );
|
||||
my $slice;
|
||||
foreach my $s (@{$disk->{'slices'}}) {
|
||||
if ($s->{'number'} eq $slice_num) {
|
||||
foreach my $s ( @{ $disk->{'slices'} } ) {
|
||||
if ( $s->{'number'} eq $slice_num ) {
|
||||
$slice = $s;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$slice or error($text{'slice_egone'});
|
||||
$slice or error( $text{'slice_egone'} );
|
||||
my $part;
|
||||
foreach my $p (@{$slice->{'parts'}}) {
|
||||
if ($p->{'letter'} eq $part_letter) { $part = $p; last; }
|
||||
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'}, "");
|
||||
$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');
|
||||
print ui_alert_box( $text{'part_bootdesc'}, 'info' );
|
||||
}
|
||||
|
||||
# Show current details
|
||||
my $zfs_info = get_all_zfs_info();
|
||||
my @st = fdisk::device_status($part->{'device'});
|
||||
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);
|
||||
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";
|
||||
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"), $hiddens;
|
||||
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>$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);
|
||||
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) {
|
||||
|
||||
# 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'}, get_format_type($part));
|
||||
print ui_table_row(
|
||||
$text{'part_type'},
|
||||
ui_select(
|
||||
"type", $part->{'type'}, [ list_partition_types('BSD') ],
|
||||
1, 0, 1
|
||||
)
|
||||
);
|
||||
}
|
||||
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);
|
||||
else {
|
||||
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 );
|
||||
|
||||
# Add a row for the partition role
|
||||
print ui_table_row($text{'part_role'}, get_partition_role($part));
|
||||
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'} ] ] );
|
||||
}
|
||||
|
||||
# Existing partitions on this slice
|
||||
if (@{ $slice->{'parts'} || [] }) {
|
||||
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'};
|
||||
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'}), $ptype, $p->{'startblock'}, $p->{'startblock'} + $p->{'blocks'} - 1, ($pb2 ? safe_nice_size($pb2) : '-'), $usep, $rolep
|
||||
]);
|
||||
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();
|
||||
}
|
||||
@@ -108,19 +156,26 @@ if (@{ $slice->{'parts'} || [] }) {
|
||||
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);
|
||||
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 $is_boot ? "<b>$text{'part_bootcannotedit'}</b><p>\n"
|
||||
: "<b>$text{'part_cannotedit'}</b><p>\n";
|
||||
}
|
||||
else {
|
||||
print $is_boot
|
||||
? "<b>$text{'part_bootcannotedit'}</b><p>\n"
|
||||
: "<b>$text{'part_cannotedit'}</b><p>\n";
|
||||
}
|
||||
|
||||
# SMART button (physical device)
|
||||
if (&has_command("smartctl")) {
|
||||
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_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=$device&slice=$slice_num", $text{'slice_return'});
|
||||
ui_print_footer( "edit_slice.cgi?device=$url_device&slice=$url_slice",
|
||||
$text{'slice_return'} );
|
||||
|
||||
@@ -4,175 +4,282 @@ 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;
|
||||
|
||||
# Get the disk and slice
|
||||
my @disks = list_disks_partitions();
|
||||
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'});
|
||||
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 $zfs_info = get_all_zfs_info();
|
||||
my ($zfs_pools, $zfs_devices) = build_zfs_devices_cache();
|
||||
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]);
|
||||
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";
|
||||
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 $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;
|
||||
print ui_form_start("save_slice.cgi");
|
||||
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_row($text{'part_device'}, "<tt>$slice->{'device'}</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_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 );
|
||||
|
||||
# Slice type selector (GPT vs legacy)
|
||||
if (is_using_gpart()) {
|
||||
my $scheme = ($disk_structure && $disk_structure->{'scheme'}) ? $disk_structure->{'scheme'} : 'GPT';
|
||||
if ( is_using_gpart() ) {
|
||||
my $scheme =
|
||||
( $disk_structure && $disk_structure->{'scheme'} )
|
||||
? $disk_structure->{'scheme'}
|
||||
: 'GPT';
|
||||
my @opts = list_partition_types($scheme);
|
||||
|
||||
# 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));
|
||||
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 {
|
||||
# Pre-cache tag options for the slice type select (legacy fdisk)
|
||||
my @tags = fdisk::list_tags();
|
||||
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));
|
||||
print ui_table_row( $text{'slice_stype'},
|
||||
ui_select( "type", $slice->{'type'}, \@tag_options ) );
|
||||
}
|
||||
|
||||
# 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 $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_sactive'},
|
||||
ui_yesno_radio( "active", $active_default ) );
|
||||
}
|
||||
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)));
|
||||
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 ) )
|
||||
);
|
||||
|
||||
# Add a row for the slice role
|
||||
print ui_table_row($text{'slice_role'}, get_partition_role($slice));
|
||||
print ui_table_row( $text{'slice_role'}, get_partition_role($slice) );
|
||||
print ui_table_end();
|
||||
print ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
print ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
print ui_hr();
|
||||
|
||||
# Show partitions table (only for MBR slices that support BSD disklabel)
|
||||
my $can_have_parts = 0;
|
||||
if (!is_using_gpart()) {
|
||||
if ( !is_using_gpart() ) {
|
||||
|
||||
# Legacy MBR with BSD disklabel
|
||||
$can_have_parts = 1;
|
||||
} elsif ($disk_structure && $disk_structure->{'scheme'} && $disk_structure->{'scheme'} !~ /GPT/i) {
|
||||
}
|
||||
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=" . urlize($disk->{'device'}) . "&slice=$in{'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'},
|
||||
]);
|
||||
|
||||
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'};
|
||||
|
||||
foreach my $p (@{$slice->{'parts'}}) {
|
||||
|
||||
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 $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);
|
||||
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'} : '-';
|
||||
|
||||
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(
|
||||
( my $pn = $p->{'device'} ) =~ s{^/dev/}{};
|
||||
my ( $fmt, $use_txt, $role_txt ) = classify_partition_row(
|
||||
base_device => $base_device,
|
||||
scheme => ($disk_structure->{'scheme'} || ''),
|
||||
scheme => ( $disk_structure->{'scheme'} || '' ),
|
||||
part_name => $pn,
|
||||
entry_part_type => $p->{'type'},
|
||||
zfs_devices => $zfs_devices,
|
||||
);
|
||||
$use_txt ||= $part_use;
|
||||
$use_txt ||= $part_use;
|
||||
$role_txt ||= get_partition_role($p);
|
||||
|
||||
|
||||
# Build edit URL
|
||||
my $url = "edit_part.cgi?device=" . urlize($disk->{'device'}) . "&slice=" . $slice->{'number'} . "&part=" . $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'>" . ($fmt || get_format_type($p)) . "</a>",
|
||||
$ext,
|
||||
($psz_b ? safe_nice_size($psz_b) : '-'),
|
||||
$p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1,
|
||||
$stripesize,
|
||||
$use_txt,
|
||||
$role_txt,
|
||||
]);
|
||||
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 {
|
||||
print ui_links_row( \@links ) if @links;
|
||||
}
|
||||
else {
|
||||
# GPT partitions do not have sub-partitions
|
||||
if (!$can_have_parts) {
|
||||
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) {
|
||||
|
||||
# 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 {
|
||||
}
|
||||
else {
|
||||
print "<b>$text{'slice_none'}</b><p>\n";
|
||||
print ui_links_row(\@links) if @links;
|
||||
print ui_links_row( \@links ) if @links;
|
||||
}
|
||||
}
|
||||
if ($canedit && !$is_boot) { # Do not allow editing boot slices
|
||||
if ( $canedit && !$is_boot ) { # Do not allow editing boot slices
|
||||
print ui_hr();
|
||||
print ui_buttons_start();
|
||||
if (!@{$slice->{'parts'}}) {
|
||||
show_filesystem_buttons($hiddens, \@slice_status, $slice);
|
||||
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'})
|
||||
ui_hidden( "device", $in{'device'} ) . "\n"
|
||||
. ui_hidden( "slice", $in{'slice'} )
|
||||
);
|
||||
print ui_buttons_end();
|
||||
}
|
||||
|
||||
# SMART button (physical device)
|
||||
if (&has_command("smartctl")) {
|
||||
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_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=$in{'device'}", $text{'disk_return'});
|
||||
ui_print_footer( "edit_disk.cgi?device=$url_device", $text{'disk_return'} );
|
||||
|
||||
@@ -6,83 +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;
|
||||
}
|
||||
$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 fsck on boot partitions or in-use devices
|
||||
if (is_boot_partition($object)) {
|
||||
&error($in{'part'} ne '' ? $text{'part_eboot'} : $text{'slice_eboot'});
|
||||
if ( is_boot_partition($object) ) {
|
||||
&error( $in{'part'} ne '' ? $text{'part_eboot'} : $text{'slice_eboot'} );
|
||||
}
|
||||
my @st_obj = &fdisk::device_status($object->{'device'});
|
||||
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', $use_obj));
|
||||
if ( @st_obj && $st_obj[2] ) {
|
||||
&error( &text( 'part_esave', &html_escape($use_obj) ) );
|
||||
}
|
||||
|
||||
&ui_print_unbuffered_header($object->{'desc'}, $text{'fsck_title'}, "");
|
||||
&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>$object->{'device'}</tt>"),"<br>\n";
|
||||
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));
|
||||
&additional_log( 'exec', undef, $cmd );
|
||||
print &html_escape( &backquote_command($cmd) );
|
||||
print "</pre>";
|
||||
print $text{'fsck_done'},"<p>\n";
|
||||
} else {
|
||||
print $text{'fsck_done'}, "<p>\n";
|
||||
}
|
||||
else {
|
||||
# Do the creation
|
||||
print &text('fsck_checking', "<tt>$object->{'device'}</tt>"),"<br>\n";
|
||||
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 $fh;
|
||||
&open_execute_command($fh, $cmd, 2);
|
||||
if ($fh) {
|
||||
while (my $line = <$fh>) {
|
||||
$line =~ s/[^\x09\x0A\x0D\x20-\x7E]//g;
|
||||
print &html_escape($line);
|
||||
}
|
||||
close($fh);
|
||||
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>";
|
||||
if ($?) {
|
||||
print $text{'fsck_failed'},"<p>\n";
|
||||
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_done'},"<p>\n";
|
||||
print $text{'fsck_failed'}, "<p>\n";
|
||||
}
|
||||
}
|
||||
&webmin_log("fsck", $in{'part'} ne '' ? "part" : "object",
|
||||
$object->{'device'}, $object);
|
||||
&webmin_log( "fsck", $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'});
|
||||
}
|
||||
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'} );
|
||||
}
|
||||
|
||||
@@ -111,11 +111,21 @@ 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
|
||||
@@ -134,6 +144,7 @@ nslice_autonext=Will auto-select next index
|
||||
nslice_diskblocks=Disk size in blocks
|
||||
nslice_start=Starting block
|
||||
nslice_end=Ending block
|
||||
nslice_type=Slice type
|
||||
nslice_err=Failed to create slice
|
||||
nslice_enumber=Slice number must be a number
|
||||
nslice_eclash=A slice with number $1 already exists
|
||||
@@ -148,6 +159,8 @@ 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
|
||||
|
||||
@@ -183,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
|
||||
@@ -229,6 +242,54 @@ newfs_elabel=Missing or invalid filesystem label
|
||||
newfs_creating=Creating filesystem on $1 ..
|
||||
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
|
||||
@@ -240,7 +301,10 @@ fsck_err=Failed to check filesystem
|
||||
fsck_exec=Executing command $1 ..
|
||||
fsck_failed=.. check failed!
|
||||
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?
|
||||
|
||||
# Logging
|
||||
action_create_slice=Created slice $1
|
||||
|
||||
@@ -6,93 +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'});
|
||||
if ( is_boot_partition($object) ) {
|
||||
&error( $in{'part'} ne '' ? $text{'part_eboot'} : $text{'slice_eboot'} );
|
||||
}
|
||||
my @st_obj = &fdisk::device_status($object->{'device'});
|
||||
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', $use_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'} >= 0 && $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'} ||
|
||||
length($in{'label'}) > 0 ||
|
||||
&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;
|
||||
&open_execute_command($fh, $cmd, 2);
|
||||
if ($fh) {
|
||||
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);
|
||||
# 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
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,62 +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'});
|
||||
my $object;
|
||||
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;
|
||||
}
|
||||
# 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'} );
|
||||
my $object;
|
||||
my $url_device = &urlize( $in{'device'} );
|
||||
my $url_slice = &urlize( $in{'slice'} );
|
||||
my $url_part = &urlize( $in{'part'} );
|
||||
|
||||
&ui_print_header($object->{'desc'}, $text{'newfs_title'}, "");
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
# 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'}, "" );
|
||||
|
||||
print &ui_table_row($text{'part_device'},
|
||||
"<tt>$object->{'device'}</tt>");
|
||||
# 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";
|
||||
}
|
||||
}
|
||||
|
||||
print &ui_table_row($text{'newfs_free'},
|
||||
&ui_opt_textbox("free", undef, 4, $text{'newfs_deffree'}) . "%");
|
||||
# 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";
|
||||
}
|
||||
}
|
||||
|
||||
print &ui_table_row($text{'newfs_trim'},
|
||||
&ui_yesno_radio("trim", 0));
|
||||
# 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";
|
||||
}
|
||||
}
|
||||
|
||||
print &ui_table_row($text{'newfs_label'},
|
||||
&ui_opt_textbox("label", undef, 20, $text{'newfs_none'}));
|
||||
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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
# Default: UFS newfs form
|
||||
&ui_print_header( $object->{'desc'}, $text{'newfs_title'}, "" );
|
||||
|
||||
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{'part_device'}, "<tt>$object->{'device'}</tt>" );
|
||||
|
||||
print &ui_table_row( $text{'newfs_free'},
|
||||
&ui_opt_textbox( "free", undef, 4, $text{'newfs_deffree'} ) . "%" );
|
||||
|
||||
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{'save'} ] ]);
|
||||
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,82 +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, 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 %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) . " <i>(" . $text{'npart_creserved'} . ")</i>");
|
||||
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 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'};
|
||||
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));
|
||||
|
||||
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
|
||||
# For BSD-on-MBR inner label partitions, offer FreeBSD partition types
|
||||
my $scheme = 'BSD';
|
||||
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_row( $text{'npart_type'},
|
||||
&ui_select( "type", $default_ptype, [ list_partition_types($scheme) ] ) );
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
print &ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
|
||||
# Existing partitions summary
|
||||
if (@{$slice->{'parts'}||[]}) {
|
||||
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'};
|
||||
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'}), $ptype, $p->{'startblock'}, $p->{'startblock'} + $p->{'blocks'} - 1, ($pb ? safe_nice_size($pb) : '-'), $usep, $rolep
|
||||
]);
|
||||
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();
|
||||
}
|
||||
|
||||
&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,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,38 +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'};
|
||||
$slice->{'active'} = $in{'active'} if (defined $in{'active'});
|
||||
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'} );
|
||||
|
||||
# Apply active flag for MBR disks via gpart set/unset when it changed
|
||||
my $base = $disk->{'device'}; $base =~ s{^/dev/}{};
|
||||
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 = slice_number($slice);
|
||||
if (defined $oldslice->{'active'} && defined $slice->{'active'} && $oldslice->{'active'} != $slice->{'active'}) {
|
||||
my $cmd = $slice->{'active'} ? "gpart set -a active -i $idx $base" : "gpart unset -a active -i $idx $base";
|
||||
my $out = `$cmd 2>&1`;
|
||||
if ($? != 0) {
|
||||
&error("Failed to change active flag: $out");
|
||||
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($err) if ($err);
|
||||
my $err = &modify_slice( $disk, $oldslice, $slice );
|
||||
&error( &html_escape($err) ) if ($err);
|
||||
|
||||
&webmin_log("modify", "slice", $slice->{'device'}, $slice);
|
||||
&redirect("edit_disk.cgi?device=$in{'device'}");
|
||||
&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
Executable file
49
bsdfdisk/save_slice_label.cgi
Executable 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,26 +6,35 @@ 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'} );
|
||||
|
||||
&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 $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);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
@@ -33,102 +42,141 @@ if ($disk_structure && $disk_structure->{'entries'}) {
|
||||
}
|
||||
|
||||
# If no free space, show error and return
|
||||
if (!$has_free_space) {
|
||||
if ( !$has_free_space ) {
|
||||
print "<p><b>$text{'nslice_enospace'}</b></p>\n";
|
||||
&ui_print_footer("edit_disk.cgi?device=$in{'device'}", $text{'disk_return'});
|
||||
&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);
|
||||
|
||||
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++;
|
||||
}
|
||||
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 (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). 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
|
||||
my %used = map { $_->{'number'}, $_ } @{ $disk->{'slices'} };
|
||||
my $n = 1;
|
||||
while ( $used{$n} ) {
|
||||
$n++;
|
||||
}
|
||||
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));
|
||||
|
||||
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 (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). 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
|
||||
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) ]));
|
||||
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() ]));
|
||||
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? (only for MBR slices with BSD disklabel support)
|
||||
if (!$is_gpt) {
|
||||
print &ui_table_row($text{'slice_add'},
|
||||
&ui_yesno_radio("makepart", 1));
|
||||
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{'save'} ] ]);
|
||||
print &ui_form_end( [ [ undef, $text{'save'} ] ] );
|
||||
|
||||
# Existing slices summary
|
||||
print &ui_hr();
|
||||
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'});
|
||||
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'},
|
||||
$stype,
|
||||
$s->{'startblock'},
|
||||
$s->{'startblock'} + $s->{'blocks'} - 1,
|
||||
$sz,
|
||||
]);
|
||||
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'});
|
||||
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'}), $ptype, $p->{'startblock'}, $p->{'startblock'} + $p->{'blocks'} - 1, $psz ];
|
||||
push @parts_rows,
|
||||
[
|
||||
$s->{'number'}, uc( $p->{'letter'} ),
|
||||
&html_escape($ptype), $p->{'startblock'},
|
||||
$p->{'startblock'} + $p->{'blocks'} - 1, $psz
|
||||
];
|
||||
}
|
||||
}
|
||||
if (@parts_rows) {
|
||||
print &ui_columns_start(['Slice', $text{'slice_letter'}, $text{'slice_type'}, $text{'slice_start'}, $text{'slice_end'}, $text{'slice_size'}], $text{'nslice_existing_parts'});
|
||||
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=$in{'device'}",
|
||||
$text{'disk_return'});
|
||||
|
||||
&ui_print_footer( "edit_disk.cgi?device=$url_device", $text{'disk_return'} );
|
||||
|
||||
101
bsdfdisk/smart.cgi
Normal file → Executable file
101
bsdfdisk/smart.cgi
Normal file → Executable file
@@ -5,27 +5,106 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './bsdfdisk-lib.pl';
|
||||
our (%in, %text, $module_name);
|
||||
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');
|
||||
$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');
|
||||
&has_command('smartctl')
|
||||
or &error( $text{'index_ecmd'}
|
||||
? &text( 'index_ecmd', 'smartctl' )
|
||||
: 'smartctl not available' );
|
||||
|
||||
my $device = $in{'device'};
|
||||
my $device = $in{'device'};
|
||||
my $dev_html = &html_escape($device);
|
||||
|
||||
&ui_print_header($dev_html, $text{'disk_smart'} || 'SMART Status', "");
|
||||
&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-heading'><h3 class='panel-title'>SMART status for "
|
||||
. "<tt>$dev_html</tt></h3></div>\n";
|
||||
print "<div class='panel-body'>\n";
|
||||
my $cmd = "smartctl -a " . "e_path($device) . " 2>&1";
|
||||
my $out = &backquote_command($cmd);
|
||||
print "<pre>" . &html_escape("Command: $cmd\n\n$out") . "</pre>\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'});
|
||||
&ui_print_footer( "edit_disk.cgi?device=" . &urlize($device),
|
||||
$text{'disk_return'} );
|
||||
|
||||
121
bsdfdisk/zfs_create.cgi
Executable file
121
bsdfdisk/zfs_create.cgi
Executable 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
Executable file
128
bsdfdisk/zvol_create.cgi
Executable 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'} );
|
||||
}
|
||||
@@ -5,66 +5,47 @@ require 'cluster-passwd-lib.pl';
|
||||
# Output HTML for editing security options for the passwd module
|
||||
sub acl_security_form
|
||||
{
|
||||
print "<tr> <td valign=top><b>$passwd::text{'acl_users'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=mode value=0 %s> %s\n",
|
||||
$_[0]->{'mode'} == 0 ? 'checked' : '', $passwd::text{'acl_mode0'};
|
||||
my ($o) = @_;
|
||||
|
||||
printf "<input type=radio name=mode value=3 %s> %s<br>\n",
|
||||
$_[0]->{'mode'} == 3 ? 'checked' : '', $passwd::text{'acl_mode3'};
|
||||
print &ui_table_row($passwd::text{'acl_users'},
|
||||
&ui_radio_table("mode", defined($o->{'mode'}) ? $o->{'mode'} : 0,
|
||||
[ [ 0, $passwd::text{'acl_mode0'} ],
|
||||
[ 3, $passwd::text{'acl_mode3'} ],
|
||||
[ 1, $passwd::text{'acl_mode1'},
|
||||
&ui_textbox("users1", $o->{'mode'} == 1 ? $o->{'users'} : "",
|
||||
40)." ".&user_chooser_button("users1", 1) ],
|
||||
[ 2, $passwd::text{'acl_mode2'},
|
||||
&ui_textbox("users2", $o->{'mode'} == 2 ? $o->{'users'} : "",
|
||||
40)." ".&user_chooser_button("users2", 1) ],
|
||||
[ 4, $passwd::text{'acl_mode4'},
|
||||
&ui_textbox("low", $o->{'mode'} == 4 ? $o->{'low'} : "", 8).
|
||||
" - ".&ui_textbox("high",
|
||||
$o->{'mode'} == 4 ? $o->{'high'} : "", 8) ],
|
||||
[ 5, $passwd::text{'acl_mode5'},
|
||||
&ui_textbox("groups", $o->{'mode'} == 5 ? $o->{'users'} : "",
|
||||
20)." ".&group_chooser_button("groups", 1)."<br>\n".
|
||||
&ui_checkbox("sec", 1, $passwd::text{'acl_sec'}, $o->{'sec'}) ],
|
||||
[ 6, $passwd::text{'acl_mode6'},
|
||||
&ui_textbox("match", $o->{'mode'} == 6 ? $o->{'users'} : "",
|
||||
15) ] ], 1),
|
||||
3);
|
||||
|
||||
printf "<input type=radio name=mode value=1 %s> %s\n",
|
||||
$_[0]->{'mode'} == 1 ? 'checked' : '', $passwd::text{'acl_mode1'};
|
||||
printf "<input name=users1 size=40 value='%s'> %s<br>\n",
|
||||
$_[0]->{'mode'} == 1 ? $_[0]->{'users'} : '',
|
||||
&user_chooser_button("users1", 1);
|
||||
print &ui_table_row($passwd::text{'acl_repeat'},
|
||||
&ui_yesno_radio("repeat", $o->{'repeat'}), 3);
|
||||
|
||||
printf "<input type=radio name=mode value=2 %s> %s\n",
|
||||
$_[0]->{'mode'} == 2 ? 'checked' : '', $passwd::text{'acl_mode2'};
|
||||
printf "<input name=users2 size=40 value='%s'> %s<br>\n",
|
||||
$_[0]->{'mode'} == 2 ? $_[0]->{'users'} : '',
|
||||
&user_chooser_button("users2", 1);
|
||||
print &ui_table_row($passwd::text{'acl_others'},
|
||||
&ui_radio("others", defined($o->{'others'}) ? $o->{'others'} : 0,
|
||||
[ [ 1, $passwd::text{'yes'} ],
|
||||
[ 2, $passwd::text{'acl_opt'} ],
|
||||
[ 0, $passwd::text{'no'} ] ]),
|
||||
3);
|
||||
|
||||
printf "<input type=radio name=mode value=4 %s> %s\n",
|
||||
$_[0]->{'mode'} == 4 ? 'checked' : '', $passwd::text{'acl_mode4'};
|
||||
printf "<input name=low size=8 value='%s'> -\n",
|
||||
$_[0]->{'mode'} == 4 ? $_[0]->{'low'} : '';
|
||||
printf "<input name=high size=8 value='%s'><br>\n",
|
||||
$_[0]->{'mode'} == 4 ? $_[0]->{'high'} : '';
|
||||
|
||||
printf "<input type=radio name=mode value=5 %s> %s\n",
|
||||
$_[0]->{'mode'} == 5 ? 'checked' : '', $passwd::text{'acl_mode5'};
|
||||
printf "<input name=groups size=20 value='%s'> %s<br>\n",
|
||||
$_[0]->{'mode'} == 5 ? $_[0]->{'users'} : '',
|
||||
&group_chooser_button("groups", 1);
|
||||
printf "%s <input type=checkbox name=sec value=1 %s> %s<br>\n",
|
||||
" " x 5, $_[0]->{'sec'} ? 'checked' : '',$passwd::text{'acl_sec'};
|
||||
|
||||
printf "<input type=radio name=mode value=6 %s> %s\n",
|
||||
$_[0]->{'mode'} == 6 ? 'checked' : '', $passwd::text{'acl_mode6'};
|
||||
printf "<input name=match size=15 value='%s'></td> </tr>\n",
|
||||
$_[0]->{'mode'} == 6 ? $_[0]->{'users'} : '';
|
||||
|
||||
print "<tr> <td><b>$passwd::text{'acl_repeat'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=repeat value=1 %s> $passwd::text{'yes'}\n",
|
||||
$_[0]->{'repeat'} ? "checked" : "";
|
||||
printf "<input type=radio name=repeat value=0 %s> $passwd::text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'repeat'} ? "" : "checked";
|
||||
|
||||
print "<td><b>$passwd::text{'acl_others'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=others value=1 %s> $passwd::text{'yes'}\n",
|
||||
$_[0]->{'others'} == 1 ? "checked" : "";
|
||||
printf "<input type=radio name=others value=2 %s> $passwd::text{'acl_opt'}\n",
|
||||
$_[0]->{'others'} == 2 ? "checked" : "";
|
||||
printf "<input type=radio name=others value=0 %s> $passwd::text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'others'} == 0 ? "checked" : "";
|
||||
|
||||
print "<tr> <td><b>$passwd::text{'acl_old'}</b></td> <td colspan=3>\n";
|
||||
printf "<input type=radio name=old value=1 %s> $passwd::text{'yes'}\n",
|
||||
$_[0]->{'old'} == 1 ? "checked" : "";
|
||||
printf "<input type=radio name=old value=2 %s> $passwd::text{'acl_old_this'}\n",
|
||||
$_[0]->{'old'} == 2 ? "checked" : "";
|
||||
printf "<input type=radio name=old value=0 %s> $passwd::text{'no'}</td> </tr>\n",
|
||||
$_[0]->{'old'} == 0 ? "checked" : "";
|
||||
print &ui_table_row($passwd::text{'acl_old'},
|
||||
&ui_radio("old", defined($o->{'old'}) ? $o->{'old'} : 0,
|
||||
[ [ 1, $passwd::text{'yes'} ],
|
||||
[ 2, $passwd::text{'acl_old_this'} ],
|
||||
[ 0, $passwd::text{'no'} ] ]),
|
||||
3);
|
||||
}
|
||||
|
||||
# acl_security_save(&options)
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -34,7 +34,7 @@ index_installon=التثبيت على
|
||||
index_upgradeon=خوادم للترقية
|
||||
index_update=وحدات التحديث
|
||||
index_updateon=خوادم لتحديث
|
||||
index_updatedesc=يسمح لك هذا النموذج بترقية وحدات Usermin التي تبين أنها تحتوي على أخطاء أو ثغرات أمنية من صفحة <a href=http://www.webmin.com/updates.html>تحديثات Usermin</a> أو من مصدر آخر.
|
||||
index_updatedesc=يسمح لك هذا النموذج بترقية وحدات Usermin التي تبين أنها تحتوي على أخطاء أو ثغرات أمنية من صفحة <a href=https://webmin.com/updates.html>تحديثات Usermin</a> أو من مصدر آخر.
|
||||
index_thost=اسم المضيف
|
||||
index_tdesc=وصف
|
||||
index_tver=الإصدار
|
||||
|
||||
@@ -34,7 +34,7 @@ index_installon=Инсталиране на
|
||||
index_upgradeon=Сървъри за надграждане
|
||||
index_update=Актуализиране на модули
|
||||
index_updateon=Сървъри за актуализация
|
||||
index_updatedesc=Този формуляр ви позволява да надстроите Usermin модули, за които е установено, че съдържат грешки или дупки за сигурност от страницата <a href=http://www.webmin.com/updates.html>Usermin Updates</a> или от друг източник.
|
||||
index_updatedesc=Този формуляр ви позволява да надстроите Usermin модули, за които е установено, че съдържат грешки или дупки за сигурност от страницата <a href=https://webmin.com/updates.html>Usermin Updates</a> или от друг източник.
|
||||
index_thost=Име на хост
|
||||
index_tdesc=описание
|
||||
index_tver=версия
|
||||
|
||||
@@ -34,7 +34,7 @@ index_installon=Instal·la a
|
||||
index_upgradeon=Servidors a actualitzar
|
||||
index_update=Actualitza Mòduls
|
||||
index_updateon=Servidors a actualitzar
|
||||
index_updatedesc=Aquest formulari permet actualitzar els mòduls d'Usermin que tenen errors o forats de seguretat a partir de la pàgina d'<a href=http://www.webmin.com/updates.html>Actualitzacions d'Usermin</a> o des d'una altra font.
|
||||
index_updatedesc=Aquest formulari permet actualitzar els mòduls d'Usermin que tenen errors o forats de seguretat a partir de la pàgina d'<a href=https://webmin.com/updates.html>Actualitzacions d'Usermin</a> o des d'una altra font.
|
||||
index_thost=Nom de host
|
||||
index_tdesc=Descripció
|
||||
index_tver=Versió
|
||||
|
||||
@@ -34,7 +34,7 @@ index_installon=Instalovat na
|
||||
index_upgradeon=Servery k povýšení
|
||||
index_update=Povýšení modulů
|
||||
index_updateon=Servery k povýšení
|
||||
index_updatedesc=Tento formulář umožní aktualizovat moduly Userminu, které obsahují chyby nebo bezpečnostní trhliny ze stránky <a href=http://www.webmin.com/updates.html>Aktualizace Userminu</a> nebo z jiného zdroje.
|
||||
index_updatedesc=Tento formulář umožní aktualizovat moduly Userminu, které obsahují chyby nebo bezpečnostní trhliny ze stránky <a href=https://webmin.com/updates.html>Aktualizace Userminu</a> nebo z jiného zdroje.
|
||||
index_thost=Jméno počítače
|
||||
index_tdesc=Poznámka
|
||||
index_tver=Verze
|
||||
|
||||
@@ -34,7 +34,7 @@ index_installon=Installer på
|
||||
index_upgradeon=Servere, der skal opgraderes
|
||||
index_update=Opdater moduler
|
||||
index_updateon=Servere, der skal opdateres
|
||||
index_updatedesc=Denne formular giver dig mulighed for at opgradere Usermin-moduler, der har vist sig at indeholde fejl eller sikkerhedshuller fra siden <a href=http://www.webmin.com/updates.html>Usermin Updates</a> eller fra en anden kilde.
|
||||
index_updatedesc=Denne formular giver dig mulighed for at opgradere Usermin-moduler, der har vist sig at indeholde fejl eller sikkerhedshuller fra siden <a href=https://webmin.com/updates.html>Usermin Updates</a> eller fra en anden kilde.
|
||||
index_thost=Værtsnavn
|
||||
index_tdesc=Beskrivelse
|
||||
index_tver=Version
|
||||
|
||||
@@ -34,7 +34,7 @@ index_installon=Installieren auf
|
||||
index_upgradeon=Zu aktualisierende Server
|
||||
index_update=Module aktualisieren
|
||||
index_updateon=Zu aktualisierende Server
|
||||
index_updatedesc=Dieses Formular ermöglicht es Ihnen, Usermin-Module zu aktualisieren, die Fehler oder Sicherheitslücken enthalten. Weitere Informationen finden Sie auf der <a href=http://www.webmin.com/updates.html>Usermin Updates</a>-Seite oder von einer anderen Quelle.
|
||||
index_updatedesc=Dieses Formular ermöglicht es Ihnen, Usermin-Module zu aktualisieren, die Fehler oder Sicherheitslücken enthalten. Weitere Informationen finden Sie auf der <a href=https://webmin.com/updates.html>Usermin Updates</a>-Seite oder von einer anderen Quelle.
|
||||
index_thost=Hostname
|
||||
index_tdesc=Beschreibung
|
||||
index_tver=Version
|
||||
|
||||
@@ -34,7 +34,7 @@ index_installon=Εγκαταστήστε το
|
||||
index_upgradeon=Διακομιστές για αναβάθμιση
|
||||
index_update=Ενημερώστε τις ενότητες
|
||||
index_updateon=Οι διακομιστές για ενημέρωση
|
||||
index_updatedesc=Αυτή η φόρμα σάς επιτρέπει να αναβαθμίσετε τις ενότητες Usermin που έχουν βρεθεί ότι περιέχουν σφάλματα ή τρύπες ασφαλείας από τη σελίδα <a href=http://www.webmin.com/updates.html>Usermin Updates</a> ή από άλλη πηγή.
|
||||
index_updatedesc=Αυτή η φόρμα σάς επιτρέπει να αναβαθμίσετε τις ενότητες Usermin που έχουν βρεθεί ότι περιέχουν σφάλματα ή τρύπες ασφαλείας από τη σελίδα <a href=https://webmin.com/updates.html>Usermin Updates</a> ή από άλλη πηγή.
|
||||
index_thost=Όνομα κεντρικού υπολογιστή
|
||||
index_tdesc=Περιγραφή
|
||||
index_tver=Εκδοχή
|
||||
|
||||
@@ -34,7 +34,7 @@ index_installon=Install on
|
||||
index_upgradeon=Servers to upgrade
|
||||
index_update=Update Modules
|
||||
index_updateon=Servers to update
|
||||
index_updatedesc=This form allows you to upgrade Usermin modules that have been found to contain bugs or security holes from the <a href=http://www.webmin.com/updates.html>Usermin Updates</a> page or from another source.
|
||||
index_updatedesc=This form allows you to upgrade Usermin modules that have been found to contain bugs or security holes from the <a href=https://webmin.com/updates.html>Usermin Updates</a> page or from another source.
|
||||
index_thost=Hostname
|
||||
index_tdesc=Description
|
||||
index_tver=Version
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user