mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-01-31 13:25:10 +01:00
ref(systemd): Fix safety bugs and improve performance (#1020)
* fix(systemd): Fix the issue of special characters in unit names In systemd unit processing, filtering of special characters in unit names has been added to prevent command injection and security issues. Additionally, the rendering performance of the unit list has been optimized by merging unnecessary watch calls and removing confirmation dialogs to simplify the operation process. * feat(Systemd): Add a confirmation dialog for systemd unit operations Display a confirmation dialog when stopping or restarting systemd units to prevent accidental operations. For other operations, directly navigate to the SSH page to execute commands. * fix(systemd): Fix the range of characters allowed in unit names Extend the regular expression to allow more valid characters, including dots, @, and colons, in system unit names, to support a broader range of unit naming conventions * fix(systemd): Fix the issue of parsing service names with dots When dealing with service IDs containing multiple dots (such as org.cups.cupsd.service), correctly extract the service name and type. When there are no dots in the service ID, set the type to an empty string.
This commit is contained in:
@@ -96,7 +96,7 @@ final class SystemdUnit {
|
||||
|
||||
String getCmd({required SystemdUnitFunc func, required bool isRoot}) {
|
||||
final prefix = scope.getCmdPrefix(isRoot);
|
||||
return '$prefix ${func.name} $name';
|
||||
return '$prefix ${func.name} ${name.replaceAll(RegExp(r'[^a-zA-Z0-9\-_.@:]'), '')}';
|
||||
}
|
||||
|
||||
List<SystemdUnitFunc> get availableFuncs {
|
||||
|
||||
@@ -77,11 +77,16 @@ class SystemdNotifier extends _$SystemdNotifier {
|
||||
}
|
||||
|
||||
Future<List<SystemdUnit>> _parseUnitObj(List<String> unitNames, SystemdUnitScope scope) async {
|
||||
final unitNames_ = unitNames.map((e) => e.trim().split('/').last.split('.').first).toList();
|
||||
final unitNames_ = unitNames.map((e) {
|
||||
final fullName = e.trim().split('/').last;
|
||||
final lastDot = fullName.lastIndexOf('.');
|
||||
final name = lastDot > 0 ? fullName.substring(0, lastDot) : fullName;
|
||||
return name.replaceAll(RegExp(r'[^a-zA-Z0-9\-_.@:]'), '');
|
||||
}).toList();
|
||||
final script =
|
||||
'''
|
||||
for unit in ${unitNames_.join(' ')}; do
|
||||
state=\$(systemctl show --no-pager \$unit)
|
||||
for unit in ${unitNames_.map((e) => '"$e"').join(' ')}; do
|
||||
state=\$(systemctl show --no-pager -- "\$unit")
|
||||
echo "\$state"
|
||||
echo -n "\n${ScriptConstants.separator}\n"
|
||||
done
|
||||
@@ -102,10 +107,15 @@ done
|
||||
if (part.startsWith('Id=')) {
|
||||
final val = _getIniVal(part);
|
||||
if (val == null) continue;
|
||||
// Id=sshd.service
|
||||
final vals = val.split('.');
|
||||
name = vals.first;
|
||||
type = vals.last;
|
||||
// Id=org.cups.cupsd.service
|
||||
final lastDot = val.lastIndexOf('.');
|
||||
if (lastDot > 0) {
|
||||
name = val.substring(0, lastDot);
|
||||
type = val.substring(lastDot + 1);
|
||||
} else {
|
||||
name = val;
|
||||
type = '';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (part.startsWith('ActiveState=')) {
|
||||
|
||||
@@ -73,8 +73,7 @@ final class _SystemdPageState extends ConsumerState<SystemdPage> {
|
||||
}
|
||||
|
||||
Widget _buildUnitList() {
|
||||
ref.watch(_pro.select((p) => p.units));
|
||||
ref.watch(_pro.select((p) => p.scopeFilter));
|
||||
ref.watch(_pro.select((p) => (p.units, p.scopeFilter)));
|
||||
final filteredUnits = _notifier.filteredUnits;
|
||||
if (filteredUnits.isEmpty) {
|
||||
return SliverToBoxAdapter(child: CenterGreyTitle(libL10n.empty).paddingSymmetric(horizontal: 13));
|
||||
@@ -97,28 +96,42 @@ final class _SystemdPageState extends ConsumerState<SystemdPage> {
|
||||
Widget _buildUnitFuncs(SystemdUnit unit) {
|
||||
return PopupMenu(
|
||||
items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(),
|
||||
onSelected: (val) async {
|
||||
final cmd = unit.getCmd(func: val, isRoot: widget.args.spi.isRoot);
|
||||
final sure = await context.showRoundDialog(
|
||||
title: libL10n.attention,
|
||||
child: SimpleMarkdown(data: '```shell\n$cmd\n```'),
|
||||
actions: [
|
||||
CountDownBtn(
|
||||
seconds: 1,
|
||||
onTap: () => context.pop(true),
|
||||
text: libL10n.ok,
|
||||
afterColor: Colors.red,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (sure != true) return;
|
||||
|
||||
final args = SshPageArgs(spi: widget.args.spi, initCmd: cmd);
|
||||
SSHPage.route.go(context, args);
|
||||
},
|
||||
onSelected: (val) => _handleUnitFuncSelected(unit, val),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleUnitFuncSelected(SystemdUnit unit, SystemdUnitFunc func) {
|
||||
final cmd = unit.getCmd(func: func, isRoot: widget.args.spi.isRoot);
|
||||
|
||||
if (func == SystemdUnitFunc.stop || func == SystemdUnitFunc.restart) {
|
||||
_showConfirmDialog(cmd);
|
||||
} else {
|
||||
_navigateToSsh(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void _showConfirmDialog(String cmd) async {
|
||||
final sure = await context.showRoundDialog(
|
||||
title: libL10n.attention,
|
||||
child: SimpleMarkdown(data: '```shell\n$cmd\n```'),
|
||||
actions: [
|
||||
Btn.cancel(),
|
||||
CountDownBtn(
|
||||
seconds: 3,
|
||||
onTap: () => context.pop(true),
|
||||
text: libL10n.ok,
|
||||
afterColor: Colors.red,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (sure == true) _navigateToSsh(cmd);
|
||||
}
|
||||
|
||||
void _navigateToSsh(String cmd) {
|
||||
final args = SshPageArgs(spi: widget.args.spi, initCmd: cmd);
|
||||
SSHPage.route.go(context, args);
|
||||
}
|
||||
|
||||
PopupMenuEntry _buildUnitFuncBtn(SystemdUnitFunc func) {
|
||||
return PopupMenuItem<SystemdUnitFunc>(
|
||||
value: func,
|
||||
|
||||
Reference in New Issue
Block a user